Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // 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.
- //
- // This file exists to aggregate all of the javascript used by the
- // settings page into a single file which will be flattened and served
- // as a single resource.
- // 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() {
- /////////////////////////////////////////////////////////////////////////////
- // Preferences class:
- /**
- * Preferences class manages access to Chrome profile preferences.
- * @constructor
- */
- function Preferences() {
- }
- cr.addSingletonGetter(Preferences);
- /**
- * Sets value of a boolean preference.
- * and signals its changed value.
- * @param {string} name Preference name.
- * @param {boolean} value New preference value.
- * @param {string} metric User metrics identifier.
- */
- Preferences.setBooleanPref = function(name, value, metric) {
- var argumentList = [name, Boolean(value)];
- if (metric != undefined) argumentList.push(metric);
- chrome.send('setBooleanPref', argumentList);
- };
- /**
- * Sets value of an integer preference.
- * and signals its changed value.
- * @param {string} name Preference name.
- * @param {number} value New preference value.
- * @param {string} metric User metrics identifier.
- */
- Preferences.setIntegerPref = function(name, value, metric) {
- var argumentList = [name, Number(value)];
- if (metric != undefined) argumentList.push(metric);
- chrome.send('setIntegerPref', argumentList);
- };
- /**
- * Sets value of a double-valued preference.
- * and signals its changed value.
- * @param {string} name Preference name.
- * @param {number} value New preference value.
- * @param {string} metric User metrics identifier.
- */
- Preferences.setDoublePref = function(name, value, metric) {
- var argumentList = [name, Number(value)];
- if (metric != undefined) argumentList.push(metric);
- chrome.send('setDoublePref', argumentList);
- };
- /**
- * Sets value of a string preference.
- * and signals its changed value.
- * @param {string} name Preference name.
- * @param {string} value New preference value.
- * @param {string} metric User metrics identifier.
- */
- Preferences.setStringPref = function(name, value, metric) {
- var argumentList = [name, String(value)];
- if (metric != undefined) argumentList.push(metric);
- chrome.send('setStringPref', argumentList);
- };
- /**
- * Sets value of a string preference that represents a URL
- * and signals its changed value. The value will be fixed to be a valid URL.
- * @param {string} name Preference name.
- * @param {string} value New preference value.
- * @param {string} metric User metrics identifier.
- */
- Preferences.setURLPref = function(name, value, metric) {
- var argumentList = [name, String(value)];
- if (metric != undefined) argumentList.push(metric);
- chrome.send('setURLPref', argumentList);
- };
- /**
- * Sets value of a JSON list preference.
- * and signals its changed value.
- * @param {string} name Preference name.
- * @param {Array} value New preference value.
- * @param {string} metric User metrics identifier.
- */
- Preferences.setListPref = function(name, value, metric) {
- var argumentList = [name, JSON.stringify(value)];
- if (metric != undefined) argumentList.push(metric);
- chrome.send('setListPref', argumentList);
- };
- /**
- * Clears value of a JSON preference.
- * @param {string} name Preference name.
- * @param {string} metric User metrics identifier.
- */
- Preferences.clearPref = function(name, metric) {
- var argumentList = [name];
- if (metric != undefined) argumentList.push(metric);
- chrome.send('clearPref', argumentList);
- };
- Preferences.prototype = {
- __proto__: cr.EventTarget.prototype,
- // Map of registered preferences.
- registeredPreferences_: {},
- /**
- * Adds an event listener to the target.
- * @param {string} type The name of the event.
- * @param {!Function|{handleEvent:Function}} handler The handler for the
- * event. This is called when the event is dispatched.
- */
- addEventListener: function(type, handler) {
- cr.EventTarget.prototype.addEventListener.call(this, type, handler);
- this.registeredPreferences_[type] = true;
- },
- /**
- * Initializes preference reading and change notifications.
- */
- initialize: function() {
- var params1 = ['Preferences.prefsFetchedCallback'];
- var params2 = ['Preferences.prefsChangedCallback'];
- for (var prefName in this.registeredPreferences_) {
- params1.push(prefName);
- params2.push(prefName);
- }
- chrome.send('fetchPrefs', params1);
- chrome.send('observePrefs', params2);
- },
- /**
- * Helper function for flattening of dictionary passed via fetchPrefs
- * callback.
- * @param {string} prefix Preference name prefix.
- * @param {object} dict Map with preference values.
- */
- flattenMapAndDispatchEvent_: function(prefix, dict) {
- for (var prefName in dict) {
- if (typeof dict[prefName] == 'object' &&
- !this.registeredPreferences_[prefix + prefName]) {
- this.flattenMapAndDispatchEvent_(prefix + prefName + '.',
- dict[prefName]);
- } else {
- var event = new cr.Event(prefix + prefName);
- event.value = dict[prefName];
- this.dispatchEvent(event);
- }
- }
- }
- };
- /**
- * Callback for fetchPrefs method.
- * @param {object} dict Map of fetched property values.
- */
- Preferences.prefsFetchedCallback = function(dict) {
- Preferences.getInstance().flattenMapAndDispatchEvent_('', dict);
- };
- /**
- * Callback for observePrefs method.
- * @param {array} notification An array defining changed preference values.
- * notification[0] contains name of the change preference while its new value
- * is stored in notification[1].
- */
- Preferences.prefsChangedCallback = function(notification) {
- var event = new cr.Event(notification[0]);
- event.value = notification[1];
- Preferences.getInstance().dispatchEvent(event);
- };
- // Export
- return {
- Preferences: Preferences
- };
- });
- // 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
- };
- });
- // 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() {
- const List = cr.ui.List;
- const ListItem = cr.ui.ListItem;
- /**
- * Creates a deletable list item, which has a button that will trigger a call
- * to deleteItemAtIndex(index) in the list.
- */
- var DeletableItem = cr.ui.define('li');
- DeletableItem.prototype = {
- __proto__: ListItem.prototype,
- /**
- * The element subclasses should populate with content.
- * @type {HTMLElement}
- * @private
- */
- contentElement_: null,
- /**
- * The close button element.
- * @type {HTMLElement}
- * @private
- */
- closeButtonElement_: null,
- /**
- * Whether or not this item can be deleted.
- * @type {boolean}
- * @private
- */
- deletable_: true,
- /** @inheritDoc */
- decorate: function() {
- ListItem.prototype.decorate.call(this);
- this.classList.add('deletable-item');
- this.contentElement_ = this.ownerDocument.createElement('div');
- this.appendChild(this.contentElement_);
- this.closeButtonElement_ = this.ownerDocument.createElement('button');
- this.closeButtonElement_.className =
- 'raw-button close-button custom-appearance';
- this.closeButtonElement_.addEventListener('mousedown',
- this.handleMouseDownUpOnClose_);
- this.closeButtonElement_.addEventListener('mouseup',
- this.handleMouseDownUpOnClose_);
- this.closeButtonElement_.addEventListener('focus',
- this.handleFocus_.bind(this));
- this.appendChild(this.closeButtonElement_);
- },
- /**
- * Returns the element subclasses should add content to.
- * @return {HTMLElement} The element subclasses should popuplate.
- */
- get contentElement() {
- return this.contentElement_;
- },
- /* Gets/sets the deletable property. An item that is not deletable doesn't
- * show the delete button (although space is still reserved for it).
- */
- get deletable() {
- return this.deletable_;
- },
- set deletable(value) {
- this.deletable_ = value;
- this.closeButtonElement_.disabled = !value;
- },
- /**
- * Called when a focusable child element receives focus. Selects this item
- * in the list selection model.
- * @private
- */
- handleFocus_: function() {
- var list = this.parentNode;
- var index = list.getIndexOfListItem(this);
- list.selectionModel.selectedIndex = index;
- list.selectionModel.anchorIndex = index;
- },
- /**
- * Don't let the list have a crack at the event. We don't want clicking the
- * close button to change the selection of the list.
- * @param {Event} e The mouse down/up event object.
- * @private
- */
- handleMouseDownUpOnClose_: function(e) {
- if (!e.target.disabled)
- e.stopPropagation();
- },
- };
- var DeletableItemList = cr.ui.define('list');
- DeletableItemList.prototype = {
- __proto__: List.prototype,
- /** @inheritDoc */
- decorate: function() {
- List.prototype.decorate.call(this);
- this.addEventListener('click', this.handleClick_);
- this.addEventListener('keydown', this.handleKeyDown_);
- },
- /**
- * Callback for onclick events.
- * @param {Event} e The click event object.
- * @private
- */
- handleClick_: function(e) {
- if (this.disabled)
- return;
- var target = e.target;
- if (target.classList.contains('close-button')) {
- var listItem = this.getListItemAncestor(target);
- var selected = this.selectionModel.selectedIndexes;
- // Check if the list item that contains the close button being clicked
- // is not in the list of selected items. Only delete this item in that
- // case.
- var idx = this.getIndexOfListItem(listItem);
- if (selected.indexOf(idx) == -1) {
- this.deleteItemAtIndex(idx);
- } else {
- this.deleteSelectedItems_();
- }
- }
- },
- /**
- * Callback for keydown events.
- * @param {Event} e The keydown event object.
- * @private
- */
- handleKeyDown_: function(e) {
- // Map delete (and backspace on Mac) to item deletion (unless focus is
- // in an input field, where it's intended for text editing).
- if ((e.keyCode == 46 || (e.keyCode == 8 && cr.isMac)) &&
- e.target.tagName != 'INPUT') {
- this.deleteSelectedItems_();
- // Prevent the browser from going back.
- e.preventDefault();
- }
- },
- /**
- * Deletes all the currently selected items that are deletable.
- * @private
- */
- deleteSelectedItems_: function() {
- var selected = this.selectionModel.selectedIndexes;
- // Reverse through the list of selected indexes to maintain the
- // correct index values after deletion.
- for (var j = selected.length - 1; j >= 0; j--) {
- var index = selected[j];
- if (this.getListItemByIndex(index).deletable)
- this.deleteItemAtIndex(index);
- }
- },
- /**
- * Called when an item should be deleted; subclasses are responsible for
- * implementing.
- * @param {number} index The index of the item that is being deleted.
- */
- deleteItemAtIndex: function(index) {
- },
- };
- return {
- DeletableItemList: DeletableItemList,
- DeletableItem: DeletableItem,
- };
- });
- // 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() {
- const DeletableItem = options.DeletableItem;
- const DeletableItemList = options.DeletableItemList;
- /**
- * Creates a new list item with support for inline editing.
- * @constructor
- * @extends {options.DeletableListItem}
- */
- function InlineEditableItem() {
- var el = cr.doc.createElement('div');
- InlineEditableItem.decorate(el);
- return el;
- }
- /**
- * Decorates an element as a inline-editable list item. Note that this is
- * a subclass of DeletableItem.
- * @param {!HTMLElement} el The element to decorate.
- */
- InlineEditableItem.decorate = function(el) {
- el.__proto__ = InlineEditableItem.prototype;
- el.decorate();
- };
- InlineEditableItem.prototype = {
- __proto__: DeletableItem.prototype,
- /**
- * Whether or not this item can be edited.
- * @type {boolean}
- * @private
- */
- editable_: true,
- /**
- * Whether or not this is a placeholder for adding a new item.
- * @type {boolean}
- * @private
- */
- isPlaceholder_: false,
- /**
- * Fields associated with edit mode.
- * @type {array}
- * @private
- */
- editFields_: null,
- /**
- * Whether or not the current edit should be considered cancelled, rather
- * than committed, when editing ends.
- * @type {boolean}
- * @private
- */
- editCancelled_: true,
- /**
- * The editable item corresponding to the last click, if any. Used to decide
- * initial focus when entering edit mode.
- * @type {HTMLElement}
- * @private
- */
- editClickTarget_: null,
- /** @inheritDoc */
- decorate: function() {
- DeletableItem.prototype.decorate.call(this);
- this.editFields_ = [];
- this.addEventListener('mousedown', this.handleMouseDown_);
- this.addEventListener('keydown', this.handleKeyDown_);
- this.addEventListener('leadChange', this.handleLeadChange_);
- },
- /** @inheritDoc */
- selectionChanged: function() {
- this.updateEditState();
- },
- /**
- * Called when this element gains or loses 'lead' status. Updates editing
- * mode accordingly.
- * @private
- */
- handleLeadChange_: function() {
- this.updateEditState();
- },
- /**
- * Updates the edit state based on the current selected and lead states.
- */
- updateEditState: function() {
- if (this.editable)
- this.editing = this.selected && this.lead;
- },
- /**
- * Whether the user is currently editing the list item.
- * @type {boolean}
- */
- get editing() {
- return this.hasAttribute('editing');
- },
- set editing(editing) {
- if (this.editing == editing)
- return;
- if (editing)
- this.setAttribute('editing', '');
- else
- this.removeAttribute('editing');
- if (editing) {
- this.editCancelled_ = false;
- cr.dispatchSimpleEvent(this, 'edit', true);
- var focusElement = this.editClickTarget_ || this.initialFocusElement;
- this.editClickTarget_ = null;
- // When this is called in response to the selectedChange event,
- // the list grabs focus immediately afterwards. Thus we must delay
- // our focus grab.
- var self = this;
- if (focusElement) {
- window.setTimeout(function() {
- // Make sure we are still in edit mode by the time we execute.
- if (self.editing) {
- focusElement.focus();
- focusElement.select();
- }
- }, 50);
- }
- } else {
- if (!this.editCancelled_ && this.hasBeenEdited &&
- this.currentInputIsValid) {
- if (this.isPlaceholder)
- this.parentNode.focusPlaceholder = true;
- this.updateStaticValues_();
- cr.dispatchSimpleEvent(this, 'commitedit', true);
- } else {
- this.resetEditableValues_();
- cr.dispatchSimpleEvent(this, 'canceledit', true);
- }
- }
- },
- /**
- * Whether the item is editable.
- * @type {boolean}
- */
- get editable() {
- return this.editable_;
- },
- set editable(editable) {
- this.editable_ = editable;
- if (!editable)
- this.editing = false;
- },
- /**
- * Whether the item is a new item placeholder.
- * @type {boolean}
- */
- get isPlaceholder() {
- return this.isPlaceholder_;
- },
- set isPlaceholder(isPlaceholder) {
- this.isPlaceholder_ = isPlaceholder;
- if (isPlaceholder)
- this.deletable = false;
- },
- /**
- * The HTML element that should have focus initially when editing starts,
- * if a specific element wasn't clicked.
- * Defaults to the first <input> element; can be overriden by subclasses if
- * a different element should be focused.
- * @type {HTMLElement}
- */
- get initialFocusElement() {
- return this.contentElement.querySelector('input');
- },
- /**
- * Whether the input in currently valid to submit. If this returns false
- * when editing would be submitted, either editing will not be ended,
- * or it will be cancelled, depending on the context.
- * Can be overrided by subclasses to perform input validation.
- * @type {boolean}
- */
- get currentInputIsValid() {
- return true;
- },
- /**
- * Returns true if the item has been changed by an edit.
- * Can be overrided by subclasses to return false when nothing has changed
- * to avoid unnecessary commits.
- * @type {boolean}
- */
- get hasBeenEdited() {
- return true;
- },
- /**
- * Returns a div containing an <input>, as well as static text if
- * isPlaceholder is not true.
- * @param {string} text The text of the cell.
- * @return {HTMLElement} The HTML element for the cell.
- * @private
- */
- createEditableTextCell: function(text) {
- var container = this.ownerDocument.createElement('div');
- if (!this.isPlaceholder) {
- var textEl = this.ownerDocument.createElement('div');
- textEl.className = 'static-text';
- textEl.textContent = text;
- textEl.setAttribute('displaymode', 'static');
- container.appendChild(textEl);
- }
- var inputEl = this.ownerDocument.createElement('input');
- inputEl.type = 'text';
- inputEl.value = text;
- if (!this.isPlaceholder) {
- inputEl.setAttribute('displaymode', 'edit');
- inputEl.staticVersion = textEl;
- } else {
- // At this point |this| is not attached to the parent list yet, so give
- // a short timeout in order for the attachment to occur.
- var self = this;
- window.setTimeout(function() {
- var list = self.parentNode;
- if (list && list.focusPlaceholder) {
- list.focusPlaceholder = false;
- if (list.shouldFocusPlaceholder())
- inputEl.focus();
- }
- }, 50);
- }
- inputEl.addEventListener('focus', this.handleFocus_.bind(this));
- container.appendChild(inputEl);
- this.editFields_.push(inputEl);
- return container;
- },
- /**
- * Resets the editable version of any controls created by createEditable*
- * to match the static text.
- * @private
- */
- resetEditableValues_: function() {
- var editFields = this.editFields_;
- for (var i = 0; i < editFields.length; i++) {
- var staticLabel = editFields[i].staticVersion;
- if (!staticLabel && !this.isPlaceholder)
- continue;
- if (editFields[i].tagName == 'INPUT') {
- editFields[i].value =
- this.isPlaceholder ? '' : staticLabel.textContent;
- }
- // Add more tag types here as new createEditable* methods are added.
- editFields[i].setCustomValidity('');
- }
- },
- /**
- * Sets the static version of any controls created by createEditable*
- * to match the current value of the editable version. Called on commit so
- * that there's no flicker of the old value before the model updates.
- * @private
- */
- updateStaticValues_: function() {
- var editFields = this.editFields_;
- for (var i = 0; i < editFields.length; i++) {
- var staticLabel = editFields[i].staticVersion;
- if (!staticLabel)
- continue;
- if (editFields[i].tagName == 'INPUT')
- staticLabel.textContent = editFields[i].value;
- // Add more tag types here as new createEditable* methods are added.
- }
- },
- /**
- * Called a key is pressed. Handles committing and cancelling edits.
- * @param {Event} e The key down event.
- * @private
- */
- handleKeyDown_: function(e) {
- if (!this.editing)
- return;
- var endEdit = false;
- switch (e.keyIdentifier) {
- case 'U+001B': // Esc
- this.editCancelled_ = true;
- endEdit = true;
- break;
- case 'Enter':
- if (this.currentInputIsValid)
- endEdit = true;
- break;
- }
- if (endEdit) {
- // Blurring will trigger the edit to end; see InlineEditableItemList.
- this.ownerDocument.activeElement.blur();
- // Make sure that handled keys aren't passed on and double-handled.
- // (e.g., esc shouldn't both cancel an edit and close a subpage)
- e.stopPropagation();
- }
- },
- /**
- * Called when the list item is clicked. If the click target corresponds to
- * an editable item, stores that item to focus when edit mode is started.
- * @param {Event} e The mouse down event.
- * @private
- */
- handleMouseDown_: function(e) {
- if (!this.editable || this.editing)
- return;
- var clickTarget = e.target;
- var editFields = this.editFields_;
- for (var i = 0; i < editFields.length; i++) {
- if (editFields[i] == clickTarget ||
- editFields[i].staticVersion == clickTarget) {
- this.editClickTarget_ = editFields[i];
- return;
- }
- }
- },
- };
- /**
- * Takes care of committing changes to inline editable list items when the
- * window loses focus.
- */
- function handleWindowBlurs() {
- window.addEventListener('blur', function(e) {
- var itemAncestor = findAncestor(document.activeElement, function(node) {
- return node instanceof InlineEditableItem;
- });
- if (itemAncestor);
- document.activeElement.blur();
- });
- }
- handleWindowBlurs();
- var InlineEditableItemList = cr.ui.define('list');
- InlineEditableItemList.prototype = {
- __proto__: DeletableItemList.prototype,
- /**
- * Focuses the input element of the placeholder if true.
- * @type {boolean}
- */
- focusPlaceholder: false,
- /** @inheritDoc */
- decorate: function() {
- DeletableItemList.prototype.decorate.call(this);
- this.setAttribute('inlineeditable', '');
- this.addEventListener('hasElementFocusChange',
- this.handleListFocusChange_);
- },
- /**
- * Called when the list hierarchy as a whole loses or gains focus; starts
- * or ends editing for the lead item if necessary.
- * @param {Event} e The change event.
- * @private
- */
- handleListFocusChange_: function(e) {
- var leadItem = this.getListItemByIndex(this.selectionModel.leadIndex);
- if (leadItem) {
- if (e.newValue)
- leadItem.updateEditState();
- else
- leadItem.editing = false;
- }
- },
- /**
- * May be overridden by subclasses to disable focusing the placeholder.
- * @return true if the placeholder element should be focused on edit commit.
- */
- shouldFocusPlaceholder: function() {
- return true;
- },
- };
- // Export
- return {
- InlineEditableItem: InlineEditableItem,
- InlineEditableItemList: InlineEditableItemList,
- };
- });
- // 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;
- /**
- * 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__: HTMLSpanElement.prototype,
- /**
- * Decorates the base element to show the proper icon.
- */
- decorate: function() {
- var self = this;
- var doc = self.ownerDocument;
- // Create the details and summary elements.
- var detailsContainer = doc.createElement('details');
- detailsContainer.appendChild(doc.createElement('summary'));
- // This should really create a div element, but that breaks :hover. See
- // https://bugs.webkit.org/show_bug.cgi?id=72957
- var bubbleContainer = doc.createElement('p');
- bubbleContainer.className = 'controlled-setting-bubble';
- detailsContainer.appendChild(bubbleContainer);
- self.appendChild(detailsContainer);
- // If there is a pref, track its controlledBy property in order to be able
- // to bring up the correct bubble.
- if (this.hasAttribute('pref')) {
- Preferences.getInstance().addEventListener(
- this.getAttribute('pref'),
- function(event) {
- if (event.value) {
- var controlledBy = event.value['controlledBy'];
- self.controlledBy = controlledBy ? controlledBy : null;
- }
- });
- }
- self.addEventListener('click', self.show_);
- },
- /**
- * Closes the bubble.
- */
- close: function() {
- this.querySelector('details').removeAttribute('open');
- this.ownerDocument.removeEventListener('click', this.closeHandler_, true);
- },
- /**
- * Constructs the bubble DOM tree and shows it.
- * @private
- */
- show_: function() {
- var self = this;
- var doc = self.ownerDocument;
- // Clear out the old bubble contents.
- var bubbleContainer = this.querySelector('.controlled-setting-bubble');
- if (bubbleContainer) {
- while (bubbleContainer.hasChildNodes())
- bubbleContainer.removeChild(bubbleContainer.lastChild);
- }
- // Work out the bubble text.
- defaultStrings = {
- 'policy' : localStrings.getString('controlledSettingPolicy'),
- 'extension' : localStrings.getString('controlledSettingExtension'),
- 'recommended' : localStrings.getString('controlledSettingRecommended'),
- };
- // No controller, no bubble.
- if (!self.controlledBy || !self.controlledBy in defaultStrings)
- return;
- var text = defaultStrings[self.controlledBy];
- // Apply text overrides.
- if (self.hasAttribute('text' + self.controlledBy))
- text = self.getAttribute('text' + self.controlledBy);
- // Create the DOM tree.
- var bubbleText = doc.createElement('p');
- bubbleText.className = 'controlled-setting-bubble-text';
- bubbleText.textContent = text;
- var pref = self.getAttribute('pref');
- if (self.controlledBy == 'recommended' && pref) {
- var container = doc.createElement('div');
- var action = doc.createElement('button');
- action.classList.add('link-button');
- action.classList.add('controlled-setting-bubble-action');
- action.textContent =
- localStrings.getString('controlledSettingApplyRecommendation');
- action.addEventListener(
- 'click',
- function(e) {
- Preferences.clearPref(pref);
- });
- container.appendChild(action);
- bubbleText.appendChild(container);
- }
- bubbleContainer.appendChild(bubbleText);
- // One-time bubble-closing event handler.
- self.closeHandler_ = this.close.bind(this);
- doc.addEventListener('click', self.closeHandler_, true);
- }
- };
- /**
- * The controlling entity of the setting. Can take the values "policy",
- * "extension", "recommended" or be unset.
- */
- cr.defineProperty(ControlledSettingIndicator, 'controlledBy',
- cr.PropertyKind.ATTR,
- ControlledSettingIndicator.prototype.close);
- // Export.
- return {
- ControlledSettingIndicator : ControlledSettingIndicator
- };
- });
- // 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() {
- /////////////////////////////////////////////////////////////////////////////
- // OptionsPage class:
- /**
- * Base class for options page.
- * @constructor
- * @param {string} name Options page name, also defines id of the div element
- * containing the options view and the name of options page navigation bar
- * item as name+'PageNav'.
- * @param {string} title Options page title, used for navigation bar
- * @extends {EventTarget}
- */
- function OptionsPage(name, title, pageDivName) {
- this.name = name;
- this.title = title;
- this.pageDivName = pageDivName;
- this.pageDiv = $(this.pageDivName);
- this.tab = null;
- }
- const SUBPAGE_SHEET_COUNT = 2;
- /**
- * Main level option pages. Maps lower-case page names to the respective page
- * object.
- * @protected
- */
- OptionsPage.registeredPages = {};
- /**
- * Pages which are meant to behave like modal dialogs. Maps lower-case overlay
- * names to the respective overlay object.
- * @protected
- */
- OptionsPage.registeredOverlayPages = {};
- /**
- * Whether or not |initialize| has been called.
- * @private
- */
- OptionsPage.initialized_ = false;
- /**
- * Gets the default page (to be shown on initial load).
- */
- OptionsPage.getDefaultPage = function() {
- return BrowserOptions.getInstance();
- };
- /**
- * Shows the default page.
- */
- OptionsPage.showDefaultPage = function() {
- this.navigateToPage(this.getDefaultPage().name);
- };
- /**
- * "Navigates" to a page, meaning that the page will be shown and the
- * appropriate entry is placed in the history.
- * @param {string} pageName Page name.
- */
- OptionsPage.navigateToPage = function(pageName) {
- this.showPageByName(pageName, true);
- };
- /**
- * Shows a registered page. This handles both top-level pages and sub-pages.
- * @param {string} pageName Page name.
- * @param {boolean} updateHistory True if we should update the history after
- * showing the page.
- * @private
- */
- OptionsPage.showPageByName = function(pageName, updateHistory) {
- // Find the currently visible root-level page.
- var rootPage = null;
- for (var name in this.registeredPages) {
- var page = this.registeredPages[name];
- if (page.visible && !page.parentPage) {
- rootPage = page;
- break;
- }
- }
- // Find the target page.
- var targetPage = this.registeredPages[pageName.toLowerCase()];
- if (!targetPage || !targetPage.canShowPage()) {
- // If it's not a page, try it as an overlay.
- if (!targetPage && this.showOverlay_(pageName, rootPage)) {
- if (updateHistory)
- this.updateHistoryState_();
- return;
- } else {
- targetPage = this.getDefaultPage();
- }
- }
- pageName = targetPage.name.toLowerCase();
- var targetPageWasVisible = targetPage.visible;
- // Determine if the root page is 'sticky', meaning that it
- // shouldn't change when showing a sub-page. This can happen for special
- // pages like Search.
- var isRootPageLocked =
- rootPage && rootPage.sticky && targetPage.parentPage;
- // Notify pages if they will be hidden.
- for (var name in this.registeredPages) {
- var page = this.registeredPages[name];
- if (!page.parentPage && isRootPageLocked)
- continue;
- if (page.willHidePage && name != pageName &&
- !page.isAncestorOfPage(targetPage))
- page.willHidePage();
- }
- // Update visibilities to show only the hierarchy of the target page.
- for (var name in this.registeredPages) {
- var page = this.registeredPages[name];
- if (!page.parentPage && isRootPageLocked)
- continue;
- page.visible = name == pageName ||
- (!document.documentElement.classList.contains('hide-menu') &&
- page.isAncestorOfPage(targetPage));
- }
- // Update the history and current location.
- if (updateHistory)
- this.updateHistoryState_();
- // Always update the page title.
- document.title = targetPage.title;
- // Notify pages if they were shown.
- for (var name in this.registeredPages) {
- var page = this.registeredPages[name];
- if (!page.parentPage && isRootPageLocked)
- continue;
- if (!targetPageWasVisible && page.didShowPage && (name == pageName ||
- page.isAncestorOfPage(targetPage)))
- page.didShowPage();
- }
- };
- /**
- * Updates the visibility and stacking order of the subpage backdrop
- * according to which subpage is topmost and visible.
- * @private
- */
- OptionsPage.updateSubpageBackdrop_ = function () {
- var topmostPage = this.getTopmostVisibleNonOverlayPage_();
- var nestingLevel = topmostPage ? topmostPage.nestingLevel : 0;
- var subpageBackdrop = $('subpage-backdrop');
- if (nestingLevel > 0) {
- var container = $('subpage-sheet-container-' + nestingLevel);
- subpageBackdrop.style.zIndex =
- parseInt(window.getComputedStyle(container).zIndex) - 1;
- subpageBackdrop.hidden = false;
- } else {
- subpageBackdrop.hidden = true;
- }
- };
- /**
- * Scrolls the page to the correct position (the top when opening a subpage,
- * or the old scroll position a previously hidden subpage becomes visible).
- * @private
- */
- OptionsPage.updateScrollPosition_ = function () {
- var topmostPage = this.getTopmostVisibleNonOverlayPage_();
- var nestingLevel = topmostPage ? topmostPage.nestingLevel : 0;
- var container = (nestingLevel > 0) ?
- $('subpage-sheet-container-' + nestingLevel) : $('page-container');
- var scrollTop = container.oldScrollTop || 0;
- container.oldScrollTop = undefined;
- window.scroll(document.body.scrollLeft, scrollTop);
- };
- /**
- * Pushes the current page onto the history stack, overriding the last page
- * if it is the generic chrome://settings/.
- * @private
- */
- OptionsPage.updateHistoryState_ = function() {
- var page = this.getTopmostVisiblePage();
- var path = location.pathname;
- if (path)
- path = path.slice(1).replace(/\/$/, ''); // Remove trailing slash.
- // The page is already in history (the user may have clicked the same link
- // twice). Do nothing.
- if (path == page.name)
- return;
- // If there is no path, the current location is chrome://settings/.
- // Override this with the new page.
- var historyFunction = path ? window.history.pushState :
- window.history.replaceState;
- historyFunction.call(window.history,
- {pageName: page.name},
- page.title,
- '/' + page.name);
- // Update tab title.
- document.title = page.title;
- };
- /**
- * Shows a registered Overlay page. Does not update history.
- * @param {string} overlayName Page name.
- * @param {OptionPage} rootPage The currently visible root-level page.
- * @return {boolean} whether we showed an overlay.
- */
- OptionsPage.showOverlay_ = function(overlayName, rootPage) {
- var overlay = this.registeredOverlayPages[overlayName.toLowerCase()];
- if (!overlay || !overlay.canShowPage())
- return false;
- if ((!rootPage || !rootPage.sticky) && overlay.parentPage)
- this.showPageByName(overlay.parentPage.name, false);
- if (!overlay.visible) {
- overlay.visible = true;
- if (overlay.didShowPage) overlay.didShowPage();
- }
- return true;
- };
- /**
- * Returns whether or not an overlay is visible.
- * @return {boolean} True if an overlay is visible.
- * @private
- */
- OptionsPage.isOverlayVisible_ = function() {
- return this.getVisibleOverlay_() != null;
- };
- /**
- * Returns the currently visible overlay, or null if no page is visible.
- * @return {OptionPage} The visible overlay.
- */
- OptionsPage.getVisibleOverlay_ = function() {
- for (var name in this.registeredOverlayPages) {
- var page = this.registeredOverlayPages[name];
- if (page.visible)
- return page;
- }
- return null;
- };
- /**
- * Closes the visible overlay. Updates the history state after closing the
- * overlay.
- */
- OptionsPage.closeOverlay = function() {
- var overlay = this.getVisibleOverlay_();
- if (!overlay)
- return;
- overlay.visible = false;
- if (overlay.didClosePage) overlay.didClosePage();
- this.updateHistoryState_();
- };
- /**
- * Hides the visible overlay. Does not affect the history state.
- * @private
- */
- OptionsPage.hideOverlay_ = function() {
- var overlay = this.getVisibleOverlay_();
- if (overlay)
- overlay.visible = false;
- };
- /**
- * Returns the topmost visible page (overlays excluded).
- * @return {OptionPage} The topmost visible page aside any overlay.
- * @private
- */
- OptionsPage.getTopmostVisibleNonOverlayPage_ = function() {
- var topPage = null;
- for (var name in this.registeredPages) {
- var page = this.registeredPages[name];
- if (page.visible &&
- (!topPage || page.nestingLevel > topPage.nestingLevel))
- topPage = page;
- }
- return topPage;
- };
- /**
- * Returns the topmost visible page, or null if no page is visible.
- * @return {OptionPage} The topmost visible page.
- */
- OptionsPage.getTopmostVisiblePage = function() {
- // Check overlays first since they're top-most if visible.
- return this.getVisibleOverlay_() || this.getTopmostVisibleNonOverlayPage_();
- };
- /**
- * Closes the topmost open subpage, if any.
- * @private
- */
- OptionsPage.closeTopSubPage_ = function() {
- var topPage = this.getTopmostVisiblePage();
- if (topPage && !topPage.isOverlay && topPage.parentPage) {
- if (topPage.willHidePage)
- topPage.willHidePage();
- topPage.visible = false;
- }
- this.updateHistoryState_();
- };
- /**
- * Closes all subpages below the given level.
- * @param {number} level The nesting level to close below.
- */
- OptionsPage.closeSubPagesToLevel = function(level) {
- var topPage = this.getTopmostVisiblePage();
- while (topPage && topPage.nestingLevel > level) {
- if (topPage.willHidePage)
- topPage.willHidePage();
- topPage.visible = false;
- topPage = topPage.parentPage;
- }
- this.updateHistoryState_();
- };
- /**
- * Updates managed banner visibility state based on the topmost page.
- */
- OptionsPage.updateManagedBannerVisibility = function() {
- var topPage = this.getTopmostVisiblePage();
- if (topPage)
- topPage.updateManagedBannerVisibility();
- };
- /**
- * Shows the tab contents for the given navigation tab.
- * @param {!Element} tab The tab that the user clicked.
- */
- OptionsPage.showTab = function(tab) {
- // Search parents until we find a tab, or the nav bar itself. This allows
- // tabs to have child nodes, e.g. labels in separately-styled spans.
- while (tab && !tab.classList.contains('subpages-nav-tabs') &&
- !tab.classList.contains('tab')) {
- tab = tab.parentNode;
- }
- if (!tab || !tab.classList.contains('tab'))
- return;
- // Find tab bar of the tab.
- var tabBar = tab;
- while (tabBar && !tabBar.classList.contains('subpages-nav-tabs')) {
- tabBar = tabBar.parentNode;
- }
- if (!tabBar)
- return;
- if (tabBar.activeNavTab != null) {
- tabBar.activeNavTab.classList.remove('active-tab');
- $(tabBar.activeNavTab.getAttribute('tab-contents')).classList.
- remove('active-tab-contents');
- }
- tab.classList.add('active-tab');
- $(tab.getAttribute('tab-contents')).classList.add('active-tab-contents');
- tabBar.activeNavTab = tab;
- };
- /**
- * Registers new options page.
- * @param {OptionsPage} page Page to register.
- */
- OptionsPage.register = function(page) {
- this.registeredPages[page.name.toLowerCase()] = page;
- // Create and add new page <li> element to navbar.
- var pageNav = document.createElement('li');
- pageNav.id = page.name + 'PageNav';
- pageNav.className = 'navbar-item';
- pageNav.setAttribute('pageName', page.name);
- pageNav.setAttribute('role', 'tab');
- pageNav.textContent = page.pageDiv.querySelector('h1').textContent;
- pageNav.tabIndex = -1;
- pageNav.onclick = function(event) {
- OptionsPage.navigateToPage(this.getAttribute('pageName'));
- };
- pageNav.onkeydown = function(event) {
- if ((event.keyCode == 37 || event.keyCode==38) &&
- this.previousSibling && this.previousSibling.onkeydown) {
- // Left and up arrow moves back one tab.
- OptionsPage.navigateToPage(
- this.previousSibling.getAttribute('pageName'));
- this.previousSibling.focus();
- } else if ((event.keyCode == 39 || event.keyCode == 40) &&
- this.nextSibling) {
- // Right and down arrows move forward one tab.
- OptionsPage.navigateToPage(this.nextSibling.getAttribute('pageName'));
- this.nextSibling.focus();
- }
- };
- pageNav.onkeypress = function(event) {
- // Enter or space
- if (event.keyCode == 13 || event.keyCode == 32) {
- OptionsPage.navigateToPage(this.getAttribute('pageName'));
- }
- };
- var navbar = $('navbar');
- navbar.appendChild(pageNav);
- page.tab = pageNav;
- page.initializePage();
- };
- /**
- * Find an enclosing section for an element if it exists.
- * @param {Element} element Element to search.
- * @return {OptionPage} The section element, or null.
- * @private
- */
- OptionsPage.findSectionForNode_ = function(node) {
- while (node = node.parentNode) {
- if (node.nodeName == 'SECTION')
- return node;
- }
- return null;
- };
- /**
- * Registers a new Sub-page.
- * @param {OptionsPage} subPage Sub-page to register.
- * @param {OptionsPage} parentPage Associated parent page for this page.
- * @param {Array} associatedControls Array of control elements that lead to
- * this sub-page. The first item is typically a button in a root-level
- * page. There may be additional buttons for nested sub-pages.
- */
- OptionsPage.registerSubPage = function(subPage,
- parentPage,
- associatedControls) {
- this.registeredPages[subPage.name.toLowerCase()] = subPage;
- subPage.parentPage = parentPage;
- if (associatedControls) {
- subPage.associatedControls = associatedControls;
- if (associatedControls.length) {
- subPage.associatedSection =
- this.findSectionForNode_(associatedControls[0]);
- }
- }
- subPage.tab = undefined;
- subPage.initializePage();
- };
- /**
- * Registers a new Overlay page.
- * @param {OptionsPage} overlay Overlay to register.
- * @param {OptionsPage} parentPage Associated parent page for this overlay.
- * @param {Array} associatedControls Array of control elements associated with
- * this page.
- */
- OptionsPage.registerOverlay = function(overlay,
- parentPage,
- associatedControls) {
- this.registeredOverlayPages[overlay.name.toLowerCase()] = overlay;
- overlay.parentPage = parentPage;
- if (associatedControls) {
- overlay.associatedControls = associatedControls;
- if (associatedControls.length) {
- overlay.associatedSection =
- this.findSectionForNode_(associatedControls[0]);
- }
- }
- // Reverse the button strip for views. See the documentation of
- // reverseButtonStrip_() for an explanation of why this is necessary.
- if (cr.isViews)
- this.reverseButtonStrip_(overlay);
- overlay.tab = undefined;
- overlay.isOverlay = true;
- overlay.initializePage();
- };
- /**
- * Reverses the child elements of a button strip. This is necessary because
- * WebKit does not alter the tab order for elements that are visually reversed
- * using -webkit-box-direction: reverse, and the button order is reversed for
- * views. See https://bugs.webkit.org/show_bug.cgi?id=62664 for more
- * information.
- * @param {Object} overlay The overlay containing the button strip to reverse.
- * @private
- */
- OptionsPage.reverseButtonStrip_ = function(overlay) {
- var buttonStrips = overlay.pageDiv.querySelectorAll('.button-strip');
- // Reverse all button-strips in the overlay.
- for (var j = 0; j < buttonStrips.length; j++) {
- var buttonStrip = buttonStrips[j];
- var childNodes = buttonStrip.childNodes;
- for (var i = childNodes.length - 1; i >= 0; i--)
- buttonStrip.appendChild(childNodes[i]);
- }
- };
- /**
- * Callback for window.onpopstate.
- * @param {Object} data State data pushed into history.
- */
- OptionsPage.setState = function(data) {
- if (data && data.pageName) {
- // It's possible an overlay may be the last top-level page shown.
- if (this.isOverlayVisible_() &&
- !this.registeredOverlayPages[data.pageName.toLowerCase()]) {
- this.hideOverlay_();
- }
- this.showPageByName(data.pageName, false);
- }
- };
- /**
- * Callback for window.onbeforeunload. Used to notify overlays that they will
- * be closed.
- */
- OptionsPage.willClose = function() {
- var overlay = this.getVisibleOverlay_();
- if (overlay && overlay.didClosePage)
- overlay.didClosePage();
- };
- /**
- * Freezes/unfreezes the scroll position of given level's page container.
- * @param {boolean} freeze Whether the page should be frozen.
- * @param {number} level The level to freeze/unfreeze.
- * @private
- */
- OptionsPage.setPageFrozenAtLevel_ = function(freeze, level) {
- var container = level == 0 ? $('page-container')
- : $('subpage-sheet-container-' + level);
- if (container.classList.contains('frozen') == freeze)
- return;
- if (freeze) {
- // Lock the width, since auto width computation may change.
- container.style.width = window.getComputedStyle(container).width;
- container.oldScrollTop = document.body.scrollTop;
- container.classList.add('frozen');
- var verticalPosition =
- container.getBoundingClientRect().top - container.oldScrollTop;
- container.style.top = verticalPosition + 'px';
- this.updateFrozenElementHorizontalPosition_(container);
- } else {
- container.classList.remove('frozen');
- container.style.top = '';
- container.style.left = '';
- container.style.right = '';
- container.style.width = '';
- }
- };
- /**
- * Freezes/unfreezes the scroll position of visible pages based on the current
- * page stack.
- */
- OptionsPage.updatePageFreezeStates = function() {
- var topPage = OptionsPage.getTopmostVisiblePage();
- if (!topPage)
- return;
- var nestingLevel = topPage.isOverlay ? 100 : topPage.nestingLevel;
- for (var i = 0; i <= SUBPAGE_SHEET_COUNT; i++) {
- this.setPageFrozenAtLevel_(i < nestingLevel, i);
- }
- };
- /**
- * Initializes the complete options page. This will cause all C++ handlers to
- * be invoked to do final setup.
- */
- OptionsPage.initialize = function() {
- chrome.send('coreOptionsInitialize');
- this.initialized_ = true;
- document.addEventListener('scroll', this.handleScroll_.bind(this));
- window.addEventListener('resize', this.handleResize_.bind(this));
- if (!document.documentElement.classList.contains('hide-menu')) {
- // Close subpages if the user clicks on the html body. Listen in the
- // capturing phase so that we can stop the click from doing anything.
- document.body.addEventListener('click',
- this.bodyMouseEventHandler_.bind(this),
- true);
- // We also need to cancel mousedowns on non-subpage content.
- document.body.addEventListener('mousedown',
- this.bodyMouseEventHandler_.bind(this),
- true);
- var self = this;
- // Hook up the close buttons.
- subpageCloseButtons = document.querySelectorAll('.close-subpage');
- for (var i = 0; i < subpageCloseButtons.length; i++) {
- subpageCloseButtons[i].onclick = function() {
- self.closeTopSubPage_();
- };
- };
- // Install handler for key presses.
- document.addEventListener('keydown',
- this.keyDownEventHandler_.bind(this));
- document.addEventListener('focus', this.manageFocusChange_.bind(this),
- true);
- }
- // Calculate and store the horizontal locations of elements that may be
- // frozen later.
- var sidebarWidth =
- parseInt(window.getComputedStyle($('mainview')).webkitPaddingStart, 10);
- $('page-container').horizontalOffset = sidebarWidth +
- parseInt(window.getComputedStyle(
- $('mainview-content')).webkitPaddingStart, 10);
- for (var level = 1; level <= SUBPAGE_SHEET_COUNT; level++) {
- var containerId = 'subpage-sheet-container-' + level;
- $(containerId).horizontalOffset = sidebarWidth;
- }
- $('subpage-backdrop').horizontalOffset = sidebarWidth;
- // Trigger the resize handler manually to set the initial state.
- this.handleResize_(null);
- };
- /**
- * Does a bounds check for the element on the given x, y client coordinates.
- * @param {Element} e The DOM element.
- * @param {number} x The client X to check.
- * @param {number} y The client Y to check.
- * @return {boolean} True if the point falls within the element's bounds.
- * @private
- */
- OptionsPage.elementContainsPoint_ = function(e, x, y) {
- var clientRect = e.getBoundingClientRect();
- return x >= clientRect.left && x <= clientRect.right &&
- y >= clientRect.top && y <= clientRect.bottom;
- };
- /**
- * Called when focus changes; ensures that focus doesn't move outside
- * the topmost subpage/overlay.
- * @param {Event} e The focus change event.
- * @private
- */
- OptionsPage.manageFocusChange_ = function(e) {
- var focusableItemsRoot;
- var topPage = this.getTopmostVisiblePage();
- if (!topPage)
- return;
- if (topPage.isOverlay) {
- // If an overlay is visible, that defines the tab loop.
- focusableItemsRoot = topPage.pageDiv;
- } else {
- // If a subpage is visible, use its parent as the tab loop constraint.
- // (The parent is used because it contains the close button.)
- if (topPage.nestingLevel > 0)
- focusableItemsRoot = topPage.pageDiv.parentNode;
- }
- if (focusableItemsRoot && !focusableItemsRoot.contains(e.target))
- topPage.focusFirstElement();
- };
- /**
- * Called when the page is scrolled; moves elements that are position:fixed
- * but should only behave as if they are fixed for vertical scrolling.
- * @param {Event} e The scroll event.
- * @private
- */
- OptionsPage.handleScroll_ = function(e) {
- var scrollHorizontalOffset = document.body.scrollLeft;
- // position:fixed doesn't seem to work for horizontal scrolling in RTL mode,
- // so only adjust in LTR mode (where scroll values will be positive).
- if (scrollHorizontalOffset >= 0) {
- $('navbar-container').style.left = -scrollHorizontalOffset + 'px';
- var subpageBackdrop = $('subpage-backdrop');
- subpageBackdrop.style.left = subpageBackdrop.horizontalOffset -
- scrollHorizontalOffset + 'px';
- this.updateAllFrozenElementPositions_();
- }
- };
- /**
- * Updates all frozen pages to match the horizontal scroll position.
- * @private
- */
- OptionsPage.updateAllFrozenElementPositions_ = function() {
- var frozenElements = document.querySelectorAll('.frozen');
- for (var i = 0; i < frozenElements.length; i++) {
- this.updateFrozenElementHorizontalPosition_(frozenElements[i]);
- }
- };
- /**
- * Updates the given frozen element to match the horizontal scroll position.
- * @param {HTMLElement} e The frozen element to update
- * @private
- */
- OptionsPage.updateFrozenElementHorizontalPosition_ = function(e) {
- if (document.documentElement.dir == 'rtl')
- e.style.right = e.horizontalOffset + 'px';
- else
- e.style.left = e.horizontalOffset - document.body.scrollLeft + 'px';
- };
- /**
- * Called when the page is resized; adjusts the size of elements that depend
- * on the veiwport.
- * @param {Event} e The resize event.
- * @private
- */
- OptionsPage.handleResize_ = function(e) {
- // Set an explicit height equal to the viewport on all the subpage
- // containers shorter than the viewport. This is used instead of
- // min-height: 100% so that there is an explicit height for the subpages'
- // min-height: 100%.
- var viewportHeight = document.documentElement.clientHeight;
- var subpageContainers =
- document.querySelectorAll('.subpage-sheet-container');
- for (var i = 0; i < subpageContainers.length; i++) {
- if (subpageContainers[i].scrollHeight > viewportHeight)
- subpageContainers[i].style.removeProperty('height');
- else
- subpageContainers[i].style.height = viewportHeight + 'px';
- }
- };
- /**
- * A function to handle mouse events (mousedown or click) on the html body by
- * closing subpages and/or stopping event propagation.
- * @return {Event} a mousedown or click event.
- * @private
- */
- OptionsPage.bodyMouseEventHandler_ = function(event) {
- // Do nothing if a subpage isn't showing.
- var topPage = this.getTopmostVisiblePage();
- if (!topPage || topPage.isOverlay || !topPage.parentPage)
- return;
- // Don't close subpages if a user is clicking in a select element.
- // This is necessary because WebKit sends click events with strange
- // coordinates when a user selects a new entry in a select element.
- // See: http://crbug.com/87199
- if (event.srcElement.nodeName == 'SELECT')
- return;
- // Do nothing if the client coordinates are not within the source element.
- // This occurs if the user toggles a checkbox by pressing spacebar.
- // This is a workaround to prevent keyboard events from closing the window.
- // See: crosbug.com/15678
- if (event.clientX == -document.body.scrollLeft &&
- event.clientY == -document.body.scrollTop) {
- return;
- }
- // Don't interfere with navbar clicks.
- if ($('navbar').contains(event.target))
- return;
- // Figure out which page the click happened in.
- for (var level = topPage.nestingLevel; level >= 0; level--) {
- var clickIsWithinLevel = level == 0 ? true :
- OptionsPage.elementContainsPoint_(
- $('subpage-sheet-' + level), event.clientX, event.clientY);
- if (!clickIsWithinLevel)
- continue;
- // Event was within the topmost page; do nothing.
- if (topPage.nestingLevel == level)
- return;
- // Block propgation of both clicks and mousedowns, but only close subpages
- // on click.
- if (event.type == 'click')
- this.closeSubPagesToLevel(level);
- event.stopPropagation();
- event.preventDefault();
- return;
- }
- };
- /**
- * A function to handle key press events.
- * @return {Event} a keydown event.
- * @private
- */
- OptionsPage.keyDownEventHandler_ = function(event) {
- // Close the top overlay or sub-page on esc.
- if (event.keyCode == 27) { // Esc
- if (this.isOverlayVisible_())
- this.closeOverlay();
- else
- this.closeTopSubPage_();
- }
- };
- OptionsPage.setClearPluginLSODataEnabled = function(enabled) {
- if (enabled) {
- document.documentElement.setAttribute(
- 'flashPluginSupportsClearSiteData', '');
- } else {
- document.documentElement.removeAttribute(
- 'flashPluginSupportsClearSiteData');
- }
- };
- /**
- * Re-initializes the C++ handlers if necessary. This is called if the
- * handlers are torn down and recreated but the DOM may not have been (in
- * which case |initialize| won't be called again). If |initialize| hasn't been
- * called, this does nothing (since it will be later, once the DOM has
- * finished loading).
- */
- OptionsPage.reinitializeCore = function() {
- if (this.initialized_)
- chrome.send('coreOptionsInitialize');
- }
- OptionsPage.prototype = {
- __proto__: cr.EventTarget.prototype,
- /**
- * The parent page of this option page, or null for top-level pages.
- * @type {OptionsPage}
- */
- parentPage: null,
- /**
- * The section on the parent page that is associated with this page.
- * Can be null.
- * @type {Element}
- */
- associatedSection: null,
- /**
- * An array of controls that are associated with this page. The first
- * control should be located on a top-level page.
- * @type {OptionsPage}
- */
- associatedControls: null,
- /**
- * Initializes page content.
- */
- initializePage: function() {},
- /**
- * Updates managed banner visibility state. This function iterates over
- * all input fields of a window and if any of these is marked as managed
- * it triggers the managed banner to be visible. The banner can be enforced
- * being on through the managed flag of this class but it can not be forced
- * being off if managed items exist.
- */
- updateManagedBannerVisibility: function() {
- var bannerDiv = $('managed-prefs-banner');
- var controlledByPolicy = false;
- var controlledByExtension = false;
- var inputElements = this.pageDiv.querySelectorAll('input[controlled-by]');
- for (var i = 0, len = inputElements.length; i < len; i++) {
- if (inputElements[i].controlledBy == 'policy')
- controlledByPolicy = true;
- else if (inputElements[i].controlledBy == 'extension')
- controlledByExtension = true;
- }
- if (!controlledByPolicy && !controlledByExtension) {
- bannerDiv.hidden = true;
- } else {
- bannerDiv.hidden = false;
- var height = window.getComputedStyle(bannerDiv).height;
- if (controlledByPolicy && !controlledByExtension) {
- $('managed-prefs-text').textContent =
- templateData.policyManagedPrefsBannerText;
- } else if (!controlledByPolicy && controlledByExtension) {
- $('managed-prefs-text').textContent =
- templateData.extensionManagedPrefsBannerText;
- } else if (controlledByPolicy && controlledByExtension) {
- $('managed-prefs-text').textContent =
- templateData.policyAndExtensionManagedPrefsBannerText;
- }
- }
- },
- /**
- * Gets page visibility state.
- */
- get visible() {
- return !this.pageDiv.hidden;
- },
- /**
- * Sets page visibility.
- */
- set visible(visible) {
- if ((this.visible && visible) || (!this.visible && !visible))
- return;
- this.setContainerVisibility_(visible);
- if (visible) {
- this.pageDiv.hidden = false;
- if (this.tab) {
- this.tab.classList.add('navbar-item-selected');
- this.tab.setAttribute('aria-selected', 'true');
- this.tab.tabIndex = 0;
- }
- } else {
- this.pageDiv.hidden = true;
- if (this.tab) {
- this.tab.classList.remove('navbar-item-selected');
- this.tab.setAttribute('aria-selected', 'false');
- this.tab.tabIndex = -1;
- }
- }
- OptionsPage.updatePageFreezeStates();
- // The managed prefs banner is global, so after any visibility change
- // update it based on the topmost page, not necessarily this page
- // (e.g., if an ancestor is made visible after a child).
- OptionsPage.updateManagedBannerVisibility();
- // A subpage was shown or hidden.
- if (!this.isOverlay && this.nestingLevel > 0) {
- OptionsPage.updateSubpageBackdrop_();
- OptionsPage.updateScrollPosition_();
- }
- cr.dispatchPropertyChange(this, 'visible', visible, !visible);
- },
- /**
- * Shows or hides this page's container.
- * @param {boolean} visible Whether the container should be visible or not.
- * @private
- */
- setContainerVisibility_: function(visible) {
- var container = null;
- if (this.isOverlay) {
- container = $('overlay');
- } else {
- var nestingLevel = this.nestingLevel;
- if (nestingLevel > 0)
- container = $('subpage-sheet-container-' + nestingLevel);
- }
- var isSubpage = !this.isOverlay;
- if (!container)
- return;
- if (container.hidden != visible) {
- if (visible) {
- // If the container is set hidden and then immediately set visible
- // again, the fadeCompleted_ callback would cause it to be erroneously
- // hidden again. Removing the transparent tag avoids that.
- container.classList.remove('transparent');
- }
- return;
- }
- if (visible) {
- container.hidden = false;
- if (isSubpage) {
- var computedStyle = window.getComputedStyle(container);
- container.style.WebkitPaddingStart =
- parseInt(computedStyle.WebkitPaddingStart, 10) + 100 + 'px';
- }
- // Separate animating changes from the removal of display:none.
- window.setTimeout(function() {
- container.classList.remove('transparent');
- if (isSubpage)
- container.style.WebkitPaddingStart = '';
- });
- } else {
- var self = this;
- container.addEventListener('webkitTransitionEnd', function f(e) {
- if (e.propertyName != 'opacity')
- return;
- container.removeEventListener('webkitTransitionEnd', f);
- self.fadeCompleted_(container);
- });
- container.classList.add('transparent');
- }
- },
- /**
- * Called when a container opacity transition finishes.
- * @param {HTMLElement} container The container element.
- * @private
- */
- fadeCompleted_: function(container) {
- if (container.classList.contains('transparent'))
- container.hidden = true;
- },
- /**
- * Focuses the first control on the page.
- */
- focusFirstElement: function() {
- // Sets focus on the first interactive element in the page.
- var focusElement =
- this.pageDiv.querySelector('button, input, list, select');
- if (focusElement)
- focusElement.focus();
- },
- /**
- * The nesting level of this page.
- * @type {number} The nesting level of this page (0 for top-level page)
- */
- get nestingLevel() {
- var level = 0;
- var parent = this.parentPage;
- while (parent) {
- level++;
- parent = parent.parentPage;
- }
- return level;
- },
- /**
- * Whether the page is considered 'sticky', such that it will
- * remain a top-level page even if sub-pages change.
- * @type {boolean} True if this page is sticky.
- */
- get sticky() {
- return false;
- },
- /**
- * Checks whether this page is an ancestor of the given page in terms of
- * subpage nesting.
- * @param {OptionsPage} page
- * @return {boolean} True if this page is nested under |page|
- */
- isAncestorOfPage: function(page) {
- var parent = page.parentPage;
- while (parent) {
- if (parent == this)
- return true;
- parent = parent.parentPage;
- }
- return false;
- },
- /**
- * Whether it should be possible to show the page.
- * @return {boolean} True if the page should be shown
- */
- canShowPage: function() {
- return true;
- },
- };
- // Export
- return {
- OptionsPage: OptionsPage
- };
- });
- // 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() {
- const Tree = cr.ui.Tree;
- const TreeItem = cr.ui.TreeItem;
- /**
- * Creates a new tree item for certificate data.
- * @param {Object=} data Data used to create a certificate tree item.
- * @constructor
- * @extends {TreeItem}
- */
- function CertificateTreeItem(data) {
- // TODO(mattm): other columns
- var treeItem = new TreeItem({
- label: data.name,
- data: data
- });
- treeItem.__proto__ = CertificateTreeItem.prototype;
- if (data.icon) {
- treeItem.icon = data.icon;
- }
- if (data.untrusted) {
- var badge = document.createElement('span');
- badge.setAttribute('class', 'certUntrusted');
- badge.textContent = localStrings.getString("badgeCertUntrusted");
- treeItem.labelElement.insertBefore(
- badge, treeItem.labelElement.firstChild);
- }
- return treeItem;
- }
- CertificateTreeItem.prototype = {
- __proto__: TreeItem.prototype,
- /**
- * The tree path id/.
- * @type {string}
- */
- get pathId() {
- var parent = this.parentItem;
- if (parent && parent instanceof CertificateTreeItem) {
- return parent.pathId + ',' + this.data.id;
- } else {
- return this.data.id;
- }
- }
- };
- /**
- * Creates a new cookies tree.
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
- * @extends {Tree}
- */
- var CertificatesTree = cr.ui.define('tree');
- CertificatesTree.prototype = {
- __proto__: Tree.prototype,
- /** @inheritDoc */
- decorate: function() {
- Tree.prototype.decorate.call(this);
- this.treeLookup_ = {};
- },
- /** @inheritDoc */
- addAt: function(child, index) {
- Tree.prototype.addAt.call(this, child, index);
- if (child.data && child.data.id)
- this.treeLookup_[child.data.id] = child;
- },
- /** @inheritDoc */
- remove: function(child) {
- Tree.prototype.remove.call(this, child);
- if (child.data && child.data.id)
- delete this.treeLookup_[child.data.id];
- },
- /**
- * Clears the tree.
- */
- clear: function() {
- // Remove all fields without recreating the object since other code
- // references it.
- for (var id in this.treeLookup_){
- delete this.treeLookup_[id];
- }
- this.textContent = '';
- },
- /**
- * Populate the tree.
- * @param {Array} nodesData Nodes data array.
- */
- populate: function(nodesData) {
- this.clear();
- for (var i = 0; i < nodesData.length; ++i) {
- var subnodes = nodesData[i]['subnodes'];
- delete nodesData[i]['subnodes'];
- var item = new CertificateTreeItem(nodesData[i]);
- this.addAt(item, i);
- for (var j = 0; j < subnodes.length; ++j) {
- var subitem = new CertificateTreeItem(subnodes[j]);
- item.addAt(subitem, j);
- }
- // Make tree expanded by default.
- item.expanded = true;
- }
- cr.dispatchSimpleEvent(this, 'change');
- },
- };
- return {
- CertificatesTree: CertificatesTree
- };
- });
- // 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 OptionsPage = options.OptionsPage;
- /////////////////////////////////////////////////////////////////////////////
- // CertificateManagerTab class:
- /**
- * blah
- * @param {!string} id The id of this tab.
- */
- function CertificateManagerTab(id) {
- this.tree = $(id + '-tree');
- options.CertificatesTree.decorate(this.tree);
- this.tree.addEventListener('change',
- this.handleCertificatesTreeChange_.bind(this));
- var tree = this.tree;
- this.viewButton = $(id + '-view');
- this.viewButton.onclick = function(e) {
- var selected = tree.selectedItem;
- chrome.send('viewCertificate', [selected.data.id]);
- }
- this.editButton = $(id + '-edit');
- if (this.editButton !== null) {
- if (id == 'serverCertsTab') {
- this.editButton.onclick = function(e) {
- var selected = tree.selectedItem;
- chrome.send('editServerCertificate', [selected.data.id]);
- }
- } else if (id == 'caCertsTab') {
- this.editButton.onclick = function(e) {
- var data = tree.selectedItem.data;
- CertificateEditCaTrustOverlay.show(data.id, data.name);
- }
- }
- }
- this.backupButton = $(id + '-backup');
- if (this.backupButton !== null) {
- this.backupButton.onclick = function(e) {
- var selected = tree.selectedItem;
- chrome.send('exportPersonalCertificate', [selected.data.id]);
- }
- }
- this.backupAllButton = $(id + '-backup-all');
- if (this.backupAllButton !== null) {
- this.backupAllButton.onclick = function(e) {
- chrome.send('exportAllPersonalCertificates', []);
- }
- }
- this.importButton = $(id + '-import');
- if (this.importButton !== null) {
- if (id == 'personalCertsTab') {
- this.importButton.onclick = function(e) {
- chrome.send('importPersonalCertificate', [false]);
- }
- } else if (id == 'serverCertsTab') {
- this.importButton.onclick = function(e) {
- chrome.send('importServerCertificate', []);
- }
- } else if (id == 'caCertsTab') {
- this.importButton.onclick = function(e) {
- chrome.send('importCaCertificate', []);
- }
- }
- }
- this.importAndBindButton = $(id + '-import-and-bind');
- if (this.importAndBindButton !== null) {
- if (id == 'personalCertsTab') {
- this.importAndBindButton.onclick = function(e) {
- chrome.send('importPersonalCertificate', [true]);
- }
- }
- }
- this.exportButton = $(id + '-export');
- if (this.exportButton !== null) {
- this.exportButton.onclick = function(e) {
- var selected = tree.selectedItem;
- chrome.send('exportCertificate', [selected.data.id]);
- }
- }
- this.deleteButton = $(id + '-delete');
- this.deleteButton.onclick = function(e) {
- var data = tree.selectedItem.data;
- AlertOverlay.show(
- localStrings.getStringF(id + 'DeleteConfirm', data.name),
- localStrings.getString(id + 'DeleteImpact'),
- localStrings.getString('ok'),
- localStrings.getString('cancel'),
- function() {
- tree.selectedItem = null;
- chrome.send('deleteCertificate', [data.id]);
- });
- }
- }
- CertificateManagerTab.prototype = {
- /**
- * Update button state.
- * @private
- * @param {!Object} data The data of the selected item.
- */
- updateButtonState: function(data) {
- var isCert = !!data && data.id.substr(0, 5) == 'cert-';
- var readOnly = !!data && data.readonly;
- var hasChildren = this.tree.items.length > 0;
- this.viewButton.disabled = !isCert;
- if (this.editButton !== null)
- this.editButton.disabled = !isCert;
- if (this.backupButton !== null)
- this.backupButton.disabled = !isCert;
- if (this.backupAllButton !== null)
- this.backupAllButton.disabled = !hasChildren;
- if (this.exportButton !== null)
- this.exportButton.disabled = !isCert;
- this.deleteButton.disabled = !isCert || readOnly;
- },
- /**
- * Handles certificate tree selection change.
- * @private
- * @param {!Event} e The change event object.
- */
- handleCertificatesTreeChange_: function(e) {
- var data = null;
- if (this.tree.selectedItem) {
- data = this.tree.selectedItem.data;
- }
- this.updateButtonState(data);
- },
- }
- // TODO(xiyuan): Use notification from backend instead of polling.
- // TPM token check polling timer.
- var tpmPollingTimer;
- // Initiate tpm token check if needed.
- function checkTpmToken() {
- var importAndBindButton = $('personalCertsTab-import-and-bind');
- if (importAndBindButton && importAndBindButton.disabled)
- chrome.send('checkTpmTokenReady');
- }
- // Stop tpm polling timer.
- function stopTpmTokenCheckPolling() {
- if (tpmPollingTimer) {
- window.clearTimeout(tpmPollingTimer);
- tpmPollingTimer = undefined;
- }
- }
- /////////////////////////////////////////////////////////////////////////////
- // CertificateManager class:
- /**
- * Encapsulated handling of ChromeOS accounts options page.
- * @constructor
- */
- function CertificateManager(model) {
- OptionsPage.call(this, 'certificates',
- templateData.certificateManagerPageTabTitle,
- 'certificateManagerPage');
- }
- cr.addSingletonGetter(CertificateManager);
- CertificateManager.prototype = {
- __proto__: OptionsPage.prototype,
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- this.personalTab = new CertificateManagerTab('personalCertsTab');
- this.serverTab = new CertificateManagerTab('serverCertsTab');
- this.caTab = new CertificateManagerTab('caCertsTab');
- this.otherTab = new CertificateManagerTab('otherCertsTab');
- this.addEventListener('visibleChange', this.handleVisibleChange_);
- },
- initalized_: false,
- /**
- * Handler for OptionsPage's visible property change event.
- * @private
- * @param {Event} e Property change event.
- */
- handleVisibleChange_: function(e) {
- if (!this.initalized_ && this.visible) {
- this.initalized_ = true;
- OptionsPage.showTab($('personal-certs-nav-tab'));
- chrome.send('populateCertificateManager');
- }
- if (cr.isChromeOS) {
- // Ensure TPM token check on visible and stop polling when hidden.
- if (this.visible)
- checkTpmToken();
- else
- stopTpmTokenCheckPolling();
- }
- }
- };
- // CertificateManagerHandler callbacks.
- CertificateManager.onPopulateTree = function(args) {
- $(args[0]).populate(args[1]);
- };
- CertificateManager.exportPersonalAskPassword = function(args) {
- CertificateBackupOverlay.show();
- };
- CertificateManager.importPersonalAskPassword = function(args) {
- CertificateRestoreOverlay.show();
- };
- CertificateManager.onCheckTpmTokenReady = function(ready) {
- var importAndBindButton = $('personalCertsTab-import-and-bind');
- if (importAndBindButton) {
- importAndBindButton.disabled = !ready;
- // Check again after 5 seconds if Tpm is not ready and certificate manager
- // is still visible.
- if (!ready && CertificateManager.getInstance().visible)
- tpmPollingTimer = window.setTimeout(checkTpmToken, 5000);
- }
- };
- // Export
- return {
- CertificateManagerTab: CertificateManagerTab,
- CertificateManager: CertificateManager
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- /**
- * CertificateRestoreOverlay class
- * Encapsulated handling of the 'enter restore password' overlay page.
- * @class
- */
- function CertificateRestoreOverlay() {
- OptionsPage.call(this, 'certificateRestore',
- '',
- 'certificateRestoreOverlay');
- }
- cr.addSingletonGetter(CertificateRestoreOverlay);
- CertificateRestoreOverlay.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * Initializes the page.
- */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- var self = this;
- $('certificateRestoreCancelButton').onclick = function(event) {
- self.cancelRestore_();
- }
- $('certificateRestoreOkButton').onclick = function(event) {
- self.finishRestore_();
- }
- self.clearInputFields_();
- },
- /**
- * Clears any uncommitted input, and dismisses the overlay.
- * @private
- */
- dismissOverlay_: function() {
- this.clearInputFields_();
- OptionsPage.closeOverlay();
- },
- /**
- * Attempt the restore operation.
- * The overlay will be left up with inputs disabled until the backend
- * finishes and dismisses it.
- * @private
- */
- finishRestore_: function() {
- chrome.send('importPersonalCertificatePasswordSelected',
- [$('certificateRestorePassword').value]);
- $('certificateRestoreCancelButton').disabled = true;
- $('certificateRestoreOkButton').disabled = true;
- },
- /**
- * Cancel the restore operation.
- * @private
- */
- cancelRestore_: function() {
- chrome.send('cancelImportExportCertificate');
- this.dismissOverlay_();
- },
- /**
- * Clears the value of each input field.
- * @private
- */
- clearInputFields_: function() {
- $('certificateRestorePassword').value = '';
- $('certificateRestoreCancelButton').disabled = false;
- $('certificateRestoreOkButton').disabled = false;
- },
- };
- CertificateRestoreOverlay.show = function() {
- CertificateRestoreOverlay.getInstance().clearInputFields_();
- OptionsPage.navigateToPage('certificateRestore');
- };
- CertificateRestoreOverlay.dismiss = function() {
- CertificateRestoreOverlay.getInstance().dismissOverlay_();
- };
- // Export
- return {
- CertificateRestoreOverlay: CertificateRestoreOverlay
- };
- });
- // Copyright (c) 2010 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() {
- const OptionsPage = options.OptionsPage;
- /**
- * CertificateBackupOverlay class
- * Encapsulated handling of the 'enter backup password' overlay page.
- * @class
- */
- function CertificateBackupOverlay() {
- OptionsPage.call(this, 'certificateBackupOverlay',
- '',
- 'certificateBackupOverlay');
- }
- cr.addSingletonGetter(CertificateBackupOverlay);
- CertificateBackupOverlay.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * Initializes the page.
- */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- var self = this;
- $('certificateBackupCancelButton').onclick = function(event) {
- self.cancelBackup_();
- }
- $('certificateBackupOkButton').onclick = function(event) {
- self.finishBackup_();
- }
- $('certificateBackupPassword').oninput =
- $('certificateBackupPassword2').oninput = function(event) {
- self.comparePasswords_();
- }
- self.clearInputFields_();
- },
- /**
- * Clears any uncommitted input, and dismisses the overlay.
- * @private
- */
- dismissOverlay_: function() {
- this.clearInputFields_();
- OptionsPage.closeOverlay();
- },
- /**
- * Attempt the Backup operation.
- * The overlay will be left up with inputs disabled until the backend
- * finishes and dismisses it.
- * @private
- */
- finishBackup_: function() {
- chrome.send('exportPersonalCertificatePasswordSelected',
- [$('certificateBackupPassword').value]);
- $('certificateBackupCancelButton').disabled = true;
- $('certificateBackupOkButton').disabled = true;
- $('certificateBackupPassword').disabled = true;
- $('certificateBackupPassword2').disabled = true;
- },
- /**
- * Cancel the Backup operation.
- * @private
- */
- cancelBackup_: function() {
- chrome.send('cancelImportExportCertificate');
- this.dismissOverlay_();
- },
- /**
- * Compares the password fields and sets the button state appropriately.
- * @private
- */
- comparePasswords_: function() {
- var password1 = $('certificateBackupPassword').value;
- var password2 = $('certificateBackupPassword2').value;
- $('certificateBackupOkButton').disabled =
- !password1 || password1 != password2;
- },
- /**
- * Clears the value of each input field.
- * @private
- */
- clearInputFields_: function() {
- $('certificateBackupPassword').value = '';
- $('certificateBackupPassword2').value = '';
- $('certificateBackupPassword').disabled = false;
- $('certificateBackupPassword2').disabled = false;
- $('certificateBackupCancelButton').disabled = false;
- $('certificateBackupOkButton').disabled = true;
- },
- };
- CertificateBackupOverlay.show = function() {
- CertificateBackupOverlay.getInstance().clearInputFields_();
- OptionsPage.navigateToPage('certificateBackupOverlay');
- };
- CertificateBackupOverlay.dismiss = function() {
- CertificateBackupOverlay.getInstance().dismissOverlay_();
- };
- // Export
- return {
- CertificateBackupOverlay: CertificateBackupOverlay
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- /**
- * CertificateEditCaTrustOverlay class
- * Encapsulated handling of the 'edit ca trust' and 'import ca' overlay pages.
- * @class
- */
- function CertificateEditCaTrustOverlay() {
- OptionsPage.call(this, 'certificateEditCaTrustOverlay',
- '',
- 'certificateEditCaTrustOverlay');
- }
- cr.addSingletonGetter(CertificateEditCaTrustOverlay);
- CertificateEditCaTrustOverlay.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * Dismisses the overlay.
- * @private
- */
- dismissOverlay_: function() {
- OptionsPage.closeOverlay();
- },
- /**
- * Enables or disables input fields.
- * @private
- */
- enableInputs_: function(enabled) {
- $('certificateCaTrustSSLCheckbox').disabled =
- $('certificateCaTrustEmailCheckbox').disabled =
- $('certificateCaTrustObjSignCheckbox').disabled =
- $('certificateEditCaTrustCancelButton').disabled =
- $('certificateEditCaTrustOkButton').disabled = !enabled;
- },
- /**
- * Attempt the Edit operation.
- * The overlay will be left up with inputs disabled until the backend
- * finishes and dismisses it.
- * @private
- */
- finishEdit_: function() {
- // TODO(mattm): Send checked values as booleans. For now send them as
- // strings, since WebUIBindings::send does not support any other types :(
- chrome.send('editCaCertificateTrust',
- [this.certId,
- $('certificateCaTrustSSLCheckbox').checked.toString(),
- $('certificateCaTrustEmailCheckbox').checked.toString(),
- $('certificateCaTrustObjSignCheckbox').checked.toString()]);
- this.enableInputs_(false);
- },
- /**
- * Cancel the Edit operation.
- * @private
- */
- cancelEdit_: function() {
- this.dismissOverlay_();
- },
- /**
- * Attempt the Import operation.
- * The overlay will be left up with inputs disabled until the backend
- * finishes and dismisses it.
- * @private
- */
- finishImport_: function() {
- // TODO(mattm): Send checked values as booleans. For now send them as
- // strings, since WebUIBindings::send does not support any other types :(
- chrome.send('importCaCertificateTrustSelected',
- [$('certificateCaTrustSSLCheckbox').checked.toString(),
- $('certificateCaTrustEmailCheckbox').checked.toString(),
- $('certificateCaTrustObjSignCheckbox').checked.toString()]);
- this.enableInputs_(false);
- },
- /**
- * Cancel the Import operation.
- * @private
- */
- cancelImport_: function() {
- chrome.send('cancelImportExportCertificate');
- this.dismissOverlay_();
- },
- };
- /**
- * Callback from CertificateManagerHandler with the trust values.
- * @param {boolean} trustSSL The initial value of SSL trust checkbox.
- * @param {boolean} trustEmail The initial value of Email trust checkbox.
- * @param {boolean} trustObjSign The initial value of Object Signing trust
- */
- CertificateEditCaTrustOverlay.populateTrust = function(
- trustSSL, trustEmail, trustObjSign) {
- $('certificateCaTrustSSLCheckbox').checked = trustSSL;
- $('certificateCaTrustEmailCheckbox').checked = trustEmail;
- $('certificateCaTrustObjSignCheckbox').checked = trustObjSign;
- CertificateEditCaTrustOverlay.getInstance().enableInputs_(true);
- }
- /**
- * Show the Edit CA Trust overlay.
- * @param {string} certId The id of the certificate to be passed to the
- * certificate manager model.
- * @param {string} certName The display name of the certificate.
- * checkbox.
- */
- CertificateEditCaTrustOverlay.show = function(certId, certName) {
- var self = CertificateEditCaTrustOverlay.getInstance();
- self.certId = certId;
- $('certificateEditCaTrustCancelButton').onclick = function(event) {
- self.cancelEdit_();
- }
- $('certificateEditCaTrustOkButton').onclick = function(event) {
- self.finishEdit_();
- }
- $('certificateEditCaTrustDescription').textContent =
- localStrings.getStringF('certificateEditCaTrustDescriptionFormat',
- certName);
- self.enableInputs_(false);
- OptionsPage.navigateToPage('certificateEditCaTrustOverlay');
- chrome.send('getCaCertificateTrust', [certId]);
- }
- /**
- * Show the Import CA overlay.
- * @param {string} certId The id of the certificate to be passed to the
- * certificate manager model.
- * @param {string} certName The display name of the certificate.
- * checkbox.
- */
- CertificateEditCaTrustOverlay.showImport = function(certName) {
- var self = CertificateEditCaTrustOverlay.getInstance();
- // TODO(mattm): do we want a view certificate button here like firefox has?
- $('certificateEditCaTrustCancelButton').onclick = function(event) {
- self.cancelImport_();
- }
- $('certificateEditCaTrustOkButton').onclick = function(event) {
- self.finishImport_();
- }
- $('certificateEditCaTrustDescription').textContent =
- localStrings.getStringF('certificateImportCaDescriptionFormat',
- certName);
- CertificateEditCaTrustOverlay.populateTrust(false, false, false);
- OptionsPage.navigateToPage('certificateEditCaTrustOverlay');
- }
- CertificateEditCaTrustOverlay.dismiss = function() {
- CertificateEditCaTrustOverlay.getInstance().dismissOverlay_();
- };
- // Export
- return {
- CertificateEditCaTrustOverlay: CertificateEditCaTrustOverlay
- };
- });
- // Copyright (c) 2010 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 OptionsPage = options.OptionsPage;
- /**
- * CertificateImportErrorOverlay class
- * Displays a list of certificates and errors.
- * @class
- */
- function CertificateImportErrorOverlay() {
- OptionsPage.call(this, 'certificateImportErrorOverlay', '',
- 'certificateImportErrorOverlay');
- }
- cr.addSingletonGetter(CertificateImportErrorOverlay);
- CertificateImportErrorOverlay.prototype = {
- // Inherit CertificateImportErrorOverlay from OptionsPage.
- __proto__: OptionsPage.prototype,
- /**
- * Initialize the page.
- */
- initializePage: function() {
- // Call base class implementation to start preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- $('certificateImportErrorOverlayOk').onclick = function(event) {
- OptionsPage.closeOverlay();
- };
- },
- };
- /**
- * Show an alert overlay with the given message, button titles, and
- * callbacks.
- * @param {string} title The alert title to display to the user.
- * @param {string} message The alert message to display to the user.
- * @param {Array} certErrors The list of cert errors. Each error should have
- * a .name and .error attribute.
- */
- CertificateImportErrorOverlay.show = function(title, message, certErrors) {
- $('certificateImportErrorOverlayTitle').textContent = title;
- $('certificateImportErrorOverlayMessage').textContent = message;
- ul = $('certificateImportErrorOverlayCertErrors');
- ul.innerHTML = '';
- for (var i = 0; i < certErrors.length; ++i) {
- li = document.createElement("li");
- li.textContent = localStrings.getStringF('certificateImportErrorFormat',
- certErrors[i].name,
- certErrors[i].error);
- ul.appendChild(li);
- }
- OptionsPage.navigateToPage('certificateImportErrorOverlay');
- }
- // Export
- return {
- CertificateImportErrorOverlay: CertificateImportErrorOverlay
- };
- });
- var CertificateManager = options.CertificateManager;
- var CertificateRestoreOverlay = options.CertificateRestoreOverlay;
- var CertificateBackupOverlay = options.CertificateBackupOverlay;
- var CertificateEditCaTrustOverlay = options.CertificateEditCaTrustOverlay;
- var CertificateImportErrorOverlay = options.CertificateImportErrorOverlay;
- // 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 OptionsPage = options.OptionsPage;
- //
- // AdvancedOptions class
- // Encapsulated handling of advanced options page.
- //
- function AdvancedOptions() {
- OptionsPage.call(this, 'advanced', templateData.advancedPageTabTitle,
- 'advancedPage');
- }
- cr.addSingletonGetter(AdvancedOptions);
- AdvancedOptions.prototype = {
- // Inherit AdvancedOptions from OptionsPage.
- __proto__: options.OptionsPage.prototype,
- /**
- * Initializes the page.
- */
- initializePage: function() {
- // Call base class implementation to starts preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- // Set up click handlers for buttons.
- $('privacyContentSettingsButton').onclick = function(event) {
- OptionsPage.navigateToPage('content');
- OptionsPage.showTab($('cookies-nav-tab'));
- chrome.send('coreOptionsUserMetricsAction',
- ['Options_ContentSettings']);
- };
- $('privacyClearDataButton').onclick = function(event) {
- OptionsPage.navigateToPage('clearBrowserData');
- chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
- };
- // 'metricsReportingEnabled' element is only present on Chrome branded
- // builds.
- if ($('metricsReportingEnabled')) {
- $('metricsReportingEnabled').onclick = function(event) {
- chrome.send('metricsReportingCheckboxAction',
- [String(event.target.checked)]);
- };
- }
- if (!cr.isChromeOS) {
- $('autoOpenFileTypesResetToDefault').onclick = function(event) {
- chrome.send('autoOpenFileTypesAction');
- };
- }
- $('fontSettingsCustomizeFontsButton').onclick = function(event) {
- OptionsPage.navigateToPage('fonts');
- chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
- };
- $('defaultFontSize').onchange = function(event) {
- chrome.send('defaultFontSizeAction',
- [String(event.target.options[event.target.selectedIndex].value)]);
- };
- $('defaultZoomFactor').onchange = function(event) {
- chrome.send('defaultZoomFactorAction',
- [String(event.target.options[event.target.selectedIndex].value)]);
- };
- $('language-button').onclick = function(event) {
- OptionsPage.navigateToPage('languages');
- chrome.send('coreOptionsUserMetricsAction',
- ['Options_LanuageAndSpellCheckSettings']);
- };
- if (cr.isWindows || cr.isMac) {
- $('certificatesManageButton').onclick = function(event) {
- chrome.send('showManageSSLCertificates');
- };
- } else {
- $('certificatesManageButton').onclick = function(event) {
- OptionsPage.navigateToPage('certificates');
- chrome.send('coreOptionsUserMetricsAction',
- ['Options_ManageSSLCertificates']);
- };
- }
- if (!cr.isChromeOS) {
- $('proxiesConfigureButton').onclick = function(event) {
- chrome.send('showNetworkProxySettings');
- };
- $('downloadLocationChangeButton').onclick = function(event) {
- chrome.send('selectDownloadLocation');
- };
- // This text field is always disabled. Setting ".disabled = true" isn't
- // enough, since a policy can disable it but shouldn't re-enable when
- // it is removed.
- $('downloadLocationPath').setDisabled('readonly', true);
- }
- $('sslCheckRevocation').onclick = function(event) {
- chrome.send('checkRevocationCheckboxAction',
- [String($('sslCheckRevocation').checked)]);
- };
- if ($('backgroundModeCheckbox')) {
- $('backgroundModeCheckbox').onclick = function(event) {
- chrome.send('backgroundModeAction',
- [String($('backgroundModeCheckbox').checked)]);
- };
- }
- // 'cloudPrintProxyEnabled' is true for Chrome branded builds on
- // certain platforms, or could be enabled by a lab.
- if (!cr.isChromeOS) {
- $('cloudPrintProxySetupButton').onclick = function(event) {
- if ($('cloudPrintProxyManageButton').style.display == 'none') {
- // Disable the button, set it's text to the intermediate state.
- $('cloudPrintProxySetupButton').textContent =
- localStrings.getString('cloudPrintProxyEnablingButton');
- $('cloudPrintProxySetupButton').disabled = true;
- chrome.send('showCloudPrintSetupDialog');
- } else {
- chrome.send('disableCloudPrintProxy');
- }
- };
- }
- $('cloudPrintProxyManageButton').onclick = function(event) {
- chrome.send('showCloudPrintManagePage');
- };
- }
- };
- //
- // Chrome callbacks
- //
- // Set the checked state of the metrics reporting checkbox.
- AdvancedOptions.SetMetricsReportingCheckboxState = function(
- checked, disabled) {
- $('metricsReportingEnabled').checked = checked;
- $('metricsReportingEnabled').disabled = disabled;
- if (disabled)
- $('metricsReportingEnabledText').className = 'disable-services-span';
- }
- AdvancedOptions.SetMetricsReportingSettingVisibility = function(visible) {
- if (visible) {
- $('metricsReportingSetting').style.display = 'block';
- } else {
- $('metricsReportingSetting').style.display = 'none';
- }
- }
- // Set the font size selected item.
- AdvancedOptions.SetFontSize = function(font_size_value) {
- var selectCtl = $('defaultFontSize');
- for (var i = 0; i < selectCtl.options.length; i++) {
- if (selectCtl.options[i].value == font_size_value) {
- selectCtl.selectedIndex = i;
- if ($('Custom'))
- selectCtl.remove($('Custom').index);
- return;
- }
- }
- // Add/Select Custom Option in the font size label list.
- if (!$('Custom')) {
- var option = new Option(localStrings.getString('fontSizeLabelCustom'),
- -1, false, true);
- option.setAttribute("id", "Custom");
- selectCtl.add(option);
- }
- $('Custom').selected = true;
- };
- /**
- * Populate the page zoom selector with values received from the caller.
- * @param {Array} items An array of items to populate the selector.
- * each object is an array with three elements as follows:
- * 0: The title of the item (string).
- * 1: The value of the item (number).
- * 2: Whether the item should be selected (boolean).
- */
- AdvancedOptions.SetupPageZoomSelector = function(items) {
- var element = $('defaultZoomFactor');
- // Remove any existing content.
- element.textContent = '';
- // Insert new child nodes into select element.
- var value, title, selected;
- for (var i = 0; i < items.length; i++) {
- title = items[i][0];
- value = items[i][1];
- selected = items[i][2];
- element.appendChild(new Option(title, value, false, selected));
- }
- };
- // Set the enabled state for the autoOpenFileTypesResetToDefault button.
- AdvancedOptions.SetAutoOpenFileTypesDisabledAttribute = function(disabled) {
- if (!cr.isChromeOS) {
- $('autoOpenFileTypesResetToDefault').disabled = disabled;
- if (disabled)
- $('auto-open-file-types-label').classList.add('disabled');
- else
- $('auto-open-file-types-label').classList.remove('disabled');
- }
- };
- // Set the enabled state for the proxy settings button.
- AdvancedOptions.SetupProxySettingsSection = function(disabled, label) {
- if (!cr.isChromeOS) {
- $('proxiesConfigureButton').disabled = disabled;
- $('proxiesLabel').textContent = label;
- }
- };
- // Set the checked state for the sslCheckRevocation checkbox.
- AdvancedOptions.SetCheckRevocationCheckboxState = function(
- checked, disabled) {
- $('sslCheckRevocation').checked = checked;
- $('sslCheckRevocation').disabled = disabled;
- };
- // Set the checked state for the backgroundModeCheckbox element.
- AdvancedOptions.SetBackgroundModeCheckboxState = function(checked) {
- $('backgroundModeCheckbox').checked = checked;
- };
- // Set the Cloud Print proxy UI to enabled, disabled, or processing.
- AdvancedOptions.SetupCloudPrintProxySection = function(
- disabled, label, allowed) {
- if (!cr.isChromeOS) {
- $('cloudPrintProxyLabel').textContent = label;
- if (disabled || !allowed) {
- $('cloudPrintProxySetupButton').textContent =
- localStrings.getString('cloudPrintProxyDisabledButton');
- $('cloudPrintProxyManageButton').style.display = 'none';
- } else {
- $('cloudPrintProxySetupButton').textContent =
- localStrings.getString('cloudPrintProxyEnabledButton');
- $('cloudPrintProxyManageButton').style.display = 'inline';
- }
- $('cloudPrintProxySetupButton').disabled = !allowed;
- }
- };
- AdvancedOptions.RemoveCloudPrintProxySection = function() {
- if (!cr.isChromeOS) {
- var proxySectionElm = $('cloud-print-proxy-section');
- if (proxySectionElm)
- proxySectionElm.parentNode.removeChild(proxySectionElm);
- }
- };
- // Export
- return {
- AdvancedOptions: AdvancedOptions
- };
- });
- // Copyright (c) 2010 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 OptionsPage = options.OptionsPage;
- /**
- * AlertOverlay class
- * Encapsulated handling of a generic alert.
- * @class
- */
- function AlertOverlay() {
- OptionsPage.call(this, 'alertOverlay', '', 'alertOverlay');
- }
- cr.addSingletonGetter(AlertOverlay);
- AlertOverlay.prototype = {
- // Inherit AlertOverlay from OptionsPage.
- __proto__: OptionsPage.prototype,
- /**
- * Whether the page can be shown. Used to make sure the page is only
- * shown via AlertOverlay.Show(), and not via the address bar.
- * @private
- */
- canShow_: false,
- /**
- * Initialize the page.
- */
- initializePage: function() {
- // Call base class implementation to start preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- var self = this;
- $('alertOverlayOk').onclick = function(event) {
- self.handleOK_();
- };
- $('alertOverlayCancel').onclick = function(event) {
- self.handleCancel_();
- };
- },
- /**
- * Handle the 'ok' button. Clear the overlay and call the ok callback if
- * available.
- * @private
- */
- handleOK_: function() {
- OptionsPage.closeOverlay();
- if (this.okCallback != undefined) {
- this.okCallback.call();
- }
- },
- /**
- * Handle the 'cancel' button. Clear the overlay and call the cancel
- * callback if available.
- * @private
- */
- handleCancel_: function() {
- OptionsPage.closeOverlay();
- if (this.cancelCallback != undefined) {
- this.cancelCallback.call();
- }
- },
- /**
- * The page is getting hidden. Don't let it be shown again.
- */
- willHidePage: function() {
- canShow_ = false;
- },
- /** @inheritDoc */
- canShowPage: function() {
- return this.canShow_;
- },
- };
- /**
- * Show an alert overlay with the given message, button titles, and
- * callbacks.
- * @param {string} title The alert title to display to the user.
- * @param {string} message The alert message to display to the user.
- * @param {string} okTitle The title of the OK button. If undefined or empty,
- * no button is shown.
- * @param {string} cancelTitle The title of the cancel button. If undefined or
- * empty, no button is shown.
- * @param {function} okCallback A function to be called when the user presses
- * the ok button. The alert window will be closed automatically. Can be
- * undefined.
- * @param {function} cancelCallback A function to be called when the user
- * presses the cancel button. The alert window will be closed
- * automatically. Can be undefined.
- */
- AlertOverlay.show = function(
- title, message, okTitle, cancelTitle, okCallback, cancelCallback) {
- if (title != undefined) {
- $('alertOverlayTitle').textContent = title;
- $('alertOverlayTitle').style.display = 'block';
- } else {
- $('alertOverlayTitle').style.display = 'none';
- }
- if (message != undefined) {
- $('alertOverlayMessage').textContent = message;
- $('alertOverlayMessage').style.display = 'block';
- } else {
- $('alertOverlayMessage').style.display = 'none';
- }
- if (okTitle != undefined && okTitle != '') {
- $('alertOverlayOk').textContent = okTitle;
- $('alertOverlayOk').style.display = 'block';
- } else {
- $('alertOverlayOk').style.display = 'none';
- }
- if (cancelTitle != undefined && cancelTitle != '') {
- $('alertOverlayCancel').textContent = cancelTitle;
- $('alertOverlayCancel').style.display = 'inline';
- } else {
- $('alertOverlayCancel').style.display = 'none';
- }
- var alertOverlay = AlertOverlay.getInstance();
- alertOverlay.okCallback = okCallback;
- alertOverlay.cancelCallback = cancelCallback;
- alertOverlay.canShow_ = true;
- // Intentionally don't show the URL in the location bar as we don't want
- // people trying to navigate here by hand.
- OptionsPage.showPageByName('alertOverlay', false);
- }
- // Export
- return {
- AlertOverlay: AlertOverlay
- };
- });
- // 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() {
- const ArrayDataModel = cr.ui.ArrayDataModel;
- const List = cr.ui.List;
- const ListItem = cr.ui.ListItem;
- /**
- * Creates a new autocomplete list item.
- * @param {Object} pageInfo The page this item represents.
- * @constructor
- * @extends {cr.ui.ListItem}
- */
- function AutocompleteListItem(pageInfo) {
- var el = cr.doc.createElement('div');
- el.pageInfo_ = pageInfo;
- AutocompleteListItem.decorate(el);
- return el;
- }
- /**
- * Decorates an element as an autocomplete list item.
- * @param {!HTMLElement} el The element to decorate.
- */
- AutocompleteListItem.decorate = function(el) {
- el.__proto__ = AutocompleteListItem.prototype;
- el.decorate();
- };
- AutocompleteListItem.prototype = {
- __proto__: ListItem.prototype,
- /** @inheritDoc */
- decorate: function() {
- ListItem.prototype.decorate.call(this);
- var title = this.pageInfo_['title'];
- var url = this.pageInfo_['displayURL'];
- var titleEl = this.ownerDocument.createElement('span');
- titleEl.className = 'title';
- titleEl.textContent = title || url;
- this.appendChild(titleEl);
- if (title && title.length > 0 && url != title) {
- var separatorEl = this.ownerDocument.createTextNode(' - ');
- this.appendChild(separatorEl);
- var urlEl = this.ownerDocument.createElement('span');
- urlEl.className = 'url';
- urlEl.textContent = url;
- this.appendChild(urlEl);
- }
- },
- };
- /**
- * Creates a new autocomplete list popup.
- * @constructor
- * @extends {cr.ui.List}
- */
- var AutocompleteList = cr.ui.define('list');
- AutocompleteList.prototype = {
- __proto__: List.prototype,
- /**
- * The text field the autocomplete popup is currently attached to, if any.
- * @type {HTMLElement}
- * @private
- */
- targetInput_: null,
- /**
- * Keydown event listener to attach to a text field.
- * @type {Function}
- * @private
- */
- textFieldKeyHandler_: null,
- /**
- * Input event listener to attach to a text field.
- * @type {Function}
- * @private
- */
- textFieldInputHandler_: null,
- /**
- * A function to call when new suggestions are needed.
- * @type {Function}
- * @private
- */
- suggestionUpdateRequestCallback_: null,
- /** @inheritDoc */
- decorate: function() {
- List.prototype.decorate.call(this);
- this.classList.add('autocomplete-suggestions');
- this.selectionModel = new cr.ui.ListSingleSelectionModel;
- this.textFieldKeyHandler_ = this.handleAutocompleteKeydown_.bind(this);
- var self = this;
- this.textFieldInputHandler_ = function(e) {
- if (self.suggestionUpdateRequestCallback_)
- self.suggestionUpdateRequestCallback_(self.targetInput_.value);
- };
- this.addEventListener('change', function(e) {
- var input = self.targetInput;
- if (!input || !self.selectedItem)
- return;
- input.value = self.selectedItem['url'];
- // Programatically change the value won't trigger a change event, but
- // clients are likely to want to know when changes happen, so fire one.
- var changeEvent = document.createEvent('Event');
- changeEvent.initEvent('change', true, true);
- input.dispatchEvent(changeEvent);
- });
- // Start hidden; adding suggestions will unhide.
- this.hidden = true;
- },
- /** @inheritDoc */
- createItem: function(pageInfo) {
- return new AutocompleteListItem(pageInfo);
- },
- /**
- * The suggestions to show.
- * @type {Array}
- */
- set suggestions(suggestions) {
- this.dataModel = new ArrayDataModel(suggestions);
- this.hidden = !this.targetInput_ || suggestions.length == 0;
- },
- /**
- * A function to call when the attached input field's contents change.
- * The function should take one string argument, which will be the text
- * to autocomplete from.
- * @type {Function}
- */
- set suggestionUpdateRequestCallback(callback) {
- this.suggestionUpdateRequestCallback_ = callback;
- },
- /**
- * Attaches the popup to the given input element. Requires
- * that the input be wrapped in a block-level container of the same width.
- * @param {HTMLElement} input The input element to attach to.
- */
- attachToInput: function(input) {
- if (this.targetInput_ == input)
- return;
- this.detach();
- this.targetInput_ = input;
- this.style.width = input.getBoundingClientRect().width + 'px';
- this.hidden = false; // Necessary for positionPopupAroundElement to work.
- cr.ui.positionPopupAroundElement(input, this, cr.ui.AnchorType.BELOW)
- // Start hidden; when the data model gets results the list will show.
- this.hidden = true;
- input.addEventListener('keydown', this.textFieldKeyHandler_, true);
- input.addEventListener('input', this.textFieldInputHandler_);
- },
- /**
- * Detaches the autocomplete popup from its current input element, if any.
- */
- detach: function() {
- var input = this.targetInput_
- if (!input)
- return;
- input.removeEventListener('keydown', this.textFieldKeyHandler_);
- input.removeEventListener('input', this.textFieldInputHandler_);
- this.targetInput_ = null;
- this.suggestions = [];
- },
- /**
- * Makes sure that the suggestion list matches the width of the input it is.
- * attached to. Should be called any time the input is resized.
- */
- syncWidthToInput: function() {
- var input = this.targetInput_
- if (input)
- this.style.width = input.getBoundingClientRect().width + 'px';
- },
- /**
- * The text field the autocomplete popup is currently attached to, if any.
- * @return {HTMLElement}
- */
- get targetInput() {
- return this.targetInput_;
- },
- /**
- * Handles input field key events that should be interpreted as autocomplete
- * commands.
- * @param {Event} event The keydown event.
- * @private
- */
- handleAutocompleteKeydown_: function(event) {
- if (this.hidden)
- return;
- var handled = false;
- switch (event.keyIdentifier) {
- case 'U+001B': // Esc
- this.suggestions = [];
- handled = true;
- break;
- case 'Enter':
- var hadSelection = this.selectedItem != null;
- this.suggestions = [];
- // Only count the event as handled if a selection is being commited.
- handled = hadSelection;
- break;
- case 'Up':
- case 'Down':
- this.dispatchEvent(event);
- handled = true;
- break;
- }
- // Don't let arrow keys affect the text field, or bubble up to, e.g.,
- // an enclosing list item.
- if (handled) {
- event.preventDefault();
- event.stopPropagation();
- }
- },
- };
- return {
- AutocompleteList: AutocompleteList
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- const ArrayDataModel = cr.ui.ArrayDataModel;
- // The GUID of the loaded address.
- var guid;
- /**
- * AutofillEditAddressOverlay class
- * Encapsulated handling of the 'Add Page' overlay page.
- * @class
- */
- function AutofillEditAddressOverlay() {
- OptionsPage.call(this, 'autofillEditAddress',
- templateData.autofillEditAddressTitle,
- 'autofill-edit-address-overlay');
- }
- cr.addSingletonGetter(AutofillEditAddressOverlay);
- AutofillEditAddressOverlay.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * Initializes the page.
- */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- this.createMultiValueLists_();
- var self = this;
- $('autofill-edit-address-cancel-button').onclick = function(event) {
- self.dismissOverlay_();
- }
- $('autofill-edit-address-apply-button').onclick = function(event) {
- self.saveAddress_();
- self.dismissOverlay_();
- }
- self.guid = '';
- self.populateCountryList_();
- self.clearInputFields_();
- self.connectInputEvents_();
- },
- /**
- * Creates, decorates and initializes the multi-value lists for full name,
- * phone, and email.
- * @private
- */
- createMultiValueLists_: function() {
- var list = $('full-name-list');
- options.autofillOptions.AutofillNameValuesList.decorate(list);
- list.autoExpands = true;
- list = $('phone-list');
- options.autofillOptions.AutofillPhoneValuesList.decorate(list);
- list.autoExpands = true;
- list = $('email-list');
- options.autofillOptions.AutofillValuesList.decorate(list);
- list.autoExpands = true;
- },
- /**
- * Updates the data model for the list named |listName| with the values from
- * |entries|.
- * @param {String} listName The id of the list.
- * @param {Array} entries The list of items to be added to the list.
- */
- setMultiValueList_: function(listName, entries) {
- // Add data entries.
- var list = $(listName);
- list.dataModel = new ArrayDataModel(entries);
- // Add special entry for adding new values.
- list.dataModel.splice(list.dataModel.length, 0, null);
- // Update the status of the 'OK' button.
- this.inputFieldChanged_();
- var self = this;
- list.dataModel.addEventListener(
- 'splice', function(event) { self.inputFieldChanged_(); });
- list.dataModel.addEventListener(
- 'change', function(event) { self.inputFieldChanged_(); });
- },
- /**
- * Updates the data model for the name list with the values from |entries|.
- * @param {Array} names The list of names to be added to the list.
- */
- setNameList_: function(names) {
- // Add the given |names| as backing data for the list.
- var list = $('full-name-list');
- list.dataModel = new ArrayDataModel(names);
- // Add special entry for adding new values.
- list.dataModel.splice(list.dataModel.length, 0, null);
- var self = this;
- list.dataModel.addEventListener(
- 'splice', function(event) { self.inputFieldChanged_(); });
- list.dataModel.addEventListener(
- 'change', function(event) { self.inputFieldChanged_(); });
- },
- /**
- * Clears any uncommitted input, resets the stored GUID and dismisses the
- * overlay.
- * @private
- */
- dismissOverlay_: function() {
- this.clearInputFields_();
- this.guid = '';
- OptionsPage.closeOverlay();
- },
- /**
- * Aggregates the values in the input fields into an array and sends the
- * array to the Autofill handler.
- * @private
- */
- saveAddress_: function() {
- var address = new Array();
- address[0] = this.guid;
- var list = $('full-name-list');
- address[1] = list.dataModel.slice(0, list.dataModel.length - 1);
- address[2] = $('company-name').value;
- address[3] = $('addr-line-1').value;
- address[4] = $('addr-line-2').value;
- address[5] = $('city').value;
- address[6] = $('state').value;
- address[7] = $('postal-code').value;
- address[8] = $('country').value;
- list = $('phone-list');
- address[9] = list.dataModel.slice(0, list.dataModel.length - 1);
- list = $('email-list');
- address[10] = list.dataModel.slice(0, list.dataModel.length - 1);
- chrome.send('setAddress', address);
- },
- /**
- * Connects each input field to the inputFieldChanged_() method that enables
- * or disables the 'Ok' button based on whether all the fields are empty or
- * not.
- * @private
- */
- connectInputEvents_: function() {
- var self = this;
- $('company-name').oninput = $('addr-line-1').oninput =
- $('addr-line-2').oninput = $('city').oninput = $('state').oninput =
- $('postal-code').oninput = function(event) {
- self.inputFieldChanged_();
- }
- $('country').onchange = function(event) {
- self.countryChanged_();
- }
- },
- /**
- * Checks the values of each of the input fields and disables the 'Ok'
- * button if all of the fields are empty.
- * @private
- */
- inputFieldChanged_: function() {
- // Length of lists are tested for <= 1 due to the "add" placeholder item
- // in the list.
- var disabled =
- $('full-name-list').items.length <= 1 &&
- !$('company-name').value &&
- !$('addr-line-1').value && !$('addr-line-2').value &&
- !$('city').value && !$('state').value && !$('postal-code').value &&
- !$('country').value && $('phone-list').items.length <= 1 &&
- $('email-list').items.length <= 1;
- $('autofill-edit-address-apply-button').disabled = disabled;
- },
- /**
- * Updates the postal code and state field labels appropriately for the
- * selected country.
- * @private
- */
- countryChanged_: function() {
- var countryCode = $('country').value;
- if (!countryCode)
- countryCode = templateData.defaultCountryCode;
- var details = templateData.autofillCountryData[countryCode];
- var postal = $('postal-code-label');
- postal.textContent = details['postalCodeLabel'];
- $('state-label').textContent = details['stateLabel'];
- // Also update the 'Ok' button as needed.
- this.inputFieldChanged_();
- },
- /**
- * Populates the country <select> list.
- * @private
- */
- populateCountryList_: function() {
- var countryData = templateData.autofillCountryData;
- var defaultCountryCode = templateData.defaultCountryCode;
- // Build an array of the country names and their corresponding country
- // codes, so that we can sort and insert them in order.
- var countries = [];
- for (var countryCode in countryData) {
- var country = {
- countryCode: countryCode,
- name: countryData[countryCode]['name']
- };
- countries.push(country);
- }
- // Sort the countries in alphabetical order by name.
- countries = countries.sort(function(a, b) {
- return a.name < b.name ? -1 : 1;
- });
- // Insert the empty and default countries at the beginning of the array.
- var emptyCountry = {
- countryCode: '',
- name: ''
- };
- var defaultCountry = {
- countryCode: defaultCountryCode,
- name: countryData[defaultCountryCode]['name']
- };
- var separator = {
- countryCode: '',
- name: '---',
- disabled: true
- }
- countries.unshift(emptyCountry, defaultCountry, separator);
- // Add the countries to the country <select> list.
- var countryList = $('country');
- for (var i = 0; i < countries.length; i++) {
- var country = new Option(countries[i].name, countries[i].countryCode);
- country.disabled = countries[i].disabled;
- countryList.appendChild(country)
- }
- },
- /**
- * Clears the value of each input field.
- * @private
- */
- clearInputFields_: function() {
- this.setNameList_([]);
- $('company-name').value = '';
- $('addr-line-1').value = '';
- $('addr-line-2').value = '';
- $('city').value = '';
- $('state').value = '';
- $('postal-code').value = '';
- $('country').value = '';
- this.setMultiValueList_('phone-list', []);
- this.setMultiValueList_('email-list', []);
- this.countryChanged_();
- },
- /**
- * Loads the address data from |address|, sets the input fields based on
- * this data and stores the GUID of the address.
- * @private
- */
- loadAddress_: function(address) {
- this.setInputFields_(address);
- this.inputFieldChanged_();
- this.guid = address['guid'];
- },
- /**
- * Sets the value of each input field according to |address|
- * @private
- */
- setInputFields_: function(address) {
- this.setNameList_(address['fullName']);
- $('company-name').value = address['companyName'];
- $('addr-line-1').value = address['addrLine1'];
- $('addr-line-2').value = address['addrLine2'];
- $('city').value = address['city'];
- $('state').value = address['state'];
- $('postal-code').value = address['postalCode'];
- $('country').value = address['country'];
- this.setMultiValueList_('phone-list', address['phone']);
- this.setMultiValueList_('email-list', address['email']);
- this.countryChanged_();
- },
- };
- AutofillEditAddressOverlay.clearInputFields = function() {
- AutofillEditAddressOverlay.getInstance().clearInputFields_();
- };
- AutofillEditAddressOverlay.loadAddress = function(address) {
- AutofillEditAddressOverlay.getInstance().loadAddress_(address);
- };
- AutofillEditAddressOverlay.setTitle = function(title) {
- $('autofill-address-title').textContent = title;
- };
- AutofillEditAddressOverlay.setValidatedPhoneNumbers = function(numbers) {
- AutofillEditAddressOverlay.getInstance().setMultiValueList_('phone-list',
- numbers);
- };
- // Export
- return {
- AutofillEditAddressOverlay: AutofillEditAddressOverlay
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- // The GUID of the loaded credit card.
- var guid_;
- /**
- * AutofillEditCreditCardOverlay class
- * Encapsulated handling of the 'Add Page' overlay page.
- * @class
- */
- function AutofillEditCreditCardOverlay() {
- OptionsPage.call(this, 'autofillEditCreditCard',
- templateData.autofillEditCreditCardTitle,
- 'autofill-edit-credit-card-overlay');
- }
- cr.addSingletonGetter(AutofillEditCreditCardOverlay);
- AutofillEditCreditCardOverlay.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * Initializes the page.
- */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- var self = this;
- $('autofill-edit-credit-card-cancel-button').onclick = function(event) {
- self.dismissOverlay_();
- }
- $('autofill-edit-credit-card-apply-button').onclick = function(event) {
- self.saveCreditCard_();
- self.dismissOverlay_();
- }
- self.guid_ = '';
- self.hasEditedNumber_ = false;
- self.clearInputFields_();
- self.connectInputEvents_();
- self.setDefaultSelectOptions_();
- },
- /**
- * Clears any uncommitted input, and dismisses the overlay.
- * @private
- */
- dismissOverlay_: function() {
- this.clearInputFields_();
- this.guid_ = '';
- this.hasEditedNumber_ = false;
- OptionsPage.closeOverlay();
- },
- /**
- * Aggregates the values in the input fields into an array and sends the
- * array to the Autofill handler.
- * @private
- */
- saveCreditCard_: function() {
- var creditCard = new Array(5);
- creditCard[0] = this.guid_;
- creditCard[1] = $('name-on-card').value;
- creditCard[2] = $('credit-card-number').value;
- creditCard[3] = $('expiration-month').value;
- creditCard[4] = $('expiration-year').value;
- chrome.send('setCreditCard', creditCard);
- },
- /**
- * Connects each input field to the inputFieldChanged_() method that enables
- * or disables the 'Ok' button based on whether all the fields are empty or
- * not.
- * @private
- */
- connectInputEvents_: function() {
- var ccNumber = $('credit-card-number');
- $('name-on-card').oninput = ccNumber.oninput =
- $('expiration-month').onchange = $('expiration-year').onchange =
- this.inputFieldChanged_.bind(this);
- },
- /**
- * Checks the values of each of the input fields and disables the 'Ok'
- * button if all of the fields are empty.
- * @param {Event} opt_event Optional data for the 'input' event.
- * @private
- */
- inputFieldChanged_: function(opt_event) {
- var disabled = !$('name-on-card').value && !$('credit-card-number').value;
- $('autofill-edit-credit-card-apply-button').disabled = disabled;
- },
- /**
- * Sets the default values of the options in the 'Expiration date' select
- * controls.
- * @private
- */
- setDefaultSelectOptions_: function() {
- // Set the 'Expiration month' default options.
- var expirationMonth = $('expiration-month');
- expirationMonth.options.length = 0;
- for (var i = 1; i <= 12; ++i) {
- var text;
- if (i < 10)
- text = '0' + i;
- else
- text = i;
- var option = document.createElement('option');
- option.text = text;
- option.value = text;
- expirationMonth.add(option, null);
- }
- // Set the 'Expiration year' default options.
- var expirationYear = $('expiration-year');
- expirationYear.options.length = 0;
- var date = new Date();
- var year = parseInt(date.getFullYear());
- for (var i = 0; i < 10; ++i) {
- var text = year + i;
- var option = document.createElement('option');
- option.text = text;
- option.value = text;
- expirationYear.add(option, null);
- }
- },
- /**
- * Clears the value of each input field.
- * @private
- */
- clearInputFields_: function() {
- $('name-on-card').value = '';
- $('credit-card-number').value = '';
- $('expiration-month').selectedIndex = 0;
- $('expiration-year').selectedIndex = 0;
- // Reset the enabled status of the 'Ok' button.
- this.inputFieldChanged_();
- },
- /**
- * Sets the value of each input field according to |creditCard|
- * @private
- */
- setInputFields_: function(creditCard) {
- $('name-on-card').value = creditCard['nameOnCard'];
- $('credit-card-number').value = creditCard['creditCardNumber'];
- // The options for the year select control may be out-dated at this point,
- // e.g. the user opened the options page before midnight on New Year's Eve
- // and then loaded a credit card profile to edit in the new year, so
- // reload the select options just to be safe.
- this.setDefaultSelectOptions_();
- var idx = parseInt(creditCard['expirationMonth'], 10);
- $('expiration-month').selectedIndex = idx - 1;
- expYear = creditCard['expirationYear'];
- var date = new Date();
- var year = parseInt(date.getFullYear());
- for (var i = 0; i < 10; ++i) {
- var text = year + i;
- if (expYear == String(text))
- $('expiration-year').selectedIndex = i;
- }
- },
- /**
- * Loads the credit card data from |creditCard|, sets the input fields based
- * on this data and stores the GUID of the credit card.
- * @private
- */
- loadCreditCard_: function(creditCard) {
- this.setInputFields_(creditCard);
- this.inputFieldChanged_();
- this.guid_ = creditCard['guid'];
- },
- };
- AutofillEditCreditCardOverlay.clearInputFields = function(title) {
- AutofillEditCreditCardOverlay.getInstance().clearInputFields_();
- };
- AutofillEditCreditCardOverlay.loadCreditCard = function(creditCard) {
- AutofillEditCreditCardOverlay.getInstance().loadCreditCard_(creditCard);
- };
- AutofillEditCreditCardOverlay.setTitle = function(title) {
- $('autofill-credit-card-title').textContent = title;
- };
- // Export
- return {
- AutofillEditCreditCardOverlay: AutofillEditCreditCardOverlay
- };
- });
- // 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.autofillOptions', function() {
- const DeletableItem = options.DeletableItem;
- const DeletableItemList = options.DeletableItemList;
- const InlineEditableItem = options.InlineEditableItem;
- const InlineEditableItemList = options.InlineEditableItemList;
- function AutofillEditProfileButton(guid, edit) {
- var editButtonEl = document.createElement('button');
- editButtonEl.className = 'raw-button custom-appearance';
- editButtonEl.textContent =
- templateData.autofillEditProfileButton;
- editButtonEl.onclick = function(e) { edit(guid); };
- // Don't select the row when clicking the button.
- editButtonEl.onmousedown = function(e) {
- e.stopPropagation();
- };
- return editButtonEl;
- }
- /**
- * Creates a new address list item.
- * @param {Array} entry An array of the form [guid, label].
- * @constructor
- * @extends {options.DeletableItem}
- */
- function AddressListItem(entry) {
- var el = cr.doc.createElement('div');
- el.guid = entry[0];
- el.label = entry[1];
- el.__proto__ = AddressListItem.prototype;
- el.decorate();
- return el;
- }
- AddressListItem.prototype = {
- __proto__: DeletableItem.prototype,
- /** @inheritDoc */
- decorate: function() {
- DeletableItem.prototype.decorate.call(this);
- // The stored label.
- var label = this.ownerDocument.createElement('div');
- label.className = 'autofill-list-item';
- label.textContent = this.label;
- this.contentElement.appendChild(label);
- // The 'Edit' button.
- var editButtonEl = new AutofillEditProfileButton(
- this.guid,
- AutofillOptions.loadAddressEditor);
- this.contentElement.appendChild(editButtonEl);
- },
- };
- /**
- * Creates a new credit card list item.
- * @param {Array} entry An array of the form [guid, label, icon].
- * @constructor
- * @extends {options.DeletableItem}
- */
- function CreditCardListItem(entry) {
- var el = cr.doc.createElement('div');
- el.guid = entry[0];
- el.label = entry[1];
- el.icon = entry[2];
- el.description = entry[3];
- el.__proto__ = CreditCardListItem.prototype;
- el.decorate();
- return el;
- }
- CreditCardListItem.prototype = {
- __proto__: DeletableItem.prototype,
- /** @inheritDoc */
- decorate: function() {
- DeletableItem.prototype.decorate.call(this);
- // The stored label.
- var label = this.ownerDocument.createElement('div');
- label.className = 'autofill-list-item';
- label.textContent = this.label;
- this.contentElement.appendChild(label);
- // The credit card icon.
- var icon = this.ownerDocument.createElement('image');
- icon.src = this.icon;
- icon.alt = this.description;
- this.contentElement.appendChild(icon);
- // The 'Edit' button.
- var editButtonEl = new AutofillEditProfileButton(
- this.guid,
- AutofillOptions.loadCreditCardEditor);
- this.contentElement.appendChild(editButtonEl);
- },
- };
- /**
- * Creates a new value list item.
- * @param {AutofillValuesList} list The parent list of this item.
- * @param {String} entry A string value.
- * @constructor
- * @extends {options.InlineEditableItem}
- */
- function ValuesListItem(list, entry) {
- var el = cr.doc.createElement('div');
- el.list = list;
- el.value = entry ? entry : '';
- el.__proto__ = ValuesListItem.prototype;
- el.decorate();
- return el;
- }
- ValuesListItem.prototype = {
- __proto__: InlineEditableItem.prototype,
- /** @inheritDoc */
- decorate: function() {
- InlineEditableItem.prototype.decorate.call(this);
- // Note: This must be set prior to calling |createEditableTextCell|.
- this.isPlaceholder = !this.value;
- // The stored value.
- var cell = this.createEditableTextCell(this.value);
- this.contentElement.appendChild(cell);
- this.input = cell.querySelector('input');
- if (this.isPlaceholder) {
- this.input.placeholder = this.list.getAttribute('placeholder');
- this.deletable = false;
- }
- this.addEventListener('commitedit', this.onEditCommitted_);
- },
- /**
- * @return This item's value.
- * @protected
- */
- value_: function() {
- return this.input.value;
- },
- /**
- * @param {Object} value The value to test.
- * @return true if the given value is non-empty.
- * @protected
- */
- valueIsNonEmpty_: function(value) {
- return !!value;
- },
- /**
- * @return true if value1 is logically equal to value2.
- */
- valuesAreEqual_: function(value1, value2) {
- return value1 === value2;
- },
- /**
- * Clears the item's value.
- * @protected
- */
- clearValue_: function() {
- this.input.value = '';
- },
- /**
- * Called when committing an edit.
- * If this is an "Add ..." item, committing a non-empty value adds that
- * value to the end of the values list, but also leaves this "Add ..." item
- * in place.
- * @param {Event} e The end event.
- * @private
- */
- onEditCommitted_: function(e) {
- var value = this.value_();
- var i = this.list.items.indexOf(this);
- if (i < this.list.dataModel.length &&
- this.valuesAreEqual_(value, this.list.dataModel.item(i))) {
- return;
- }
- var entries = this.list.dataModel.slice();
- if (this.valueIsNonEmpty_(value) &&
- !entries.some(this.valuesAreEqual_.bind(this, value))) {
- // Update with new value.
- if (this.isPlaceholder) {
- // It is important that updateIndex is done before validateAndSave.
- // Otherwise we can not be sure about AddRow index.
- this.list.dataModel.updateIndex(i);
- this.list.validateAndSave(i, 0, value);
- } else {
- this.list.validateAndSave(i, 1, value);
- }
- } else {
- // Reject empty values and duplicates.
- if (!this.isPlaceholder)
- this.list.dataModel.splice(i, 1);
- else
- this.clearValue_();
- }
- },
- };
- /**
- * Creates a new name value list item.
- * @param {AutofillNameValuesList} list The parent list of this item.
- * @param {array} entry An array of [first, middle, last] names.
- * @constructor
- * @extends {options.ValuesListItem}
- */
- function NameListItem(list, entry) {
- var el = cr.doc.createElement('div');
- el.list = list;
- el.first = entry ? entry[0] : '';
- el.middle = entry ? entry[1] : '';
- el.last = entry ? entry[2] : '';
- el.__proto__ = NameListItem.prototype;
- el.decorate();
- return el;
- }
- NameListItem.prototype = {
- __proto__: ValuesListItem.prototype,
- /** @inheritDoc */
- decorate: function() {
- InlineEditableItem.prototype.decorate.call(this);
- // Note: This must be set prior to calling |createEditableTextCell|.
- this.isPlaceholder = !this.first && !this.middle && !this.last;
- // The stored value.
- // For the simulated static "input element" to display correctly, the
- // value must not be empty. We use a space to force the UI to render
- // correctly when the value is logically empty.
- var cell = this.createEditableTextCell(this.first);
- this.contentElement.appendChild(cell);
- this.firstNameInput = cell.querySelector('input');
- cell = this.createEditableTextCell(this.middle);
- this.contentElement.appendChild(cell);
- this.middleNameInput = cell.querySelector('input');
- cell = this.createEditableTextCell(this.last);
- this.contentElement.appendChild(cell);
- this.lastNameInput = cell.querySelector('input');
- if (this.isPlaceholder) {
- this.firstNameInput.placeholder =
- templateData.autofillAddFirstNamePlaceholder;
- this.middleNameInput.placeholder =
- templateData.autofillAddMiddleNamePlaceholder;
- this.lastNameInput.placeholder =
- templateData.autofillAddLastNamePlaceholder;
- this.deletable = false;
- }
- this.addEventListener('commitedit', this.onEditCommitted_);
- },
- /** @inheritDoc */
- value_: function() {
- return [ this.firstNameInput.value,
- this.middleNameInput.value,
- this.lastNameInput.value ];
- },
- /** @inheritDoc */
- valueIsNonEmpty_: function(value) {
- return value[0] || value[1] || value[2];
- },
- /** @inheritDoc */
- valuesAreEqual_: function(value1, value2) {
- // First, check for null values.
- if (!value1 || !value2)
- return value1 == value2;
- return value1[0] === value2[0] &&
- value1[1] === value2[1] &&
- value1[2] === value2[2];
- },
- /** @inheritDoc */
- clearValue_: function() {
- this.firstNameInput.value = '';
- this.middleNameInput.value = '';
- this.lastNameInput.value = '';
- },
- };
- /**
- * Base class for shared implementation between address and credit card lists.
- * @constructor
- * @extends {options.DeletableItemList}
- */
- var AutofillProfileList = cr.ui.define('list');
- AutofillProfileList.prototype = {
- __proto__: DeletableItemList.prototype,
- decorate: function() {
- DeletableItemList.prototype.decorate.call(this);
- this.addEventListener('blur', this.onBlur_);
- },
- /**
- * When the list loses focus, unselect all items in the list.
- * @private
- */
- onBlur_: function() {
- this.selectionModel.unselectAll();
- },
- };
- /**
- * Create a new address list.
- * @constructor
- * @extends {options.AutofillProfileList}
- */
- var AutofillAddressList = cr.ui.define('list');
- AutofillAddressList.prototype = {
- __proto__: AutofillProfileList.prototype,
- decorate: function() {
- AutofillProfileList.prototype.decorate.call(this);
- },
- /** @inheritDoc */
- activateItemAtIndex: function(index) {
- AutofillOptions.loadAddressEditor(this.dataModel.item(index)[0]);
- },
- /** @inheritDoc */
- createItem: function(entry) {
- return new AddressListItem(entry);
- },
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- AutofillOptions.removeAddress(this.dataModel.item(index)[0]);
- },
- };
- /**
- * Create a new credit card list.
- * @constructor
- * @extends {options.DeletableItemList}
- */
- var AutofillCreditCardList = cr.ui.define('list');
- AutofillCreditCardList.prototype = {
- __proto__: AutofillProfileList.prototype,
- decorate: function() {
- AutofillProfileList.prototype.decorate.call(this);
- },
- /** @inheritDoc */
- activateItemAtIndex: function(index) {
- AutofillOptions.loadCreditCardEditor(this.dataModel.item(index)[0]);
- },
- /** @inheritDoc */
- createItem: function(entry) {
- return new CreditCardListItem(entry);
- },
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- AutofillOptions.removeCreditCard(this.dataModel.item(index)[0]);
- },
- };
- /**
- * Create a new value list.
- * @constructor
- * @extends {options.InlineEditableItemList}
- */
- var AutofillValuesList = cr.ui.define('list');
- AutofillValuesList.prototype = {
- __proto__: InlineEditableItemList.prototype,
- /** @inheritDoc */
- createItem: function(entry) {
- return new ValuesListItem(this, entry);
- },
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- this.dataModel.splice(index, 1);
- },
- /** @inheritDoc */
- shouldFocusPlaceholder: function() {
- return false;
- },
- /**
- * Called when the list hierarchy as a whole loses or gains focus.
- * If the list was focused in response to a mouse click, call into the
- * superclass's implementation. If the list was focused in response to a
- * keyboard navigation, focus the first item.
- * If the list loses focus, unselect all the elements.
- * @param {Event} e The change event.
- * @private
- */
- handleListFocusChange_: function(e) {
- // We check to see whether there is a selected item as a proxy for
- // distinguishing between mouse- and keyboard-originated focus events.
- var selectedItem = this.selectedItem;
- if (selectedItem)
- InlineEditableItemList.prototype.handleListFocusChange_.call(this, e);
- if (!e.newValue) {
- // When the list loses focus, unselect all the elements.
- this.selectionModel.unselectAll();
- } else {
- // When the list gains focus, select the first item if nothing else is
- // selected.
- var firstItem = this.getListItemByIndex(0);
- if (!selectedItem && firstItem && e.newValue)
- firstItem.handleFocus_();
- }
- },
- /**
- * Called when a new list item should be validated; subclasses are
- * responsible for implementing if validation is required.
- * @param {number} index The index of the item that was inserted or changed.
- * @param {number} remove The number items to remove.
- * @param {string} value The value of the item to insert.
- */
- validateAndSave: function(index, remove, value) {
- this.dataModel.splice(index, remove, value);
- },
- };
- /**
- * Create a new value list for phone number validation.
- * @constructor
- * @extends {options.AutofillValuesList}
- */
- var AutofillNameValuesList = cr.ui.define('list');
- AutofillNameValuesList.prototype = {
- __proto__: AutofillValuesList.prototype,
- /** @inheritDoc */
- createItem: function(entry) {
- return new NameListItem(this, entry);
- },
- };
- /**
- * Create a new value list for phone number validation.
- * @constructor
- * @extends {options.AutofillValuesList}
- */
- var AutofillPhoneValuesList = cr.ui.define('list');
- AutofillPhoneValuesList.prototype = {
- __proto__: AutofillValuesList.prototype,
- /** @inheritDoc */
- validateAndSave: function(index, remove, value) {
- var numbers = this.dataModel.slice(0, this.dataModel.length - 1);
- numbers.splice(index, remove, value);
- var info = new Array();
- info[0] = index;
- info[1] = numbers;
- info[2] = $('country').value;
- chrome.send('validatePhoneNumbers', info);
- },
- };
- return {
- AddressListItem: AddressListItem,
- CreditCardListItem: CreditCardListItem,
- ValuesListItem: ValuesListItem,
- NameListItem: NameListItem,
- AutofillAddressList: AutofillAddressList,
- AutofillCreditCardList: AutofillCreditCardList,
- AutofillValuesList: AutofillValuesList,
- AutofillNameValuesList: AutofillNameValuesList,
- AutofillPhoneValuesList: AutofillPhoneValuesList,
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- const ArrayDataModel = cr.ui.ArrayDataModel;
- /////////////////////////////////////////////////////////////////////////////
- // AutofillOptions class:
- /**
- * Encapsulated handling of Autofill options page.
- * @constructor
- */
- function AutofillOptions() {
- OptionsPage.call(this,
- 'autofill',
- templateData.autofillOptionsPageTabTitle,
- 'autofill-options');
- }
- cr.addSingletonGetter(AutofillOptions);
- AutofillOptions.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * The address list.
- * @type {DeletableItemList}
- * @private
- */
- addressList_: null,
- /**
- * The credit card list.
- * @type {DeletableItemList}
- * @private
- */
- creditCardList_: null,
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- this.createAddressList_();
- this.createCreditCardList_();
- var self = this;
- $('autofill-add-address').onclick = function(event) {
- self.showAddAddressOverlay_();
- };
- $('autofill-add-creditcard').onclick = function(event) {
- self.showAddCreditCardOverlay_();
- };
- // TODO(jhawkins): What happens when Autofill is disabled whilst on the
- // Autofill options page?
- },
- /**
- * Creates, decorates and initializes the address list.
- * @private
- */
- createAddressList_: function() {
- this.addressList_ = $('address-list');
- options.autofillOptions.AutofillAddressList.decorate(this.addressList_);
- this.addressList_.autoExpands = true;
- },
- /**
- * Creates, decorates and initializes the credit card list.
- * @private
- */
- createCreditCardList_: function() {
- this.creditCardList_ = $('creditcard-list');
- options.autofillOptions.AutofillCreditCardList.decorate(
- this.creditCardList_);
- this.creditCardList_.autoExpands = true;
- },
- /**
- * Shows the 'Add address' overlay, specifically by loading the
- * 'Edit address' overlay, emptying the input fields and modifying the
- * overlay title.
- * @private
- */
- showAddAddressOverlay_: function() {
- var title = localStrings.getString('addAddressTitle');
- AutofillEditAddressOverlay.setTitle(title);
- AutofillEditAddressOverlay.clearInputFields();
- OptionsPage.navigateToPage('autofillEditAddress');
- },
- /**
- * Shows the 'Add credit card' overlay, specifically by loading the
- * 'Edit credit card' overlay, emptying the input fields and modifying the
- * overlay title.
- * @private
- */
- showAddCreditCardOverlay_: function() {
- var title = localStrings.getString('addCreditCardTitle');
- AutofillEditCreditCardOverlay.setTitle(title);
- AutofillEditCreditCardOverlay.clearInputFields();
- OptionsPage.navigateToPage('autofillEditCreditCard');
- },
- /**
- * Updates the data model for the address list with the values from
- * |entries|.
- * @param {Array} entries The list of addresses.
- */
- setAddressList_: function(entries) {
- this.addressList_.dataModel = new ArrayDataModel(entries);
- },
- /**
- * Updates the data model for the credit card list with the values from
- * |entries|.
- * @param {Array} entries The list of credit cards.
- */
- setCreditCardList_: function(entries) {
- this.creditCardList_.dataModel = new ArrayDataModel(entries);
- },
- /**
- * Removes the Autofill address represented by |guid|.
- * @param {String} guid The GUID of the address to remove.
- * @private
- */
- removeAddress_: function(guid) {
- chrome.send('removeAddress', [guid]);
- },
- /**
- * Removes the Autofill credit card represented by |guid|.
- * @param {String} guid The GUID of the credit card to remove.
- * @private
- */
- removeCreditCard_: function(guid) {
- chrome.send('removeCreditCard', [guid]);
- },
- /**
- * Requests profile data for the address represented by |guid| from the
- * PersonalDataManager. Once the data is loaded, the AutofillOptionsHandler
- * calls showEditAddressOverlay().
- * @param {String} guid The GUID of the address to edit.
- * @private
- */
- loadAddressEditor_: function(guid) {
- chrome.send('loadAddressEditor', [guid]);
- },
- /**
- * Requests profile data for the credit card represented by |guid| from the
- * PersonalDataManager. Once the data is loaded, the AutofillOptionsHandler
- * calls showEditCreditCardOverlay().
- * @param {String} guid The GUID of the credit card to edit.
- * @private
- */
- loadCreditCardEditor_: function(guid) {
- chrome.send('loadCreditCardEditor', [guid]);
- },
- /**
- * Shows the 'Edit address' overlay, using the data in |address| to fill the
- * input fields. |address| is a list with one item, an associative array
- * that contains the address data.
- * @private
- */
- showEditAddressOverlay_: function(address) {
- var title = localStrings.getString('editAddressTitle');
- AutofillEditAddressOverlay.setTitle(title);
- AutofillEditAddressOverlay.loadAddress(address);
- OptionsPage.navigateToPage('autofillEditAddress');
- },
- /**
- * Shows the 'Edit credit card' overlay, using the data in |credit_card| to
- * fill the input fields. |address| is a list with one item, an associative
- * array that contains the credit card data.
- * @private
- */
- showEditCreditCardOverlay_: function(creditCard) {
- var title = localStrings.getString('editCreditCardTitle');
- AutofillEditCreditCardOverlay.setTitle(title);
- AutofillEditCreditCardOverlay.loadCreditCard(creditCard);
- OptionsPage.navigateToPage('autofillEditCreditCard');
- },
- };
- AutofillOptions.setAddressList = function(entries) {
- AutofillOptions.getInstance().setAddressList_(entries);
- };
- AutofillOptions.setCreditCardList = function(entries) {
- AutofillOptions.getInstance().setCreditCardList_(entries);
- };
- AutofillOptions.removeAddress = function(guid) {
- AutofillOptions.getInstance().removeAddress_(guid);
- };
- AutofillOptions.removeCreditCard = function(guid) {
- AutofillOptions.getInstance().removeCreditCard_(guid);
- };
- AutofillOptions.loadAddressEditor = function(guid) {
- AutofillOptions.getInstance().loadAddressEditor_(guid);
- };
- AutofillOptions.loadCreditCardEditor = function(guid) {
- AutofillOptions.getInstance().loadCreditCardEditor_(guid);
- };
- AutofillOptions.editAddress = function(address) {
- AutofillOptions.getInstance().showEditAddressOverlay_(address);
- };
- AutofillOptions.editCreditCard = function(creditCard) {
- AutofillOptions.getInstance().showEditCreditCardOverlay_(creditCard);
- };
- // Export
- return {
- AutofillOptions: AutofillOptions
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- const ArrayDataModel = cr.ui.ArrayDataModel;
- //
- // BrowserOptions class
- // Encapsulated handling of browser options page.
- //
- function BrowserOptions() {
- OptionsPage.call(this, 'browser',
- templateData.browserPageTabTitle,
- 'browserPage');
- }
- cr.addSingletonGetter(BrowserOptions);
- BrowserOptions.prototype = {
- // Inherit BrowserOptions from OptionsPage.
- __proto__: options.OptionsPage.prototype,
- startup_pages_pref_: {
- 'name': 'session.urls_to_restore_on_startup',
- 'disabled': false
- },
- /**
- * At autocomplete list that can be attached to a text field during editing.
- * @type {HTMLElement}
- * @private
- */
- autocompleteList_: null,
- // The cached value of the instant.confirm_dialog_shown preference.
- instantConfirmDialogShown_: false,
- /**
- * Initialize BrowserOptions page.
- */
- initializePage: function() {
- // Call base class implementation to start preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- // Wire up controls.
- $('startupUseCurrentButton').onclick = function(event) {
- chrome.send('setStartupPagesToCurrentPages');
- };
- $('defaultSearchManageEnginesButton').onclick = function(event) {
- OptionsPage.navigateToPage('searchEngines');
- chrome.send('coreOptionsUserMetricsAction',
- ['Options_ManageSearchEngines']);
- };
- $('defaultSearchEngine').onchange = this.setDefaultSearchEngine_;
- var self = this;
- $('instantEnabledCheckbox').customChangeHandler = function(event) {
- if (this.checked) {
- if (self.instantConfirmDialogShown_)
- chrome.send('enableInstant');
- else
- OptionsPage.navigateToPage('instantConfirm');
- } else {
- chrome.send('disableInstant');
- }
- return true;
- };
- $('instantFieldTrialCheckbox').addEventListener('change',
- function(event) {
- this.checked = true;
- chrome.send('disableInstant');
- });
- Preferences.getInstance().addEventListener('instant.confirm_dialog_shown',
- this.onInstantConfirmDialogShownChanged_.bind(this));
- Preferences.getInstance().addEventListener('instant.enabled',
- this.onInstantEnabledChanged_.bind(this));
- Preferences.getInstance().addEventListener(
- $('homepageUseNTPButton').pref,
- this.onHomepageUseNTPChanged_);
- var homepageField = $('homepageURL');
- homepageField.addEventListener('focus', function(event) {
- self.autocompleteList_.attachToInput(homepageField);
- });
- homepageField.addEventListener('blur', function(event) {
- self.autocompleteList_.detach();
- });
- homepageField.addEventListener('keydown', function(event) {
- // Remove focus when the user hits enter since people expect feedback
- // indicating that they are done editing.
- if (event.keyIdentifier == 'Enter')
- homepageField.blur();
- });
- // Text fields may change widths when the window changes size, so make
- // sure the suggestion list stays in sync.
- window.addEventListener('resize', function() {
- self.autocompleteList_.syncWidthToInput();
- });
- // Ensure that changes are committed when closing the page.
- window.addEventListener('unload', function() {
- if (document.activeElement == homepageField)
- homepageField.blur();
- });
- if (!cr.isChromeOS) {
- $('defaultBrowserUseAsDefaultButton').onclick = function(event) {
- chrome.send('becomeDefaultBrowser');
- };
- }
- var startupPagesList = $('startupPagesList');
- options.browser_options.StartupPageList.decorate(startupPagesList);
- startupPagesList.autoExpands = true;
- // Check if we are in the guest mode.
- if (cr.commandLine.options['--bwsi']) {
- // Hide the startup section.
- $('startupSection').hidden = true;
- } else {
- // Initialize control enabled states.
- Preferences.getInstance().addEventListener('session.restore_on_startup',
- this.updateCustomStartupPageControlStates_.bind(this));
- Preferences.getInstance().addEventListener(
- this.startup_pages_pref_.name,
- this.handleStartupPageListChange_.bind(this));
- this.updateCustomStartupPageControlStates_();
- }
- var suggestionList = new options.AutocompleteList();
- suggestionList.autoExpands = true;
- suggestionList.suggestionUpdateRequestCallback =
- this.requestAutocompleteSuggestions_.bind(this);
- $('main-content').appendChild(suggestionList);
- this.autocompleteList_ = suggestionList;
- startupPagesList.autocompleteList = suggestionList;
- },
- /**
- * Called when the value of the instant.confirm_dialog_shown preference
- * changes. Cache this value.
- * @param {Event} event Change event.
- * @private
- */
- onInstantConfirmDialogShownChanged_: function(event) {
- this.instantConfirmDialogShown_ = event.value['value'];
- },
- /**
- * Called when the value of the instant.enabled preference changes. Request
- * the state of the Instant field trial experiment.
- * @param {Event} event Change event.
- * @private
- */
- onInstantEnabledChanged_: function(event) {
- chrome.send('getInstantFieldTrialStatus');
- },
- /**
- * Called to set the Instant field trial status.
- * @param {boolean} enabled If true, the experiment is enabled.
- * @private
- */
- setInstantFieldTrialStatus_: function(enabled) {
- $('instantEnabledCheckbox').hidden = enabled;
- $('instantFieldTrialCheckbox').hidden = !enabled;
- $('instantLabel').htmlFor = enabled ? 'instantFieldTrialCheckbox'
- : 'instantEnabledCheckbox';
- },
- /**
- * Called when the value of the homepage-use-NTP pref changes.
- * Updates the disabled state of the homepage text field.
- * Notice that the text field can be disabled for other reasons too
- * (it can be managed by policy, for instance).
- * @param {Event} event Change event.
- * @private
- */
- onHomepageUseNTPChanged_: function(event) {
- var homepageField = $('homepageURL');
- var homepageUseURLButton = $('homepageUseURLButton');
- homepageField.setDisabled('radioNotSelected',
- !homepageUseURLButton.checked);
- },
- /**
- * Update the Default Browsers section based on the current state.
- * @param {string} statusString Description of the current default state.
- * @param {boolean} isDefault Whether or not the browser is currently
- * default.
- * @param {boolean} canBeDefault Whether or not the browser can be default.
- * @private
- */
- updateDefaultBrowserState_: function(statusString, isDefault,
- canBeDefault) {
- var label = $('defaultBrowserState');
- label.textContent = statusString;
- $('defaultBrowserUseAsDefaultButton').disabled = !canBeDefault ||
- isDefault;
- },
- /**
- * Clears the search engine popup.
- * @private
- */
- clearSearchEngines_: function() {
- $('defaultSearchEngine').textContent = '';
- },
- /**
- * Updates the search engine popup with the given entries.
- * @param {Array} engines List of available search engines.
- * @param {number} defaultValue The value of the current default engine.
- * @param {boolean} defaultManaged Whether the default search provider is
- * managed. If true, the default search provider can't be changed.
- */
- updateSearchEngines_: function(engines, defaultValue, defaultManaged) {
- this.clearSearchEngines_();
- engineSelect = $('defaultSearchEngine');
- engineSelect.disabled = defaultManaged;
- engineCount = engines.length;
- var defaultIndex = -1;
- for (var i = 0; i < engineCount; i++) {
- var engine = engines[i];
- var option = new Option(engine['name'], engine['index']);
- if (defaultValue == option.value)
- defaultIndex = i;
- engineSelect.appendChild(option);
- }
- if (defaultIndex >= 0)
- engineSelect.selectedIndex = defaultIndex;
- },
- /**
- * Returns true if the custom startup page control block should
- * be enabled.
- * @returns {boolean} Whether the startup page controls should be
- * enabled.
- */
- shouldEnableCustomStartupPageControls: function(pages) {
- return $('startupShowPagesButton').checked &&
- !this.startup_pages_pref_.disabled;
- },
- /**
- * Updates the startup pages list with the given entries.
- * @param {Array} pages List of startup pages.
- * @private
- */
- updateStartupPages_: function(pages) {
- var model = new ArrayDataModel(pages);
- // Add a "new page" row.
- model.push({
- 'modelIndex': '-1'
- });
- $('startupPagesList').dataModel = model;
- },
- /**
- * Sets the enabled state of the custom startup page list controls
- * based on the current startup radio button selection.
- * @private
- */
- updateCustomStartupPageControlStates_: function() {
- var disable = !this.shouldEnableCustomStartupPageControls();
- var startupPagesList = $('startupPagesList');
- startupPagesList.disabled = disable;
- // Explicitly set disabled state for input text elements.
- var inputs = startupPagesList.querySelectorAll("input[type='text']");
- for (var i = 0; i < inputs.length; i++)
- inputs[i].disabled = disable;
- $('startupUseCurrentButton').disabled = disable;
- },
- /**
- * Handle change events of the preference
- * 'session.urls_to_restore_on_startup'.
- * @param {event} preference changed event.
- * @private
- */
- handleStartupPageListChange_: function(event) {
- this.startup_pages_pref_.disabled = event.value['disabled'];
- this.updateCustomStartupPageControlStates_();
- },
- /**
- * Set the default search engine based on the popup selection.
- */
- setDefaultSearchEngine_: function() {
- var engineSelect = $('defaultSearchEngine');
- var selectedIndex = engineSelect.selectedIndex;
- if (selectedIndex >= 0) {
- var selection = engineSelect.options[selectedIndex];
- chrome.send('setDefaultSearchEngine', [String(selection.value)]);
- }
- },
- /**
- * Sends an asynchronous request for new autocompletion suggestions for the
- * the given query. When new suggestions are available, the C++ handler will
- * call updateAutocompleteSuggestions_.
- * @param {string} query List of autocomplete suggestions.
- * @private
- */
- requestAutocompleteSuggestions_: function(query) {
- chrome.send('requestAutocompleteSuggestions', [query]);
- },
- /**
- * Updates the autocomplete suggestion list with the given entries.
- * @param {Array} pages List of autocomplete suggestions.
- * @private
- */
- updateAutocompleteSuggestions_: function(suggestions) {
- var list = this.autocompleteList_;
- // If the trigger for this update was a value being selected from the
- // current list, do nothing.
- if (list.targetInput && list.selectedItem &&
- list.selectedItem['url'] == list.targetInput.value)
- return;
- list.suggestions = suggestions;
- },
- };
- BrowserOptions.updateDefaultBrowserState = function(statusString, isDefault,
- canBeDefault) {
- if (!cr.isChromeOS) {
- BrowserOptions.getInstance().updateDefaultBrowserState_(statusString,
- isDefault,
- canBeDefault);
- }
- };
- BrowserOptions.updateSearchEngines = function(engines, defaultValue,
- defaultManaged) {
- BrowserOptions.getInstance().updateSearchEngines_(engines, defaultValue,
- defaultManaged);
- };
- BrowserOptions.updateStartupPages = function(pages) {
- BrowserOptions.getInstance().updateStartupPages_(pages);
- };
- BrowserOptions.updateAutocompleteSuggestions = function(suggestions) {
- BrowserOptions.getInstance().updateAutocompleteSuggestions_(suggestions);
- };
- BrowserOptions.setInstantFieldTrialStatus = function(enabled) {
- BrowserOptions.getInstance().setInstantFieldTrialStatus_(enabled);
- };
- // Export
- return {
- BrowserOptions: BrowserOptions
- };
- });
- // 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.browser_options', function() {
- const AutocompleteList = options.AutocompleteList;
- const InlineEditableItem = options.InlineEditableItem;
- const InlineEditableItemList = options.InlineEditableItemList;
- /**
- * Creates a new startup page list item.
- * @param {Object} pageInfo The page this item represents.
- * @constructor
- * @extends {cr.ui.ListItem}
- */
- function StartupPageListItem(pageInfo) {
- var el = cr.doc.createElement('div');
- el.pageInfo_ = pageInfo;
- StartupPageListItem.decorate(el);
- return el;
- }
- /**
- * Decorates an element as a startup page list item.
- * @param {!HTMLElement} el The element to decorate.
- */
- StartupPageListItem.decorate = function(el) {
- el.__proto__ = StartupPageListItem.prototype;
- el.decorate();
- };
- StartupPageListItem.prototype = {
- __proto__: InlineEditableItem.prototype,
- /**
- * Input field for editing the page url.
- * @type {HTMLElement}
- * @private
- */
- urlField_: null,
- /** @inheritDoc */
- decorate: function() {
- InlineEditableItem.prototype.decorate.call(this);
- var pageInfo = this.pageInfo_;
- if (pageInfo['modelIndex'] == '-1') {
- this.isPlaceholder = true;
- pageInfo['title'] = localStrings.getString('startupAddLabel');
- pageInfo['url'] = '';
- }
- var titleEl = this.ownerDocument.createElement('div');
- titleEl.className = 'title';
- titleEl.classList.add('favicon-cell');
- titleEl.classList.add('weakrtl');
- titleEl.textContent = pageInfo['title'];
- if (!this.isPlaceholder) {
- titleEl.style.backgroundImage = url('chrome://favicon/' +
- pageInfo['url']);
- titleEl.title = pageInfo['tooltip'];
- }
- this.contentElement.appendChild(titleEl);
- var urlEl = this.createEditableTextCell(pageInfo['url']);
- urlEl.className = 'url';
- urlEl.classList.add('weakrtl');
- this.contentElement.appendChild(urlEl);
- var urlField = urlEl.querySelector('input')
- urlField.required = true;
- urlField.className = 'weakrtl';
- this.urlField_ = urlField;
- this.addEventListener('commitedit', this.onEditCommitted_);
- var self = this;
- urlField.addEventListener('focus', function(event) {
- self.parentNode.autocompleteList.attachToInput(urlField);
- });
- urlField.addEventListener('blur', function(event) {
- self.parentNode.autocompleteList.detach();
- });
- if (!this.isPlaceholder)
- this.draggable = true;
- },
- /** @inheritDoc */
- get currentInputIsValid() {
- return this.urlField_.validity.valid;
- },
- /** @inheritDoc */
- get hasBeenEdited() {
- return this.urlField_.value != this.pageInfo_['url'];
- },
- /**
- * Called when committing an edit; updates the model.
- * @param {Event} e The end event.
- * @private
- */
- onEditCommitted_: function(e) {
- var url = this.urlField_.value;
- if (this.isPlaceholder)
- chrome.send('addStartupPage', [url]);
- else
- chrome.send('editStartupPage', [this.pageInfo_['modelIndex'], url]);
- },
- };
- var StartupPageList = cr.ui.define('list');
- StartupPageList.prototype = {
- __proto__: InlineEditableItemList.prototype,
- /**
- * An autocomplete suggestion list for URL editing.
- * @type {AutocompleteList}
- */
- autocompleteList: null,
- /**
- * The drop position information: "below" or "above".
- */
- dropPos: null,
- /** @inheritDoc */
- decorate: function() {
- InlineEditableItemList.prototype.decorate.call(this);
- // Listen to drag and drop events.
- this.addEventListener('dragstart', this.handleDragStart_.bind(this));
- this.addEventListener('dragenter', this.handleDragEnter_.bind(this));
- this.addEventListener('dragover', this.handleDragOver_.bind(this));
- this.addEventListener('drop', this.handleDrop_.bind(this));
- this.addEventListener('dragleave', this.handleDragLeave_.bind(this));
- this.addEventListener('dragend', this.handleDragEnd_.bind(this));
- },
- /** @inheritDoc */
- createItem: function(pageInfo) {
- var item = new StartupPageListItem(pageInfo);
- item.urlField_.disabled = this.disabled;
- return item;
- },
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- chrome.send('removeStartupPages', [String(index)]);
- },
- /*
- * Computes the target item of drop event.
- * @param {Event} e The drop or dragover event.
- * @private
- */
- getTargetFromDropEvent_ : function(e) {
- var target = e.target;
- // e.target may be an inner element of the list item
- while (target != null && !(target instanceof StartupPageListItem)) {
- target = target.parentNode;
- }
- return target;
- },
- /*
- * Handles the dragstart event.
- * @param {Event} e The dragstart event.
- * @private
- */
- handleDragStart_: function(e) {
- // Prevent dragging if the list is disabled.
- if (this.disabled) {
- e.preventDefault();
- return false;
- }
- var target = e.target;
- // StartupPageListItem should be the only draggable element type in the
- // page but let's make sure.
- if (target instanceof StartupPageListItem) {
- this.draggedItem = target;
- this.draggedItem.editable = false;
- e.dataTransfer.effectAllowed = 'move';
- // We need to put some kind of data in the drag or it will be
- // ignored. Use the URL in case the user drags to a text field or the
- // desktop.
- e.dataTransfer.setData('text/plain', target.urlField_.value);
- }
- },
- /*
- * Handles the dragenter event.
- * @param {Event} e The dragenter event.
- * @private
- */
- handleDragEnter_: function(e) {
- e.preventDefault();
- },
- /*
- * Handles the dragover event.
- * @param {Event} e The dragover event.
- * @private
- */
- handleDragOver_: function(e) {
- var dropTarget = this.getTargetFromDropEvent_(e);
- // Determines whether the drop target is to accept the drop.
- // The drop is only successful on another StartupPageListItem.
- if (!(dropTarget instanceof StartupPageListItem) ||
- dropTarget == this.draggedItem || dropTarget.isPlaceholder) {
- this.hideDropMarker_();
- return;
- }
- // Compute the drop postion. Should we move the dragged item to
- // below or above the drop target?
- var rect = dropTarget.getBoundingClientRect();
- var dy = e.clientY - rect.top;
- var yRatio = dy / rect.height;
- var dropPos = yRatio <= .5 ? 'above' : 'below';
- this.dropPos = dropPos;
- this.showDropMarker_(dropTarget, dropPos);
- e.preventDefault();
- },
- /*
- * Handles the drop event.
- * @param {Event} e The drop event.
- * @private
- */
- handleDrop_: function(e) {
- var dropTarget = this.getTargetFromDropEvent_(e);
- this.hideDropMarker_();
- // Insert the selection at the new position.
- var newIndex = this.dataModel.indexOf(dropTarget.pageInfo_);
- if (this.dropPos == 'below')
- newIndex += 1;
- var selected = this.selectionModel.selectedIndexes;
- var stringized_selected = [];
- for (var j = 0; j < selected.length; j++)
- stringized_selected.push(String(selected[j]));
- chrome.send('dragDropStartupPage',
- [String(newIndex), stringized_selected] );
- },
- /*
- * Handles the dragleave event.
- * @param {Event} e The dragleave event
- * @private
- */
- handleDragLeave_: function(e) {
- this.hideDropMarker_();
- },
- /**
- * Handles the dragend event.
- * @param {Event} e The dragend event
- * @private
- */
- handleDragEnd_: function(e) {
- this.draggedItem.editable = true;
- this.draggedItem.updateEditState();
- },
- /*
- * Shows and positions the marker to indicate the drop target.
- * @param {HTMLElement} target The current target list item of drop
- * @param {string} pos 'below' or 'above'
- * @private
- */
- showDropMarker_ : function(target, pos) {
- window.clearTimeout(this.hideDropMarkerTimer_);
- var marker = $('startupPagesListDropmarker');
- var rect = target.getBoundingClientRect();
- var markerHeight = 6;
- if (pos == 'above') {
- marker.style.top = (rect.top - markerHeight/2) + 'px';
- } else {
- marker.style.top = (rect.bottom - markerHeight/2) + 'px';
- }
- marker.style.width = rect.width + 'px';
- marker.style.left = rect.left + 'px';
- marker.style.display = 'block';
- },
- /*
- * Hides the drop marker.
- * @private
- */
- hideDropMarker_ : function() {
- // Hide the marker in a timeout to reduce flickering as we move between
- // valid drop targets.
- window.clearTimeout(this.hideDropMarkerTimer_);
- this.hideDropMarkerTimer_ = window.setTimeout(function() {
- $('startupPagesListDropmarker').style.display = '';
- }, 100);
- },
- };
- return {
- StartupPageList: StartupPageList
- };
- });
- // 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 OptionsPage = options.OptionsPage;
- /**
- * ClearBrowserDataOverlay class
- * Encapsulated handling of the 'Clear Browser Data' overlay page.
- * @class
- */
- function ClearBrowserDataOverlay() {
- OptionsPage.call(this, 'clearBrowserData',
- templateData.clearBrowserDataOverlayTabTitle,
- 'clearBrowserDataOverlay');
- }
- cr.addSingletonGetter(ClearBrowserDataOverlay);
- ClearBrowserDataOverlay.prototype = {
- // Inherit ClearBrowserDataOverlay from OptionsPage.
- __proto__: OptionsPage.prototype,
- /**
- * Initialize the page.
- */
- initializePage: function() {
- // Call base class implementation to starts preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- var f = this.updateCommitButtonState_.bind(this);
- var types = ['browser.clear_data.browsing_history',
- 'browser.clear_data.download_history',
- 'browser.clear_data.cache',
- 'browser.clear_data.cookies',
- 'browser.clear_data.passwords',
- 'browser.clear_data.form_data'];
- types.forEach(function(type) {
- Preferences.getInstance().addEventListener(type, f);
- });
- var checkboxes = document.querySelectorAll(
- '#cbdContentArea input[type=checkbox]');
- for (var i = 0; i < checkboxes.length; i++) {
- checkboxes[i].onclick = f;
- }
- this.updateCommitButtonState_();
- $('clearBrowserDataDismiss').onclick = function(event) {
- ClearBrowserDataOverlay.dismiss();
- };
- $('clearBrowserDataCommit').onclick = function(event) {
- chrome.send('performClearBrowserData');
- };
- },
- // Set the enabled state of the commit button.
- updateCommitButtonState_: function() {
- var checkboxes = document.querySelectorAll(
- '#cbdContentArea input[type=checkbox]');
- var isChecked = false;
- for (var i = 0; i < checkboxes.length; i++) {
- if (checkboxes[i].checked) {
- isChecked = true;
- break;
- }
- }
- $('clearBrowserDataCommit').disabled = !isChecked;
- },
- };
- //
- // Chrome callbacks
- //
- ClearBrowserDataOverlay.setClearingState = function(state) {
- $('deleteBrowsingHistoryCheckbox').disabled = state;
- $('deleteDownloadHistoryCheckbox').disabled = state;
- $('deleteCacheCheckbox').disabled = state;
- $('deleteCookiesCheckbox').disabled = state;
- $('deletePasswordsCheckbox').disabled = state;
- $('deleteFormDataCheckbox').disabled = state;
- $('clearBrowserDataTimePeriod').disabled = state;
- $('cbdThrobber').style.visibility = state ? 'visible' : 'hidden';
- if (state)
- $('clearBrowserDataCommit').disabled = true;
- else
- ClearBrowserDataOverlay.getInstance().updateCommitButtonState_();
- };
- ClearBrowserDataOverlay.doneClearing = function() {
- // The delay gives the user some feedback that the clearing
- // actually worked. Otherwise the dialog just vanishes instantly in most
- // cases.
- window.setTimeout(function() {
- ClearBrowserDataOverlay.dismiss();
- }, 200);
- };
- ClearBrowserDataOverlay.dismiss = function() {
- OptionsPage.closeOverlay();
- this.setClearingState(false);
- };
- // Export
- return {
- ClearBrowserDataOverlay: ClearBrowserDataOverlay
- };
- });
- // 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 OptionsPage = options.OptionsPage;
- //////////////////////////////////////////////////////////////////////////////
- // ContentSettings class:
- /**
- * Encapsulated handling of content settings page.
- * @constructor
- */
- function ContentSettings() {
- this.activeNavTab = null;
- OptionsPage.call(this, 'content', templateData.contentSettingsPageTabTitle,
- 'content-settings-page');
- }
- cr.addSingletonGetter(ContentSettings);
- ContentSettings.prototype = {
- __proto__: OptionsPage.prototype,
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- chrome.send('getContentFilterSettings');
- var exceptionsButtons =
- this.pageDiv.querySelectorAll('.exceptions-list-button');
- for (var i = 0; i < exceptionsButtons.length; i++) {
- exceptionsButtons[i].onclick = function(event) {
- var page = ContentSettingsExceptionsArea.getInstance();
- page.showList(
- event.target.getAttribute('contentType'));
- OptionsPage.navigateToPage('contentExceptions');
- // Add on the proper hash for the content type, and store that in the
- // history so back/forward and tab restore works.
- var hash = event.target.getAttribute('contentType');
- window.history.replaceState({pageName: page.name}, page.title,
- '/' + page.name + "#" + hash);
- };
- }
- var manageHandlersButton = $('manage-handlers-button');
- if (manageHandlersButton) {
- manageHandlersButton.onclick = function(event) {
- OptionsPage.navigateToPage('handlers');
- };
- }
- var manageIntentsButton = $('manage-intents-button');
- if (manageIntentsButton) {
- manageIntentsButton.onclick = function(event) {
- OptionsPage.navigateToPage('intents');
- };
- }
- // Cookies filter page ---------------------------------------------------
- $('show-cookies-button').onclick = function(event) {
- chrome.send('coreOptionsUserMetricsAction', ['Options_ShowCookies']);
- OptionsPage.navigateToPage('cookies');
- };
- if (!templateData.enable_web_intents && $('intent-section'))
- $('intent-section').hidden = true;
- },
- };
- ContentSettings.updateHandlersEnabledRadios = function(enabled) {
- var selector = '#content-settings-page input[type=radio][value=' +
- (enabled ? 'allow' : 'block') + '].handler-radio';
- document.querySelector(selector).checked = true;
- };
- /**
- * Sets the values for all the content settings radios.
- * @param {Object} dict A mapping from radio groups to the checked value for
- * that group.
- */
- ContentSettings.setContentFilterSettingsValue = function(dict) {
- for (var group in dict) {
- document.querySelector('input[type=radio][name=' + group + '][value=' +
- dict[group]['value'] + ']').checked = true;
- var radios = document.querySelectorAll('input[type=radio][name=' +
- group + ']');
- var managedBy = dict[group]['managedBy'];
- for (var i = 0, len = radios.length; i < len; i++) {
- radios[i].disabled = (managedBy != 'default');
- radios[i].controlledBy = managedBy;
- }
- }
- OptionsPage.updateManagedBannerVisibility();
- };
- /**
- * Initializes an exceptions list.
- * @param {string} type The content type that we are setting exceptions for.
- * @param {Array} list An array of pairs, where the first element of each pair
- * is the filter string, and the second is the setting (allow/block).
- */
- ContentSettings.setExceptions = function(type, list) {
- var exceptionsList =
- document.querySelector('div[contentType=' + type + ']' +
- ' list[mode=normal]');
- exceptionsList.setExceptions(list);
- };
- ContentSettings.setHandlers = function(list) {
- $('handlers-list').setHandlers(list);
- };
- ContentSettings.setIgnoredHandlers = function(list) {
- $('ignored-handlers-list').setHandlers(list);
- };
- ContentSettings.setOTRExceptions = function(type, list) {
- var exceptionsList =
- document.querySelector('div[contentType=' + type + ']' +
- ' list[mode=otr]');
- exceptionsList.parentNode.hidden = false;
- exceptionsList.setExceptions(list);
- };
- /**
- * The browser's response to a request to check the validity of a given URL
- * pattern.
- * @param {string} type The content type.
- * @param {string} mode The browser mode.
- * @param {string} pattern The pattern.
- * @param {bool} valid Whether said pattern is valid in the context of
- * a content exception setting.
- */
- ContentSettings.patternValidityCheckComplete =
- function(type, mode, pattern, valid) {
- var exceptionsList =
- document.querySelector('div[contentType=' + type + '] ' +
- 'list[mode=' + mode + ']');
- exceptionsList.patternValidityCheckComplete(pattern, valid);
- };
- // Export
- return {
- ContentSettings: ContentSettings
- };
- });
- // 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.contentSettings', function() {
- const InlineEditableItemList = options.InlineEditableItemList;
- const InlineEditableItem = options.InlineEditableItem;
- const ArrayDataModel = cr.ui.ArrayDataModel;
- /**
- * Creates a new exceptions list item.
- * @param {string} contentType The type of the list.
- * @param {string} mode The browser mode, 'otr' or 'normal'.
- * @param {boolean} enableAskOption Whether to show an 'ask every time'
- * option in the select.
- * @param {Object} exception A dictionary that contains the data of the
- * exception.
- * @constructor
- * @extends {options.InlineEditableItem}
- */
- function ExceptionsListItem(contentType, mode, enableAskOption, exception) {
- var el = cr.doc.createElement('div');
- el.mode = mode;
- el.contentType = contentType;
- el.enableAskOption = enableAskOption;
- el.dataItem = exception;
- el.__proto__ = ExceptionsListItem.prototype;
- el.decorate();
- return el;
- }
- ExceptionsListItem.prototype = {
- __proto__: InlineEditableItem.prototype,
- /**
- * Called when an element is decorated as a list item.
- */
- decorate: function() {
- InlineEditableItem.prototype.decorate.call(this);
- this.isPlaceholder = !this.pattern;
- var patternCell = this.createEditableTextCell(this.pattern);
- patternCell.className = 'exception-pattern';
- patternCell.classList.add('weakrtl');
- this.contentElement.appendChild(patternCell);
- if (this.pattern)
- this.patternLabel = patternCell.querySelector('.static-text');
- var input = patternCell.querySelector('input');
- // TODO(stuartmorgan): Create an createEditableSelectCell abstracting
- // this code.
- // Setting label for display mode. |pattern| will be null for the 'add new
- // exception' row.
- if (this.pattern) {
- var settingLabel = cr.doc.createElement('span');
- settingLabel.textContent = this.settingForDisplay();
- settingLabel.className = 'exception-setting';
- settingLabel.setAttribute('displaymode', 'static');
- this.contentElement.appendChild(settingLabel);
- this.settingLabel = settingLabel;
- }
- // Setting select element for edit mode.
- var select = cr.doc.createElement('select');
- var optionAllow = cr.doc.createElement('option');
- optionAllow.textContent = templateData.allowException;
- optionAllow.value = 'allow';
- select.appendChild(optionAllow);
- if (this.enableAskOption) {
- var optionAsk = cr.doc.createElement('option');
- optionAsk.textContent = templateData.askException;
- optionAsk.value = 'ask';
- select.appendChild(optionAsk);
- }
- if (this.contentType == 'cookies') {
- var optionSession = cr.doc.createElement('option');
- optionSession.textContent = templateData.sessionException;
- optionSession.value = 'session';
- select.appendChild(optionSession);
- }
- if (this.contentType != 'fullscreen') {
- var optionBlock = cr.doc.createElement('option');
- optionBlock.textContent = templateData.blockException;
- optionBlock.value = 'block';
- select.appendChild(optionBlock);
- }
- this.contentElement.appendChild(select);
- select.className = 'exception-setting';
- if (this.pattern)
- select.setAttribute('displaymode', 'edit');
- // Used to track whether the URL pattern in the input is valid.
- // This will be true if the browser process has informed us that the
- // current text in the input is valid. Changing the text resets this to
- // false, and getting a response from the browser sets it back to true.
- // It starts off as false for empty string (new exceptions) or true for
- // already-existing exceptions (which we assume are valid).
- this.inputValidityKnown = this.pattern;
- // This one tracks the actual validity of the pattern in the input. This
- // starts off as true so as not to annoy the user when he adds a new and
- // empty input.
- this.inputIsValid = true;
- this.input = input;
- this.select = select;
- this.updateEditables();
- // Editing notifications and geolocation is disabled for now.
- if (this.contentType == 'notifications' ||
- this.contentType == 'location') {
- this.editable = false;
- }
- // If the source of the content setting exception is not the user
- // preference, then the content settings exception is managed and the user
- // can't edit it.
- if (this.dataItem.source &&
- this.dataItem.source != 'preference') {
- this.setAttribute('managedby', this.dataItem.source);
- this.deletable = false;
- this.editable = false;
- }
- var listItem = this;
- // Handle events on the editable nodes.
- input.oninput = function(event) {
- listItem.inputValidityKnown = false;
- chrome.send('checkExceptionPatternValidity',
- [listItem.contentType, listItem.mode, input.value]);
- };
- // Listen for edit events.
- this.addEventListener('canceledit', this.onEditCancelled_);
- this.addEventListener('commitedit', this.onEditCommitted_);
- },
- /**
- * The pattern (e.g., a URL) for the exception.
- * @type {string}
- */
- get pattern() {
- return this.dataItem['displayPattern'];
- },
- set pattern(pattern) {
- this.dataItem['displayPattern'] = pattern;
- },
- /**
- * The setting (allow/block) for the exception.
- * @type {string}
- */
- get setting() {
- return this.dataItem['setting'];
- },
- set setting(setting) {
- this.dataItem['setting'] = setting;
- },
- /**
- * Gets a human-readable setting string.
- * @type {string}
- */
- settingForDisplay: function() {
- var setting = this.setting;
- if (setting == 'allow')
- return templateData.allowException;
- else if (setting == 'block')
- return templateData.blockException;
- else if (setting == 'ask')
- return templateData.askException;
- else if (setting == 'session')
- return templateData.sessionException;
- },
- /**
- * Update this list item to reflect whether the input is a valid pattern.
- * @param {boolean} valid Whether said pattern is valid in the context of
- * a content exception setting.
- */
- setPatternValid: function(valid) {
- if (valid || !this.input.value)
- this.input.setCustomValidity('');
- else
- this.input.setCustomValidity(' ');
- this.inputIsValid = valid;
- this.inputValidityKnown = true;
- },
- /**
- * Set the <input> to its original contents. Used when the user quits
- * editing.
- */
- resetInput: function() {
- this.input.value = this.pattern;
- },
- /**
- * Copy the data model values to the editable nodes.
- */
- updateEditables: function() {
- this.resetInput();
- var settingOption =
- this.select.querySelector('[value=\'' + this.setting + '\']');
- if (settingOption)
- settingOption.selected = true;
- },
- /** @inheritDoc */
- get currentInputIsValid() {
- return this.inputValidityKnown && this.inputIsValid;
- },
- /** @inheritDoc */
- get hasBeenEdited() {
- var livePattern = this.input.value;
- var liveSetting = this.select.value;
- return livePattern != this.pattern || liveSetting != this.setting;
- },
- /**
- * Called when committing an edit.
- * @param {Event} e The end event.
- * @private
- */
- onEditCommitted_: function(e) {
- var newPattern = this.input.value;
- var newSetting = this.select.value;
- this.finishEdit(newPattern, newSetting);
- },
- /**
- * Called when cancelling an edit; resets the control states.
- * @param {Event} e The cancel event.
- * @private
- */
- onEditCancelled_: function() {
- this.updateEditables();
- this.setPatternValid(true);
- },
- /**
- * Editing is complete; update the model.
- * @param {string} newPattern The pattern that the user entered.
- * @param {string} newSetting The setting the user chose.
- */
- finishEdit: function(newPattern, newSetting) {
- this.patternLabel.textContent = newPattern;
- this.settingLabel.textContent = this.settingForDisplay();
- var oldPattern = this.pattern;
- this.pattern = newPattern;
- this.setting = newSetting;
- // TODO(estade): this will need to be updated if geolocation/notifications
- // become editable.
- if (oldPattern != newPattern) {
- chrome.send('removeException',
- [this.contentType, this.mode, oldPattern]);
- }
- chrome.send('setException',
- [this.contentType, this.mode, newPattern, newSetting]);
- }
- };
- /**
- * Creates a new list item for the Add New Item row, which doesn't represent
- * an actual entry in the exceptions list but allows the user to add new
- * exceptions.
- * @param {string} contentType The type of the list.
- * @param {string} mode The browser mode, 'otr' or 'normal'.
- * @param {boolean} enableAskOption Whether to show an 'ask every time'
- * option in the select.
- * @constructor
- * @extends {cr.ui.ExceptionsListItem}
- */
- function ExceptionsAddRowListItem(contentType, mode, enableAskOption) {
- var el = cr.doc.createElement('div');
- el.mode = mode;
- el.contentType = contentType;
- el.enableAskOption = enableAskOption;
- el.dataItem = [];
- el.__proto__ = ExceptionsAddRowListItem.prototype;
- el.decorate();
- return el;
- }
- ExceptionsAddRowListItem.prototype = {
- __proto__: ExceptionsListItem.prototype,
- decorate: function() {
- ExceptionsListItem.prototype.decorate.call(this);
- this.input.placeholder = templateData.addNewExceptionInstructions;
- // Do we always want a default of allow?
- this.setting = 'allow';
- },
- /**
- * Clear the <input> and let the placeholder text show again.
- */
- resetInput: function() {
- this.input.value = '';
- },
- /** @inheritDoc */
- get hasBeenEdited() {
- return this.input.value != '';
- },
- /**
- * Editing is complete; update the model. As long as the pattern isn't
- * empty, we'll just add it.
- * @param {string} newPattern The pattern that the user entered.
- * @param {string} newSetting The setting the user chose.
- */
- finishEdit: function(newPattern, newSetting) {
- this.resetInput();
- chrome.send('setException',
- [this.contentType, this.mode, newPattern, newSetting]);
- },
- };
- /**
- * Creates a new exceptions list.
- * @constructor
- * @extends {cr.ui.List}
- */
- var ExceptionsList = cr.ui.define('list');
- ExceptionsList.prototype = {
- __proto__: InlineEditableItemList.prototype,
- /**
- * Called when an element is decorated as a list.
- */
- decorate: function() {
- InlineEditableItemList.prototype.decorate.call(this);
- this.classList.add('settings-list');
- for (var parentNode = this.parentNode; parentNode;
- parentNode = parentNode.parentNode) {
- if (parentNode.hasAttribute('contentType')) {
- this.contentType = parentNode.getAttribute('contentType');
- break;
- }
- }
- this.mode = this.getAttribute('mode');
- var exceptionList = this;
- // Whether the exceptions in this list allow an 'Ask every time' option.
- this.enableAskOption = this.contentType == 'plugins';
- this.autoExpands = true;
- this.reset();
- },
- /**
- * Creates an item to go in the list.
- * @param {Object} entry The element from the data model for this row.
- */
- createItem: function(entry) {
- if (entry) {
- return new ExceptionsListItem(this.contentType,
- this.mode,
- this.enableAskOption,
- entry);
- } else {
- var addRowItem = new ExceptionsAddRowListItem(this.contentType,
- this.mode,
- this.enableAskOption);
- addRowItem.deletable = false;
- return addRowItem;
- }
- },
- /**
- * Sets the exceptions in the js model.
- * @param {Object} entries A list of dictionaries of values, each dictionary
- * represents an exception.
- */
- setExceptions: function(entries) {
- var deleteCount = this.dataModel.length;
- if (this.isEditable()) {
- // We don't want to remove the Add New Exception row.
- deleteCount = deleteCount - 1;
- }
- var args = [0, deleteCount];
- args.push.apply(args, entries);
- this.dataModel.splice.apply(this.dataModel, args);
- },
- /**
- * The browser has finished checking a pattern for validity. Update the
- * list item to reflect this.
- * @param {string} pattern The pattern.
- * @param {bool} valid Whether said pattern is valid in the context of
- * a content exception setting.
- */
- patternValidityCheckComplete: function(pattern, valid) {
- var listItems = this.items;
- for (var i = 0; i < listItems.length; i++) {
- var listItem = listItems[i];
- // Don't do anything for messages for the item if it is not the intended
- // recipient, or if the response is stale (i.e. the input value has
- // changed since we sent the request to analyze it).
- if (pattern == listItem.input.value)
- listItem.setPatternValid(valid);
- }
- },
- /**
- * Returns whether the rows are editable in this list.
- */
- isEditable: function() {
- // Editing notifications and geolocation is disabled for now.
- return !(this.contentType == 'notifications' ||
- this.contentType == 'location' ||
- this.contentType == 'fullscreen');
- },
- /**
- * Removes all exceptions from the js model.
- */
- reset: function() {
- if (this.isEditable()) {
- // The null creates the Add New Exception row.
- this.dataModel = new ArrayDataModel([null]);
- } else {
- this.dataModel = new ArrayDataModel([]);
- }
- },
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- var listItem = this.getListItemByIndex(index);
- if (listItem.undeletable)
- return;
- var dataItem = listItem.dataItem;
- var args = [listItem.contentType];
- if (listItem.contentType == 'location')
- args.push(dataItem['origin'], dataItem['embeddingOrigin']);
- else if (listItem.contentType == 'notifications')
- args.push(dataItem['origin'], dataItem['setting']);
- else
- args.push(listItem.mode, listItem.pattern);
- chrome.send('removeException', args);
- },
- };
- var OptionsPage = options.OptionsPage;
- /**
- * Encapsulated handling of content settings list subpage.
- * @constructor
- */
- function ContentSettingsExceptionsArea() {
- OptionsPage.call(this, 'contentExceptions',
- templateData.contentSettingsPageTabTitle,
- 'content-settings-exceptions-area');
- }
- cr.addSingletonGetter(ContentSettingsExceptionsArea);
- ContentSettingsExceptionsArea.prototype = {
- __proto__: OptionsPage.prototype,
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- var exceptionsLists = this.pageDiv.querySelectorAll('list');
- for (var i = 0; i < exceptionsLists.length; i++) {
- options.contentSettings.ExceptionsList.decorate(exceptionsLists[i]);
- }
- ContentSettingsExceptionsArea.hideOTRLists();
- // If the user types in the URL without a hash, show just cookies.
- this.showList('cookies');
- },
- /**
- * Shows one list and hides all others.
- * @param {string} type The content type.
- */
- showList: function(type) {
- var header = this.pageDiv.querySelector('h1');
- header.textContent = templateData[type + '_header'];
- var divs = this.pageDiv.querySelectorAll('div[contentType]');
- for (var i = 0; i < divs.length; i++) {
- if (divs[i].getAttribute('contentType') == type)
- divs[i].hidden = false;
- else
- divs[i].hidden = true;
- }
- },
- /**
- * Called after the page has been shown. Show the content type for the
- * location's hash.
- */
- didShowPage: function() {
- var hash = location.hash;
- if (hash)
- this.showList(hash.slice(1));
- },
- };
- /**
- * Called when the last incognito window is closed.
- */
- ContentSettingsExceptionsArea.OTRProfileDestroyed = function() {
- this.hideOTRLists();
- };
- /**
- * Clears and hides the incognito exceptions lists.
- */
- ContentSettingsExceptionsArea.hideOTRLists = function() {
- var otrLists = document.querySelectorAll('list[mode=otr]');
- for (var i = 0; i < otrLists.length; i++) {
- otrLists[i].reset();
- otrLists[i].parentNode.hidden = true;
- }
- };
- return {
- ExceptionsListItem: ExceptionsListItem,
- ExceptionsAddRowListItem: ExceptionsAddRowListItem,
- ExceptionsList: ExceptionsList,
- ContentSettingsExceptionsArea: ContentSettingsExceptionsArea,
- };
- });
- // 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() {
- //////////////////////////////////////////////////////////////////////////////
- // ContentSettingsRadio class:
- // Define a constructor that uses an input element as its underlying element.
- var ContentSettingsRadio = cr.ui.define('input');
- ContentSettingsRadio.prototype = {
- __proto__: HTMLInputElement.prototype,
- /**
- * Initialization function for the cr.ui framework.
- */
- decorate: function() {
- this.type = 'radio';
- var self = this;
- this.addEventListener('change',
- function(e) {
- chrome.send('setContentFilter', [this.name, this.value]);
- });
- },
- };
- /**
- * Whether the content setting is controlled by something else than the user's
- * settings (either 'policy' or 'extension').
- * @type {string}
- */
- cr.defineProperty(ContentSettingsRadio, 'controlledBy', cr.PropertyKind.ATTR);
- //////////////////////////////////////////////////////////////////////////////
- // HandlersEnabledRadio class:
- // Define a constructor that uses an input element as its underlying element.
- var HandlersEnabledRadio = cr.ui.define('input');
- HandlersEnabledRadio.prototype = {
- __proto__: HTMLInputElement.prototype,
- /**
- * Initialization function for the cr.ui framework.
- */
- decorate: function() {
- this.type = 'radio';
- var self = this;
- this.addEventListener('change',
- function(e) {
- chrome.send('setHandlersEnabled', [this.value == 'allow']);
- });
- },
- };
- // Export
- return {
- ContentSettingsRadio: ContentSettingsRadio,
- HandlersEnabledRadio: HandlersEnabledRadio
- };
- });
- // 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() {
- const DeletableItemList = options.DeletableItemList;
- const DeletableItem = options.DeletableItem;
- const ArrayDataModel = cr.ui.ArrayDataModel;
- const ListSingleSelectionModel = cr.ui.ListSingleSelectionModel;
- // This structure maps the various cookie type names from C++ (hence the
- // underscores) to arrays of the different types of data each has, along with
- // the i18n name for the description of that data type.
- const cookieInfo = {
- 'cookie': [ ['name', 'label_cookie_name'],
- ['content', 'label_cookie_content'],
- ['domain', 'label_cookie_domain'],
- ['path', 'label_cookie_path'],
- ['sendfor', 'label_cookie_send_for'],
- ['accessibleToScript', 'label_cookie_accessible_to_script'],
- ['created', 'label_cookie_created'],
- ['expires', 'label_cookie_expires'] ],
- 'app_cache': [ ['manifest', 'label_app_cache_manifest'],
- ['size', 'label_local_storage_size'],
- ['created', 'label_cookie_created'],
- ['accessed', 'label_cookie_last_accessed'] ],
- 'database': [ ['name', 'label_cookie_name'],
- ['desc', 'label_webdb_desc'],
- ['size', 'label_local_storage_size'],
- ['modified', 'label_local_storage_last_modified'] ],
- 'local_storage': [ ['origin', 'label_local_storage_origin'],
- ['size', 'label_local_storage_size'],
- ['modified', 'label_local_storage_last_modified'] ],
- 'indexed_db': [ ['origin', 'label_indexed_db_origin'],
- ['size', 'label_indexed_db_size'],
- ['modified', 'label_indexed_db_last_modified'] ],
- 'file_system': [ ['origin', 'label_file_system_origin'],
- ['persistent', 'label_file_system_persistent_usage' ],
- ['temporary', 'label_file_system_temporary_usage' ] ],
- };
- const localStrings = new LocalStrings();
- /**
- * Returns the item's height, like offsetHeight but such that it works better
- * when the page is zoomed. See the similar calculation in @{code cr.ui.List}.
- * This version also accounts for the animation done in this file.
- * @param {Element} item The item to get the height of.
- * @return {number} The height of the item, calculated with zooming in mind.
- */
- function getItemHeight(item) {
- var height = item.style.height;
- // Use the fixed animation target height if set, in case the element is
- // currently being animated and we'd get an intermediate height below.
- if (height && height.substr(-2) == 'px')
- return parseInt(height.substr(0, height.length - 2));
- return item.getBoundingClientRect().height;
- }
- /**
- * Create tree nodes for the objects in the data array, and insert them all
- * into the given list using its @{code splice} method at the given index.
- * @param {Array.<Object>} data The data objects for the nodes to add.
- * @param {number} start The index at which to start inserting the nodes.
- * @return {Array.<CookieTreeNode>} An array of CookieTreeNodes added.
- */
- function spliceTreeNodes(data, start, list) {
- var nodes = data.map(function(x) { return new CookieTreeNode(x); });
- // Insert [start, 0] at the beginning of the array of nodes, making it
- // into the arguments we want to pass to @{code list.splice} below.
- nodes.splice(0, 0, start, 0);
- list.splice.apply(list, nodes);
- // Remove the [start, 0] prefix and return the array of nodes.
- nodes.splice(0, 2);
- return nodes;
- }
- var parentLookup = {};
- var lookupRequests = {};
- /**
- * Creates a new list item for sites data. Note that these are created and
- * destroyed lazily as they scroll into and out of view, so they must be
- * stateless. We cache the expanded item in @{code CookiesList} though, so it
- * can keep state. (Mostly just which item is selected.)
- * @param {Object} origin Data used to create a cookie list item.
- * @param {CookiesList} list The list that will contain this item.
- * @constructor
- * @extends {DeletableItem}
- */
- function CookieListItem(origin, list) {
- var listItem = new DeletableItem(null);
- listItem.__proto__ = CookieListItem.prototype;
- listItem.origin = origin;
- listItem.list = list;
- listItem.decorate();
- // This hooks up updateOrigin() to the list item, makes the top-level
- // tree nodes (i.e., origins) register their IDs in parentLookup, and
- // causes them to request their children if they have none. Note that we
- // have special logic in the setter for the parent property to make sure
- // that we can still garbage collect list items when they scroll out of
- // view, even though it appears that we keep a direct reference.
- if (origin) {
- origin.parent = listItem;
- origin.updateOrigin();
- }
- return listItem;
- }
- CookieListItem.prototype = {
- __proto__: DeletableItem.prototype,
- /** @inheritDoc */
- decorate: function() {
- this.siteChild = this.ownerDocument.createElement('div');
- this.siteChild.className = 'cookie-site';
- this.dataChild = this.ownerDocument.createElement('div');
- this.dataChild.className = 'cookie-data';
- this.sizeChild = this.ownerDocument.createElement('div');
- this.sizeChild.className = 'cookie-size';
- this.itemsChild = this.ownerDocument.createElement('div');
- this.itemsChild.className = 'cookie-items';
- this.infoChild = this.ownerDocument.createElement('div');
- this.infoChild.className = 'cookie-details';
- this.infoChild.hidden = true;
- var remove = this.ownerDocument.createElement('button');
- remove.textContent = localStrings.getString('remove_cookie');
- remove.onclick = this.removeCookie_.bind(this);
- this.infoChild.appendChild(remove);
- var content = this.contentElement;
- content.appendChild(this.siteChild);
- content.appendChild(this.dataChild);
- content.appendChild(this.sizeChild);
- content.appendChild(this.itemsChild);
- this.itemsChild.appendChild(this.infoChild);
- if (this.origin && this.origin.data) {
- this.siteChild.textContent = this.origin.data.title;
- this.siteChild.setAttribute('title', this.origin.data.title);
- }
- this.itemList_ = [];
- },
- /** @type {boolean} */
- get expanded() {
- return this.expanded_;
- },
- set expanded(expanded) {
- if (this.expanded_ == expanded)
- return;
- this.expanded_ = expanded;
- if (expanded) {
- var oldExpanded = this.list.expandedItem;
- this.list.expandedItem = this;
- this.updateItems_();
- if (oldExpanded)
- oldExpanded.expanded = false;
- this.classList.add('show-items');
- } else {
- if (this.list.expandedItem == this) {
- this.list.leadItemHeight = 0;
- this.list.expandedItem = null;
- }
- this.style.height = '';
- this.itemsChild.style.height = '';
- this.classList.remove('show-items');
- }
- },
- /**
- * The callback for the "remove" button shown when an item is selected.
- * Requests that the currently selected cookie be removed.
- * @private
- */
- removeCookie_: function() {
- if (this.selectedIndex_ >= 0) {
- var item = this.itemList_[this.selectedIndex_];
- if (item && item.node)
- chrome.send('removeCookie', [item.node.pathId]);
- }
- },
- /**
- * Disable animation within this cookie list item, in preparation for making
- * changes that will need to be animated. Makes it possible to measure the
- * contents without displaying them, to set animation targets.
- * @private
- */
- disableAnimation_: function() {
- this.itemsHeight_ = getItemHeight(this.itemsChild);
- this.classList.add('measure-items');
- },
- /**
- * Enable animation after changing the contents of this cookie list item.
- * See @{code disableAnimation_}.
- * @private
- */
- enableAnimation_: function() {
- if (!this.classList.contains('measure-items'))
- this.disableAnimation_();
- this.itemsChild.style.height = '';
- // This will force relayout in order to calculate the new heights.
- var itemsHeight = getItemHeight(this.itemsChild);
- var fixedHeight = getItemHeight(this) + itemsHeight - this.itemsHeight_;
- this.itemsChild.style.height = this.itemsHeight_ + 'px';
- // Force relayout before enabling animation, so that if we have
- // changed things since the last layout, they will not be animated
- // during subsequent layouts.
- this.itemsChild.offsetHeight;
- this.classList.remove('measure-items');
- this.itemsChild.style.height = itemsHeight + 'px';
- this.style.height = fixedHeight + 'px';
- if (this.expanded)
- this.list.leadItemHeight = fixedHeight;
- },
- /**
- * Updates the origin summary to reflect changes in its items.
- * Both CookieListItem and CookieTreeNode implement this API.
- * This implementation scans the descendants to update the text.
- */
- updateOrigin: function() {
- var info = {
- cookies: 0,
- database: false,
- localStorage: false,
- appCache: false,
- indexedDb: false,
- fileSystem: false,
- };
- if (this.origin)
- this.origin.collectSummaryInfo(info);
- var list = [];
- if (info.cookies > 1)
- list.push(localStrings.getStringF('cookie_plural', info.cookies));
- else if (info.cookies > 0)
- list.push(localStrings.getString('cookie_singular'));
- if (info.database || info.indexedDb)
- list.push(localStrings.getString('cookie_database_storage'));
- if (info.localStorage)
- list.push(localStrings.getString('cookie_local_storage'));
- if (info.appCache)
- list.push(localStrings.getString('cookie_app_cache'));
- if (info.fileSystem)
- list.push(localStrings.getString('cookie_file_system'));
- var text = '';
- for (var i = 0; i < list.length; ++i)
- if (text.length > 0)
- text += ', ' + list[i];
- else
- text = list[i];
- this.dataChild.textContent = text;
- if (info.quota && info.quota.totalUsage) {
- this.sizeChild.textContent = info.quota.totalUsage;
- }
- if (this.expanded)
- this.updateItems_();
- },
- /**
- * Updates the items section to reflect changes, animating to the new state.
- * Removes existing contents and calls @{code CookieTreeNode.createItems}.
- * @private
- */
- updateItems_: function() {
- this.disableAnimation_();
- this.itemsChild.textContent = '';
- this.infoChild.hidden = true;
- this.selectedIndex_ = -1;
- this.itemList_ = [];
- if (this.origin)
- this.origin.createItems(this);
- this.itemsChild.appendChild(this.infoChild);
- this.enableAnimation_();
- },
- /**
- * Append a new cookie node "bubble" to this list item.
- * @param {CookieTreeNode} node The cookie node to add a bubble for.
- * @param {Element} div The DOM element for the bubble itself.
- * @return {number} The index the bubble was added at.
- */
- appendItem: function(node, div) {
- this.itemList_.push({node: node, div: div});
- this.itemsChild.appendChild(div);
- return this.itemList_.length - 1;
- },
- /**
- * The currently selected cookie node ("cookie bubble") index.
- * @type {number}
- * @private
- */
- selectedIndex_: -1,
- /**
- * Get the currently selected cookie node ("cookie bubble") index.
- * @type {number}
- */
- get selectedIndex() {
- return this.selectedIndex_;
- },
- /**
- * Set the currently selected cookie node ("cookie bubble") index to
- * @{code itemIndex}, unselecting any previously selected node first.
- * @param {number} itemIndex The index to set as the selected index.
- */
- set selectedIndex(itemIndex) {
- // Get the list index up front before we change anything.
- var index = this.list.getIndexOfListItem(this);
- // Unselect any previously selected item.
- if (this.selectedIndex_ >= 0) {
- var item = this.itemList_[this.selectedIndex_];
- if (item && item.div)
- item.div.removeAttribute('selected');
- }
- // Special case: decrementing -1 wraps around to the end of the list.
- if (itemIndex == -2)
- itemIndex = this.itemList_.length - 1;
- // Check if we're going out of bounds and hide the item details.
- if (itemIndex < 0 || itemIndex >= this.itemList_.length) {
- this.selectedIndex_ = -1;
- this.disableAnimation_();
- this.infoChild.hidden = true;
- this.enableAnimation_();
- return;
- }
- // Set the new selected item and show the item details for it.
- this.selectedIndex_ = itemIndex;
- this.itemList_[itemIndex].div.setAttribute('selected', '');
- this.disableAnimation_();
- this.itemList_[itemIndex].node.setDetailText(this.infoChild,
- this.list.infoNodes);
- this.infoChild.hidden = false;
- this.enableAnimation_();
- // If we're near the bottom of the list this may cause the list item to go
- // beyond the end of the visible area. Fix it after the animation is done.
- var list = this.list;
- window.setTimeout(function() { list.scrollIndexIntoView(index); }, 150);
- },
- };
- /**
- * {@code CookieTreeNode}s mirror the structure of the cookie tree lazily, and
- * contain all the actual data used to generate the {@code CookieListItem}s.
- * @param {Object} data The data object for this node.
- * @constructor
- */
- function CookieTreeNode(data) {
- this.data = data;
- this.children = [];
- }
- CookieTreeNode.prototype = {
- /**
- * Insert the given list of cookie tree nodes at the given index.
- * Both CookiesList and CookieTreeNode implement this API.
- * @param {Array.<Object>} data The data objects for the nodes to add.
- * @param {number} start The index at which to start inserting the nodes.
- */
- insertAt: function(data, start) {
- var nodes = spliceTreeNodes(data, start, this.children);
- for (var i = 0; i < nodes.length; i++)
- nodes[i].parent = this;
- this.updateOrigin();
- },
- /**
- * Remove a cookie tree node from the given index.
- * Both CookiesList and CookieTreeNode implement this API.
- * @param {number} index The index of the tree node to remove.
- */
- remove: function(index) {
- if (index < this.children.length) {
- this.children.splice(index, 1);
- this.updateOrigin();
- }
- },
- /**
- * Clears all children.
- * Both CookiesList and CookieTreeNode implement this API.
- * It is used by CookiesList.loadChildren().
- */
- clear: function() {
- // We might leave some garbage in parentLookup for removed children.
- // But that should be OK because parentLookup is cleared when we
- // reload the tree.
- this.children = [];
- this.updateOrigin();
- },
- /**
- * The counter used by startBatchUpdates() and endBatchUpdates().
- * @type {number}
- */
- batchCount_: 0,
- /**
- * See cr.ui.List.startBatchUpdates().
- * Both CookiesList (via List) and CookieTreeNode implement this API.
- */
- startBatchUpdates: function() {
- this.batchCount_++;
- },
- /**
- * See cr.ui.List.endBatchUpdates().
- * Both CookiesList (via List) and CookieTreeNode implement this API.
- */
- endBatchUpdates: function() {
- if (!--this.batchCount_)
- this.updateOrigin();
- },
- /**
- * Requests updating the origin summary to reflect changes in this item.
- * Both CookieListItem and CookieTreeNode implement this API.
- */
- updateOrigin: function() {
- if (!this.batchCount_ && this.parent)
- this.parent.updateOrigin();
- },
- /**
- * Summarize the information in this node and update @{code info}.
- * This will recurse into child nodes to summarize all descendants.
- * @param {Object} info The info object from @{code updateOrigin}.
- */
- collectSummaryInfo: function(info) {
- if (this.children.length > 0) {
- for (var i = 0; i < this.children.length; ++i)
- this.children[i].collectSummaryInfo(info);
- } else if (this.data && !this.data.hasChildren) {
- if (this.data.type == 'cookie') {
- info.cookies++;
- } else if (this.data.type == 'database') {
- info.database = true;
- } else if (this.data.type == 'local_storage') {
- info.localStorage = true;
- } else if (this.data.type == 'app_cache') {
- info.appCache = true;
- } else if (this.data.type == 'indexed_db') {
- info.indexedDb = true;
- } else if (this.data.type == 'file_system') {
- info.fileSystem = true;
- } else if (this.data.type == 'quota') {
- info.quota = this.data;
- }
- }
- },
- /**
- * Create the cookie "bubbles" for this node, recursing into children
- * if there are any. Append the cookie bubbles to @{code item}.
- * @param {CookieListItem} item The cookie list item to create items in.
- */
- createItems: function(item) {
- if (this.children.length > 0) {
- for (var i = 0; i < this.children.length; ++i)
- this.children[i].createItems(item);
- } else if (this.data && !this.data.hasChildren) {
- var text = '';
- switch (this.data.type) {
- case 'cookie':
- case 'database':
- text = this.data.name;
- break;
- case 'local_storage':
- text = localStrings.getString('cookie_local_storage');
- break;
- case 'app_cache':
- text = localStrings.getString('cookie_app_cache');
- break;
- case 'indexed_db':
- text = localStrings.getString('cookie_indexed_db');
- break;
- case 'file_system':
- text = localStrings.getString('cookie_file_system');
- break;
- }
- if (!text)
- return;
- var div = item.ownerDocument.createElement('div');
- div.className = 'cookie-item';
- // Help out screen readers and such: this is a clickable thing.
- div.setAttribute('role', 'button');
- div.textContent = text;
- var index = item.appendItem(this, div);
- div.onclick = function() {
- if (item.selectedIndex == index)
- item.selectedIndex = -1;
- else
- item.selectedIndex = index;
- };
- }
- },
- /**
- * Set the detail text to be displayed to that of this cookie tree node.
- * Uses preallocated DOM elements for each cookie node type from @{code
- * infoNodes}, and inserts the appropriate elements to @{code element}.
- * @param {Element} element The DOM element to insert elements to.
- * @param {Object.<string, {table: Element, info: Object.<string,
- * Element>}>} infoNodes The map from cookie node types to maps from
- * cookie attribute names to DOM elements to display cookie attribute
- * values, created by @{code CookiesList.decorate}.
- */
- setDetailText: function(element, infoNodes) {
- var table;
- if (this.data && !this.data.hasChildren) {
- if (cookieInfo[this.data.type]) {
- var info = cookieInfo[this.data.type];
- var nodes = infoNodes[this.data.type].info;
- for (var i = 0; i < info.length; ++i) {
- var name = info[i][0];
- if (name != 'id' && this.data[name])
- nodes[name].textContent = this.data[name];
- else
- nodes[name].textContent = '';
- }
- table = infoNodes[this.data.type].table;
- }
- }
- while (element.childNodes.length > 1)
- element.removeChild(element.firstChild);
- if (table)
- element.insertBefore(table, element.firstChild);
- },
- /**
- * The parent of this cookie tree node.
- * @type {?CookieTreeNode|CookieListItem}
- */
- get parent(parent) {
- // See below for an explanation of this special case.
- if (typeof this.parent_ == 'number')
- return this.list_.getListItemByIndex(this.parent_);
- return this.parent_;
- },
- set parent(parent) {
- if (parent == this.parent)
- return;
- if (parent instanceof CookieListItem) {
- // If the parent is to be a CookieListItem, then we keep the reference
- // to it by its containing list and list index, rather than directly.
- // This allows the list items to be garbage collected when they scroll
- // out of view (except the expanded item, which we cache). This is
- // transparent except in the setter and getter, where we handle it.
- this.parent_ = parent.listIndex;
- this.list_ = parent.list;
- parent.addEventListener('listIndexChange',
- this.parentIndexChanged_.bind(this));
- } else {
- this.parent_ = parent;
- }
- if (this.data && this.data.id) {
- if (parent)
- parentLookup[this.data.id] = this;
- else
- delete parentLookup[this.data.id];
- }
- if (this.data && this.data.hasChildren &&
- !this.children.length && !lookupRequests[this.data.id]) {
- lookupRequests[this.data.id] = true;
- chrome.send('loadCookie', [this.pathId]);
- }
- },
- /**
- * Called when the parent is a CookieListItem whose index has changed.
- * See the code above that avoids keeping a direct reference to
- * CookieListItem parents, to allow them to be garbage collected.
- * @private
- */
- parentIndexChanged_: function(event) {
- if (typeof this.parent_ == 'number') {
- this.parent_ = event.newValue;
- // We set a timeout to update the origin, rather than doing it right
- // away, because this callback may occur while the list items are
- // being repopulated following a scroll event. Calling updateOrigin()
- // immediately could trigger relayout that would reset the scroll
- // position within the list, among other things.
- window.setTimeout(this.updateOrigin.bind(this), 0);
- }
- },
- /**
- * The cookie tree path id.
- * @type {string}
- */
- get pathId() {
- var parent = this.parent;
- if (parent && parent instanceof CookieTreeNode)
- return parent.pathId + ',' + this.data.id;
- return this.data.id;
- },
- };
- /**
- * Creates a new cookies list.
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
- * @extends {DeletableItemList}
- */
- var CookiesList = cr.ui.define('list');
- CookiesList.prototype = {
- __proto__: DeletableItemList.prototype,
- /** @inheritDoc */
- decorate: function() {
- DeletableItemList.prototype.decorate.call(this);
- this.classList.add('cookie-list');
- this.data_ = [];
- this.dataModel = new ArrayDataModel(this.data_);
- this.addEventListener('keydown', this.handleKeyLeftRight_.bind(this));
- var sm = new ListSingleSelectionModel();
- sm.addEventListener('change', this.cookieSelectionChange_.bind(this));
- sm.addEventListener('leadIndexChange', this.cookieLeadChange_.bind(this));
- this.selectionModel = sm;
- this.infoNodes = {};
- var doc = this.ownerDocument;
- // Create a table for each type of site data (e.g. cookies, databases,
- // etc.) and save it so that we can reuse it for all origins.
- for (var type in cookieInfo) {
- var table = doc.createElement('table');
- table.className = 'cookie-details-table';
- var tbody = doc.createElement('tbody');
- table.appendChild(tbody);
- var info = {};
- for (var i = 0; i < cookieInfo[type].length; i++) {
- var tr = doc.createElement('tr');
- var name = doc.createElement('td');
- var data = doc.createElement('td');
- var pair = cookieInfo[type][i];
- name.className = 'cookie-details-label';
- name.textContent = localStrings.getString(pair[1]);
- data.className = 'cookie-details-value';
- data.textContent = '';
- tr.appendChild(name);
- tr.appendChild(data);
- tbody.appendChild(tr);
- info[pair[0]] = data;
- }
- this.infoNodes[type] = {table: table, info: info};
- }
- },
- /**
- * Handles key down events and looks for left and right arrows, then
- * dispatches to the currently expanded item, if any.
- * @param {Event} e The keydown event.
- * @private
- */
- handleKeyLeftRight_: function(e) {
- var id = e.keyIdentifier;
- if ((id == 'Left' || id == 'Right') && this.expandedItem) {
- var cs = this.ownerDocument.defaultView.getComputedStyle(this);
- var rtl = cs.direction == 'rtl';
- if ((!rtl && id == 'Left') || (rtl && id == 'Right'))
- this.expandedItem.selectedIndex--;
- else
- this.expandedItem.selectedIndex++;
- this.scrollIndexIntoView(this.expandedItem.listIndex);
- // Prevent the page itself from scrolling.
- e.preventDefault();
- }
- },
- /**
- * Called on selection model selection changes.
- * @param {Event} ce The selection change event.
- * @private
- */
- cookieSelectionChange_: function(ce) {
- ce.changes.forEach(function(change) {
- var listItem = this.getListItemByIndex(change.index);
- if (listItem) {
- if (!change.selected) {
- // We set a timeout here, rather than setting the item unexpanded
- // immediately, so that if another item gets set expanded right
- // away, it will be expanded before this item is unexpanded. It
- // will notice that, and unexpand this item in sync with its own
- // expansion. Later, this callback will end up having no effect.
- window.setTimeout(function() {
- if (!listItem.selected || !listItem.lead)
- listItem.expanded = false;
- }, 0);
- } else if (listItem.lead) {
- listItem.expanded = true;
- }
- }
- }, this);
- },
- /**
- * Called on selection model lead changes.
- * @param {Event} pe The lead change event.
- * @private
- */
- cookieLeadChange_: function(pe) {
- if (pe.oldValue != -1) {
- var listItem = this.getListItemByIndex(pe.oldValue);
- if (listItem) {
- // See cookieSelectionChange_ above for why we use a timeout here.
- window.setTimeout(function() {
- if (!listItem.lead || !listItem.selected)
- listItem.expanded = false;
- }, 0);
- }
- }
- if (pe.newValue != -1) {
- var listItem = this.getListItemByIndex(pe.newValue);
- if (listItem && listItem.selected)
- listItem.expanded = true;
- }
- },
- /**
- * The currently expanded item. Used by CookieListItem above.
- * @type {?CookieListItem}
- */
- expandedItem: null,
- // from cr.ui.List
- /** @inheritDoc */
- createItem: function(data) {
- // We use the cached expanded item in order to allow it to maintain some
- // state (like its fixed height, and which bubble is selected).
- if (this.expandedItem && this.expandedItem.origin == data)
- return this.expandedItem;
- return new CookieListItem(data, this);
- },
- // from options.DeletableItemList
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- var item = this.data_[index];
- if (item) {
- var pathId = item.pathId;
- if (pathId)
- chrome.send('removeCookie', [pathId]);
- }
- },
- /**
- * Insert the given list of cookie tree nodes at the given index.
- * Both CookiesList and CookieTreeNode implement this API.
- * @param {Array.<Object>} data The data objects for the nodes to add.
- * @param {number} start The index at which to start inserting the nodes.
- */
- insertAt: function(data, start) {
- spliceTreeNodes(data, start, this.dataModel);
- },
- /**
- * Remove a cookie tree node from the given index.
- * Both CookiesList and CookieTreeNode implement this API.
- * @param {number} index The index of the tree node to remove.
- */
- remove: function(index) {
- if (index < this.data_.length)
- this.dataModel.splice(index, 1);
- },
- /**
- * Clears the list.
- * Both CookiesList and CookieTreeNode implement this API.
- * It is used by CookiesList.loadChildren().
- */
- clear: function() {
- parentLookup = {};
- this.data_ = [];
- this.dataModel = new ArrayDataModel(this.data_);
- this.redraw();
- },
- /**
- * Add tree nodes by given parent.
- * @param {Object} parent The parent node.
- * @param {number} start The index at which to start inserting the nodes.
- * @param {Array} nodesData Nodes data array.
- * @private
- */
- addByParent_: function(parent, start, nodesData) {
- if (!parent)
- return;
- parent.startBatchUpdates();
- parent.insertAt(nodesData, start);
- parent.endBatchUpdates();
- cr.dispatchSimpleEvent(this, 'change');
- },
- /**
- * Add tree nodes by parent id.
- * This is used by cookies_view.js.
- * @param {string} parentId Id of the parent node.
- * @param {number} start The index at which to start inserting the nodes.
- * @param {Array} nodesData Nodes data array.
- */
- addByParentId: function(parentId, start, nodesData) {
- var parent = parentId ? parentLookup[parentId] : this;
- this.addByParent_(parent, start, nodesData);
- },
- /**
- * Removes tree nodes by parent id.
- * This is used by cookies_view.js.
- * @param {string} parentId Id of the parent node.
- * @param {number} start The index at which to start removing the nodes.
- * @param {number} count Number of nodes to remove.
- */
- removeByParentId: function(parentId, start, count) {
- var parent = parentId ? parentLookup[parentId] : this;
- if (!parent)
- return;
- parent.startBatchUpdates();
- while (count-- > 0)
- parent.remove(start);
- parent.endBatchUpdates();
- cr.dispatchSimpleEvent(this, 'change');
- },
- /**
- * Loads the immediate children of given parent node.
- * This is used by cookies_view.js.
- * @param {string} parentId Id of the parent node.
- * @param {Array} children The immediate children of parent node.
- */
- loadChildren: function(parentId, children) {
- if (parentId)
- delete lookupRequests[parentId];
- var parent = parentId ? parentLookup[parentId] : this;
- if (!parent)
- return;
- parent.startBatchUpdates();
- parent.clear();
- this.addByParent_(parent, 0, children);
- parent.endBatchUpdates();
- },
- };
- return {
- CookiesList: CookiesList
- };
- });
- // 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 OptionsPage = options.OptionsPage;
- /////////////////////////////////////////////////////////////////////////////
- // CookiesView class:
- /**
- * Encapsulated handling of the cookies and other site data page.
- * @constructor
- */
- function CookiesView(model) {
- OptionsPage.call(this, 'cookies',
- templateData.cookiesViewPageTabTitle,
- 'cookiesViewPage');
- }
- cr.addSingletonGetter(CookiesView);
- CookiesView.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * The timer id of the timer set on search query change events.
- * @type {number}
- * @private
- */
- queryDelayTimerId_: 0,
- /**
- * The most recent search query, or null if the query is empty.
- * @type {?string}
- * @private
- */
- lastQuery_ : null,
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- $('cookies-search-box').addEventListener('search',
- this.handleSearchQueryChange_.bind(this));
- $('remove-all-cookies-button').onclick = function(e) {
- chrome.send('removeAllCookies', []);
- };
- var cookiesList = $('cookies-list');
- options.CookiesList.decorate(cookiesList);
- window.addEventListener('resize', this.handleResize_.bind(this));
- this.addEventListener('visibleChange', this.handleVisibleChange_);
- },
- /**
- * Search cookie using text in |cookies-search-box|.
- */
- searchCookie: function() {
- this.queryDelayTimerId_ = 0;
- var filter = $('cookies-search-box').value;
- if (this.lastQuery_ != filter) {
- this.lastQuery_ = filter;
- chrome.send('updateCookieSearchResults', [filter]);
- }
- },
- /**
- * Handles search query changes.
- * @param {!Event} e The event object.
- * @private
- */
- handleSearchQueryChange_: function(e) {
- if (this.queryDelayTimerId_)
- window.clearTimeout(this.queryDelayTimerId_);
- this.queryDelayTimerId_ = window.setTimeout(
- this.searchCookie.bind(this), 500);
- },
- initialized_: false,
- /**
- * Handler for OptionsPage's visible property change event.
- * @param {Event} e Property change event.
- * @private
- */
- handleVisibleChange_: function(e) {
- if (!this.visible)
- return;
- // Resize the cookies list whenever the options page becomes visible.
- this.handleResize_(null);
- if (!this.initialized_) {
- this.initialized_ = true;
- this.searchCookie();
- } else {
- $('cookies-list').redraw();
- }
- $('cookies-search-box').focus();
- },
- /**
- * Handler for when the window changes size. Resizes the cookies list to
- * match the window height.
- * @param {?Event} e Window resize event, or null if called directly.
- * @private
- */
- handleResize_: function(e) {
- if (!this.visible)
- return;
- var cookiesList = $('cookies-list');
- // 25 pixels from the window bottom seems like a visually pleasing amount.
- var height = window.innerHeight - cookiesList.offsetTop - 25;
- cookiesList.style.height = height + 'px';
- },
- };
- // CookiesViewHandler callbacks.
- CookiesView.onTreeItemAdded = function(args) {
- $('cookies-list').addByParentId(args[0], args[1], args[2]);
- };
- CookiesView.onTreeItemRemoved = function(args) {
- $('cookies-list').removeByParentId(args[0], args[1], args[2]);
- };
- CookiesView.loadChildren = function(args) {
- $('cookies-list').loadChildren(args[0], args[1]);
- };
- // Export
- return {
- CookiesView: CookiesView
- };
- });
- // 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() {
- 'use strict';
- /**
- * A lookup helper function to find the first node that has an id (starting
- * at |node| and going up the parent chain).
- * @param {Element} node The node to start looking at.
- */
- function findIdNode(node) {
- while (node && !node.id) {
- node = node.parentNode;
- }
- return node;
- }
- /**
- * Creates a new list of extensions.
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
- * @extends {cr.ui.div}
- */
- var ExtensionsList = cr.ui.define('div');
- var handlersInstalled = false;
- /**
- * @type {Object.<string, boolean>} A map from extension id to a boolean
- * indicating whether its details section is expanded. This persists
- * between calls to decorate.
- */
- var showingDetails = {};
- /**
- * @type {Object.<string, boolean>} A map from extension id to a boolean
- * indicating whether the incognito warning is showing. This persists
- * between calls to decorate.
- */
- var showingWarning = {};
- ExtensionsList.prototype = {
- __proto__: HTMLDivElement.prototype,
- /** @inheritDoc */
- decorate: function() {
- this.initControlsAndHandlers_();
- this.deleteExistingExtensionNodes_();
- this.showExtensionNodes_();
- },
- /**
- * Initializes the controls (toggle section and button) and installs
- * handlers.
- * @private
- */
- initControlsAndHandlers_: function() {
- // Make sure developer mode section is set correctly as per saved setting.
- var toggleButton = $('toggle-dev-on');
- var toggleSection = $('dev');
- if (this.data_.developerMode) {
- toggleSection.classList.add('dev-open');
- toggleSection.classList.remove('dev-closed');
- toggleButton.checked = true;
- } else {
- toggleSection.classList.remove('dev-open');
- toggleSection.classList.add('dev-closed');
- }
- // Instal global event handlers.
- if (!handlersInstalled) {
- var searchPage = SearchPage.getInstance();
- searchPage.addEventListener('searchChanged',
- this.searchChangedHandler_.bind(this));
- // Support full keyboard accessibility without making things ugly
- // for users who click, by hiding some focus outlines when the user
- // clicks anywhere, but showing them when the user presses any key.
- this.ownerDocument.body.classList.add('hide-some-focus-outlines');
- this.ownerDocument.addEventListener('click', (function(e) {
- this.ownerDocument.body.classList.add('hide-some-focus-outlines');
- return true;
- }).bind(this), true);
- this.ownerDocument.addEventListener('keydown', (function(e) {
- this.ownerDocument.body.classList.remove('hide-some-focus-outlines');
- return true;
- }).bind(this), true);
- handlersInstalled = true;
- }
- },
- /**
- * Deletes the existing Extension nodes from the page to make room for new
- * ones.
- * @private
- */
- deleteExistingExtensionNodes_: function() {
- while (this.hasChildNodes()){
- this.removeChild(this.firstChild);
- }
- },
- /**
- * Handles decorating the details section.
- * @param {Element} details The div that the details should be attached to.
- * @param {Object} extension The extension we are showing the details for.
- * @private
- */
- showExtensionNodes_: function() {
- // Iterate over the extension data and add each item to the list.
- for (var i = 0; i < this.data_.extensions.length; i++) {
- var extension = this.data_.extensions[i];
- var id = extension.id;
- var wrapper = this.ownerDocument.createElement('div');
- var expanded = showingDetails[id];
- var butterbar = showingWarning[id];
- wrapper.classList.add(expanded ? 'extension-list-item-expanded' :
- 'extension-list-item-collaped');
- if (!extension.enabled)
- wrapper.classList.add('disabled');
- wrapper.id = id;
- this.appendChild(wrapper);
- var vboxOuter = this.ownerDocument.createElement('div');
- vboxOuter.classList.add('vbox');
- vboxOuter.classList.add('extension-list-item');
- wrapper.appendChild(vboxOuter);
- var hbox = this.ownerDocument.createElement('div');
- hbox.classList.add('hbox');
- vboxOuter.appendChild(hbox);
- // Add a container div for the zippy, so we can extend the hit area.
- var container = this.ownerDocument.createElement('div');
- // Clicking anywhere on the div expands/collapses the details.
- container.classList.add('extension-zippy-container');
- container.title = expanded ?
- localStrings.getString('extensionSettingsHideDetails') :
- localStrings.getString('extensionSettingsShowDetails');
- container.tabIndex = 0;
- container.setAttribute('role', 'button');
- container.setAttribute('aria-controls', extension.id + '_details');
- container.setAttribute('aria-expanded', expanded);
- container.addEventListener('click', this.handleZippyClick_.bind(this));
- container.addEventListener('keydown',
- this.handleZippyKeyDown_.bind(this));
- hbox.appendChild(container);
- // On the far left we have the zippy icon.
- var div = this.ownerDocument.createElement('div');
- div.id = id + '_zippy';
- div.classList.add('extension-zippy-default');
- div.classList.add(expanded ? 'extension-zippy-expanded' :
- 'extension-zippy-collapsed');
- container.appendChild(div);
- // Next to it, we have the extension icon.
- var icon = this.ownerDocument.createElement('img');
- icon.classList.add('extension-icon');
- icon.src = extension.icon;
- hbox.appendChild(icon);
- // Start a vertical box for showing the details.
- var vbox = this.ownerDocument.createElement('div');
- vbox.classList.add('vbox');
- vbox.classList.add('stretch');
- vbox.classList.add('details-view');
- hbox.appendChild(vbox);
- div = this.ownerDocument.createElement('div');
- vbox.appendChild(div);
- // Title comes next.
- var title = this.ownerDocument.createElement('span');
- title.classList.add('extension-title');
- title.textContent = extension.name;
- vbox.appendChild(title);
- // Followed by version.
- var version = this.ownerDocument.createElement('span');
- version.classList.add('extension-version');
- version.textContent = extension.version;
- vbox.appendChild(version);
- // And the additional info label (unpacked/crashed).
- if (extension.terminated || extension.isUnpacked) {
- var version = this.ownerDocument.createElement('span');
- version.classList.add('extension-version');
- version.textContent = extension.terminated ?
- localStrings.getString('extensionSettingsCrashMessage') :
- localStrings.getString('extensionSettingsInDevelopment');
- vbox.appendChild(version);
- }
- div = this.ownerDocument.createElement('div');
- vbox.appendChild(div);
- // And below that we have description (if provided).
- if (extension.description.length > 0) {
- var description = this.ownerDocument.createElement('span');
- description.classList.add('extension-description');
- description.textContent = extension.description;
- vbox.appendChild(description);
- }
- // Immediately following the description, we have the
- // Options link (optional).
- if (extension.options_url) {
- var link = this.ownerDocument.createElement('a');
- link.classList.add('extension-links-trailing');
- link.textContent = localStrings.getString('extensionSettingsOptions');
- link.href = '#';
- link.addEventListener('click', this.handleOptions_.bind(this));
- vbox.appendChild(link);
- }
- // Then the optional Visit Website link.
- if (extension.homepageUrl) {
- var link = this.ownerDocument.createElement('a');
- link.classList.add('extension-links-trailing');
- link.textContent =
- localStrings.getString('extensionSettingsVisitWebsite');
- link.href = extension.homepageUrl;
- vbox.appendChild(link);
- }
- if (extension.warnings.length > 0) {
- var warningsDiv = this.ownerDocument.createElement('div');
- warningsDiv.classList.add('extension-warnings');
- var warningsHeader = this.ownerDocument.createElement('span');
- warningsHeader.classList.add('extension-warnings-title');
- warningsHeader.textContent =
- localStrings.getString('extensionSettingsWarningsTitle');
- warningsDiv.appendChild(warningsHeader);
- var warningList = this.ownerDocument.createElement('ul');
- for (var j = 0; j < extension.warnings.length; ++j) {
- var warningEntry = this.ownerDocument.createElement('li');
- warningEntry.textContent = extension.warnings[j];
- warningList.appendChild(warningEntry);
- }
- warningsDiv.appendChild(warningList);
- vbox.appendChild(warningsDiv);
- }
- // And now the details section that is normally hidden.
- var details = this.ownerDocument.createElement('div');
- details.classList.add('vbox');
- vbox.appendChild(details);
- this.decorateDetailsSection_(details, extension, expanded, butterbar);
- // And on the right of the details we have the Enable/Enabled checkbox.
- div = this.ownerDocument.createElement('div');
- hbox.appendChild(div);
- var section = this.ownerDocument.createElement('section');
- section.classList.add('extension-enabling');
- div.appendChild(section);
- if (!extension.terminated) {
- // The Enable checkbox.
- var input = this.ownerDocument.createElement('input');
- input.addEventListener('click', this.handleEnable_.bind(this));
- input.type = 'checkbox';
- input.name = 'toggle-' + id;
- input.disabled = !extension.mayDisable;
- if (extension.enabled)
- input.checked = true;
- input.id = 'toggle-' + id;
- section.appendChild(input);
- var label = this.ownerDocument.createElement('label');
- label.classList.add('extension-enabling-label');
- if (extension.enabled)
- label.classList.add('extension-enabling-label-bold');
- label.htmlFor = 'toggle-' + id;
- label.id = 'toggle-' + id + '-label';
- if (extension.enabled) {
- // Enabled (with a d).
- label.textContent =
- localStrings.getString('extensionSettingsEnabled');
- } else {
- // Enable (no d).
- label.textContent =
- localStrings.getString('extensionSettingsEnable');
- }
- section.appendChild(label);
- } else {
- // Extension has been terminated, show a Reload link.
- var link = this.ownerDocument.createElement('a');
- link.classList.add('extension-links-trailing');
- link.id = extension.id;
- link.textContent =
- localStrings.getString('extensionSettingsReload');
- link.href = '#';
- link.addEventListener('click', this.handleReload_.bind(this));
- section.appendChild(link);
- }
- // And, on the far right we have the uninstall button.
- var button = this.ownerDocument.createElement('button');
- button.classList.add('extension-delete');
- button.id = id;
- if (!extension.mayDisable)
- button.disabled = true;
- button.textContent = localStrings.getString('extensionSettingsRemove');
- button.addEventListener('click', this.handleUninstall_.bind(this));
- hbox.appendChild(button);
- }
- // Do one pass to find what the size of the checkboxes should be.
- var minCheckboxWidth = Infinity;
- var maxCheckboxWidth = 0;
- for (var i = 0; i < this.data_.extensions.length; ++i) {
- var label = $('toggle-' + this.data_.extensions[i].id + '-label');
- if (label.offsetWidth > maxCheckboxWidth)
- maxCheckboxWidth = label.offsetWidth;
- if (label.offsetWidth < minCheckboxWidth)
- minCheckboxWidth = label.offsetWidth;
- }
- // Do another pass, making sure checkboxes line up.
- var difference = maxCheckboxWidth - minCheckboxWidth;
- for (var i = 0; i < this.data_.extensions.length; ++i) {
- var label = $('toggle-' + this.data_.extensions[i].id + '-label');
- if (label.offsetWidth < maxCheckboxWidth)
- label.style.WebkitMarginEnd = difference.toString() + 'px';
- }
- },
- /**
- * Handles decorating the details section.
- * @param {Element} details The div that the details should be attached to.
- * @param {Object} extension The extension we are shoting the details for.
- * @param {boolean} expanded Whether to show the details expanded or not.
- * @param {boolean} showButterbar Whether to show the incognito warning or
- * not.
- * @private
- */
- decorateDetailsSection_: function(details, extension,
- expanded, showButterbar) {
- // This container div is needed because vbox display
- // overrides display:hidden.
- var detailsContents = this.ownerDocument.createElement('div');
- detailsContents.classList.add(expanded ? 'extension-details-visible' :
- 'extension-details-hidden');
- detailsContents.id = extension.id + '_details';
- details.appendChild(detailsContents);
- var div = this.ownerDocument.createElement('div');
- div.classList.add('informative-text');
- detailsContents.appendChild(div);
- // Keep track of how many items we'll show in the details section.
- var itemsShown = 0;
- if (this.data_.developerMode) {
- // First we have the id.
- var content = this.ownerDocument.createElement('div');
- content.textContent =
- localStrings.getString('extensionSettingsExtensionId') +
- ' ' + extension.id;
- div.appendChild(content);
- itemsShown++;
- // Then, the path, if provided by unpacked extension.
- if (extension.isUnpacked) {
- content = this.ownerDocument.createElement('div');
- content.textContent =
- localStrings.getString('extensionSettingsExtensionPath') +
- ' ' + extension.path;
- div.appendChild(content);
- itemsShown++;
- }
- // Then, the 'managed, cannot uninstall/disable' message.
- if (!extension.mayDisable) {
- content = this.ownerDocument.createElement('div');
- content.textContent =
- localStrings.getString('extensionSettingsPolicyControlled');
- div.appendChild(content);
- itemsShown++;
- }
- // Then active views:
- if (extension.views.length > 0) {
- var table = this.ownerDocument.createElement('table');
- table.classList.add('extension-inspect-table');
- div.appendChild(table);
- var tr = this.ownerDocument.createElement('tr');
- table.appendChild(tr);
- var td = this.ownerDocument.createElement('td');
- td.classList.add('extension-inspect-left-column');
- tr.appendChild(td);
- var span = this.ownerDocument.createElement('span');
- td.appendChild(span);
- span.textContent =
- localStrings.getString('extensionSettingsInspectViews');
- td = this.ownerDocument.createElement('td');
- for (var i = 0; i < extension.views.length; ++i) {
- // Then active views:
- content = this.ownerDocument.createElement('div');
- var link = this.ownerDocument.createElement('a');
- link.classList.add('extension-links-view');
- link.textContent = extension.views[i].path;
- link.id = extension.id;
- link.href = '#';
- link.addEventListener('click', this.sendInspectMessage_.bind(this));
- content.appendChild(link);
- if (extension.views[i].incognito) {
- var incognito = this.ownerDocument.createElement('span');
- incognito.classList.add('extension-links-view');
- incognito.textContent =
- localStrings.getString('viewIncognito');
- content.appendChild(incognito);
- }
- td.appendChild(content);
- tr.appendChild(td);
- itemsShown++;
- }
- }
- }
- var content = this.ownerDocument.createElement('div');
- detailsContents.appendChild(content);
- // Then Reload:
- if (extension.enabled && extension.allow_reload) {
- this.addLinkTo_(content,
- localStrings.getString('extensionSettingsReload'),
- extension.id,
- this.handleReload_.bind(this));
- itemsShown++;
- }
- // Then Show (Browser Action) Button:
- if (extension.enabled && extension.enable_show_button) {
- this.addLinkTo_(content,
- localStrings.getString('extensionSettingsShowButton'),
- extension.id,
- this.handleShowButton_.bind(this));
- itemsShown++;
- }
- if (extension.enabled) {
- // The 'allow in incognito' checkbox.
- var label = this.ownerDocument.createElement('label');
- label.classList.add('extension-checkbox-label');
- content.appendChild(label);
- var input = this.ownerDocument.createElement('input');
- input.addEventListener('click',
- this.handleToggleEnableIncognito_.bind(this));
- input.id = extension.id;
- input.type = 'checkbox';
- if (extension.enabledIncognito)
- input.checked = true;
- label.appendChild(input);
- var span = this.ownerDocument.createElement('span');
- span.classList.add('extension-checkbox-span');
- span.textContent =
- localStrings.getString('extensionSettingsEnableIncognito');
- label.appendChild(span);
- itemsShown++;
- }
- if (extension.enabled && extension.wantsFileAccess) {
- // The 'allow access to file URLs' checkbox.
- label = this.ownerDocument.createElement('label');
- label.classList.add('extension-checkbox-label');
- content.appendChild(label);
- var input = this.ownerDocument.createElement('input');
- input.addEventListener('click',
- this.handleToggleAllowFileUrls_.bind(this));
- input.id = extension.id;
- input.type = 'checkbox';
- if (extension.allowFileAccess)
- input.checked = true;
- label.appendChild(input);
- var span = this.ownerDocument.createElement('span');
- span.classList.add('extension-checkbox-span');
- span.textContent =
- localStrings.getString('extensionSettingsAllowFileAccess');
- label.appendChild(span);
- itemsShown++;
- }
- if (extension.enabled && !extension.is_hosted_app) {
- // And add a hidden warning message for allowInIncognito.
- content = this.ownerDocument.createElement('div');
- content.id = extension.id + '_incognitoWarning';
- content.classList.add('butter-bar');
- content.hidden = !showButterbar;
- detailsContents.appendChild(content);
- var span = this.ownerDocument.createElement('span');
- span.innerHTML =
- localStrings.getString('extensionSettingsIncognitoWarning');
- content.appendChild(span);
- itemsShown++;
- }
- var zippy = extension.id + '_zippy';
- $(zippy).hidden = !itemsShown;
- // If this isn't expanded now, make sure the newly-added controls
- // are not part of the tab order.
- if (!expanded) {
- var detailsControls = details.querySelectorAll('a, input');
- for (var i = 0; i < detailsControls.length; i++)
- detailsControls[i].tabIndex = -1;
- }
- },
- /**
- * A helper function to add contextual actions for extensions (action links)
- * to the page.
- */
- addLinkTo_: function(parent, linkText, id, handler) {
- var link = this.ownerDocument.createElement('a');
- link.className = 'extension-links-trailing';
- link.textContent = linkText;
- link.id = id;
- link.href = '#';
- link.addEventListener('click', handler);
- parent.appendChild(link);
- },
- /**
- * A lookup helper function to find an extension based on an id.
- * @param {string} id The |id| of the extension to look up.
- * @private
- */
- getExtensionWithId_: function(id) {
- for (var i = 0; i < this.data_.extensions.length; ++i) {
- if (this.data_.extensions[i].id == id)
- return this.data_.extensions[i];
- }
- return null;
- },
- /**
- * Handles a key down on the zippy icon.
- * @param {Event} e Key event.
- * @private
- */
- handleZippyKeyDown_: function(e) {
- if (e.keyCode == 13 || e.keyCode == 32) // Enter or Space.
- this.handleZippyClick_(e);
- },
- /**
- * Handles the mouseclick on the zippy icon (that expands and collapses the
- * details section).
- * @param {Event} e Mouse event.
- * @private
- */
- handleZippyClick_: function(e) {
- var node = findIdNode(e.target.parentNode);
- var iter = this.firstChild;
- while (iter) {
- var zippy = $(iter.id + '_zippy');
- var details = $(iter.id + '_details');
- var container = zippy.parentElement;
- if (iter.id == node.id) {
- // Toggle visibility.
- if (iter.classList.contains('extension-list-item-expanded')) {
- // Hide yo kids! Hide yo wife!
- showingDetails[iter.id] = false;
- zippy.classList.remove('extension-zippy-expanded');
- zippy.classList.add('extension-zippy-collapsed');
- details.classList.remove('extension-details-visible');
- details.classList.add('extension-details-hidden');
- iter.classList.remove('extension-list-item-expanded');
- iter.classList.add('extension-list-item-collaped');
- container.setAttribute('aria-expanded', 'false');
- container.title =
- localStrings.getString('extensionSettingsShowDetails');
- var detailsControls = details.querySelectorAll('a, input');
- for (var i = 0; i < detailsControls.length; i++)
- detailsControls[i].tabIndex = -1;
- // Hide yo incognito warning.
- var butterBar =
- this.querySelector('#' + iter.id + '_incognitoWarning');
- if (butterBar !== null) {
- butterBar.hidden = true;
- showingWarning[iter.id] = false;
- }
- } else {
- // Show the contents.
- showingDetails[iter.id] = true;
- zippy.classList.remove('extension-zippy-collapsed');
- zippy.classList.add('extension-zippy-expanded');
- details.classList.remove('extension-details-hidden');
- details.classList.add('extension-details-visible');
- iter.classList.remove('extension-list-item-collaped');
- iter.classList.add('extension-list-item-expanded');
- container.setAttribute('aria-expanded', 'true');
- container.title =
- localStrings.getString('extensionSettingsHideDetails');
- var detailsControls = details.querySelectorAll('a, input');
- for (var i = 0; i < detailsControls.length; i++)
- detailsControls[i].tabIndex = 0;
- }
- }
- iter = iter.nextSibling;
- }
- },
- /**
- * Handles the 'searchChanged' event. This is used to limit the number of
- * items to show in the list, when the user is searching for items with the
- * search box. Otherwise, if one match is found, the whole list of
- * extensions would be shown when we only want the matching items to be
- * found.
- * @param {Event} e Change event.
- * @private
- */
- searchChangedHandler_: function(e) {
- var searchString = e.searchText;
- var child = this.firstChild;
- while (child) {
- var extension = this.getExtensionWithId_(child.id);
- if (searchString.length == 0) {
- // Show all.
- child.classList.remove('search-suppress');
- } else {
- // If the search string does not appear within the text of the
- // extension, then hide it.
- if ((extension.name.toLowerCase().indexOf(searchString) < 0) &&
- (extension.version.toLowerCase().indexOf(searchString) < 0) &&
- (extension.description.toLowerCase().indexOf(searchString) < 0)) {
- // Hide yo extension!
- child.classList.add('search-suppress');
- } else {
- // Show yourself!
- child.classList.remove('search-suppress');
- }
- }
- child = child.nextSibling;
- }
- },
- /**
- * Handles the Reload Extension functionality.
- * @param {Event} e Change event.
- * @private
- */
- handleReload_: function(e) {
- var node = findIdNode(e.target);
- chrome.send('extensionSettingsReload', [node.id]);
- },
- /**
- * Handles the Show (Browser Action) Button functionality.
- * @param {Event} e Change event.
- * @private
- */
- handleShowButton_: function(e) {
- var node = findIdNode(e.target);
- chrome.send('extensionSettingsShowButton', [node.id]);
- },
- /**
- * Handles the Enable/Disable Extension functionality.
- * @param {Event} e Change event.
- * @private
- */
- handleEnable_: function(e) {
- var node = findIdNode(e.target.parentNode);
- var extension = this.getExtensionWithId_(node.id);
- chrome.send('extensionSettingsEnable',
- [node.id, extension.enabled ? 'false' : 'true']);
- chrome.send('extensionSettingsRequestExtensionsData');
- },
- /**
- * Handles the Uninstall Extension functionality.
- * @param {Event} e Change event.
- * @private
- */
- handleUninstall_: function(e) {
- var node = findIdNode(e.target.parentNode);
- chrome.send('extensionSettingsUninstall', [node.id]);
- chrome.send('extensionSettingsRequestExtensionsData');
- },
- /**
- * Handles the View Options link.
- * @param {Event} e Change event.
- * @private
- */
- handleOptions_: function(e) {
- var node = findIdNode(e.target.parentNode);
- var extension = this.getExtensionWithId_(node.id);
- chrome.send('extensionSettingsOptions', [extension.id]);
- e.preventDefault();
- },
- /**
- * Handles the Enable Extension In Incognito functionality.
- * @param {Event} e Change event.
- * @private
- */
- handleToggleEnableIncognito_: function(e) {
- var node = findIdNode(e.target);
- var butterBar = document.getElementById(node.id + '_incognitoWarning');
- butterBar.hidden = !e.target.checked;
- showingWarning[node.id] = e.target.checked;
- chrome.send('extensionSettingsEnableIncognito',
- [node.id, String(e.target.checked)]);
- },
- /**
- * Handles the Allow On File URLs functionality.
- * @param {Event} e Change event.
- * @private
- */
- handleToggleAllowFileUrls_: function(e) {
- var node = findIdNode(e.target);
- chrome.send('extensionSettingsAllowFileAccess',
- [node.id, String(e.target.checked)]);
- },
- /**
- * Tell the C++ ExtensionDOMHandler to inspect the page detailed in
- * |viewData|.
- * @param {Event} e Change event.
- * @private
- */
- sendInspectMessage_: function(e) {
- var extension = this.getExtensionWithId_(e.srcElement.id);
- for (var i = 0; i < extension.views.length; ++i) {
- if (extension.views[i].path == e.srcElement.innerText) {
- // TODO(aa): This is ghetto, but WebUIBindings doesn't support sending
- // anything other than arrays of strings, and this is all going to get
- // replaced with V8 extensions soon anyway.
- chrome.send('extensionSettingsInspect', [
- String(extension.views[i].renderProcessId),
- String(extension.views[i].renderViewId)
- ]);
- }
- }
- },
- };
- return {
- ExtensionsList: ExtensionsList
- };
- });
- // 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.
- // Used for observing function of the backend datasource for this page by
- // tests.
- var webui_responded_ = false;
- cr.define('options', function() {
- var OptionsPage = options.OptionsPage;
- var ExtensionsList = options.ExtensionsList;
- /**
- * ExtensionSettings class
- * Encapsulated handling of the 'Manage Extensions' page.
- * @class
- */
- function ExtensionSettings() {
- OptionsPage.call(this, 'extensions',
- templateData.extensionSettingsTabTitle,
- 'extension-settings');
- }
- cr.addSingletonGetter(ExtensionSettings);
- ExtensionSettings.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * Initialize the page.
- */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- // This will request the data to show on the page and will get a response
- // back in returnExtensionsData.
- chrome.send('extensionSettingsRequestExtensionsData');
- // Set up the developer mode button.
- var toggleDevMode = $('toggle-dev-on');
- toggleDevMode.addEventListener('click',
- this.handleToggleDevMode_.bind(this));
- // Setup the gallery related links and text.
- $('suggest-gallery').innerHTML =
- localStrings.getString('extensionSettingsSuggestGallery');
- $('get-more-extensions').innerHTML =
- localStrings.getString('extensionSettingsGetMoreExtensions');
- // Set up the three dev mode buttons (load unpacked, pack and update).
- $('load-unpacked').addEventListener('click',
- this.handleLoadUnpackedExtension_.bind(this));
- $('pack-extension').addEventListener('click',
- this.handlePackExtension_.bind(this));
- $('update-extensions-now').addEventListener('click',
- this.handleUpdateExtensionNow_.bind(this));
- },
- /**
- * Utility function which asks the C++ to show a platform-specific file
- * select dialog, and fire |callback| with the |filePath| that resulted.
- * |selectType| can be either 'file' or 'folder'. |operation| can be 'load',
- * 'packRoot', or 'pem' which are signals to the C++ to do some
- * operation-specific configuration.
- * @private
- */
- showFileDialog_: function(selectType, operation, callback) {
- handleFilePathSelected = function(filePath) {
- callback(filePath);
- handleFilePathSelected = function() {};
- };
- chrome.send('extensionSettingsSelectFilePath', [selectType, operation]);
- },
- /**
- * Handles the Load Unpacked Extension button.
- * @param {Event} e Change event.
- * @private
- */
- handleLoadUnpackedExtension_: function(e) {
- this.showFileDialog_('folder', 'load', function(filePath) {
- chrome.send('extensionSettingsLoad', [String(filePath)]);
- });
- chrome.send('coreOptionsUserMetricsAction',
- ['Options_LoadUnpackedExtension']);
- },
- /**
- * Handles the Pack Extension button.
- * @param {Event} e Change event.
- * @private
- */
- handlePackExtension_: function(e) {
- OptionsPage.navigateToPage('packExtensionOverlay');
- chrome.send('coreOptionsUserMetricsAction', ['Options_PackExtension']);
- },
- /**
- * Handles the Update Extension Now button.
- * @param {Event} e Change event.
- * @private
- */
- handleUpdateExtensionNow_: function(e) {
- chrome.send('extensionSettingsAutoupdate', []);
- },
- /**
- * Handles the Toggle Dev Mode button.
- * @param {Event} e Change event.
- * @private
- */
- handleToggleDevMode_: function(e) {
- var dev = $('dev');
- if (!dev.classList.contains('dev-open')) {
- // Make the Dev section visible.
- dev.classList.add('dev-open');
- dev.classList.remove('dev-closed');
- $('load-unpacked').classList.add('dev-button-visible');
- $('load-unpacked').classList.remove('dev-button-hidden');
- $('pack-extension').classList.add('dev-button-visible');
- $('pack-extension').classList.remove('dev-button-hidden');
- $('update-extensions-now').classList.add('dev-button-visible');
- $('update-extensions-now').classList.remove('dev-button-hidden');
- } else {
- // Hide the Dev section.
- dev.classList.add('dev-closed');
- dev.classList.remove('dev-open');
- $('load-unpacked').classList.add('dev-button-hidden');
- $('load-unpacked').classList.remove('dev-button-visible');
- $('pack-extension').classList.add('dev-button-hidden');
- $('pack-extension').classList.remove('dev-button-visible');
- $('update-extensions-now').classList.add('dev-button-hidden');
- $('update-extensions-now').classList.remove('dev-button-visible');
- }
- chrome.send('extensionSettingsToggleDeveloperMode', []);
- },
- };
- /**
- * Called by the dom_ui_ to re-populate the page with data representing
- * the current state of installed extensions.
- */
- ExtensionSettings.returnExtensionsData = function(extensionsData) {
- webui_responded_ = true;
- $('no-extensions').hidden = true;
- $('suggest-gallery').hidden = true;
- $('get-more-extensions-container').hidden = true;
- if (extensionsData.extensions.length > 0) {
- // Enforce order specified in the data or (if equal) then sort by
- // extension name (case-insensitive).
- extensionsData.extensions.sort(function(a, b) {
- if (a.order == b.order) {
- a = a.name.toLowerCase();
- b = b.name.toLowerCase();
- return a < b ? -1 : (a > b ? 1 : 0);
- } else {
- return a.order < b.order ? -1 : 1;
- }
- });
- $('get-more-extensions-container').hidden = false;
- } else {
- $('no-extensions').hidden = false;
- $('suggest-gallery').hidden = false;
- }
- ExtensionsList.prototype.data_ = extensionsData;
- var extensionList = $('extension-settings-list');
- ExtensionsList.decorate(extensionList);
- }
- // Export
- return {
- ExtensionSettings: ExtensionSettings
- };
- });
- // 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 OptionsPage = options.OptionsPage;
- /**
- * This is the absolute difference maintained between standard and
- * fixed-width font sizes. Refer http://crbug.com/91922.
- */
- const SIZE_DIFFERENCE_FIXED_STANDARD = 3;
- /**
- * FontSettings class
- * Encapsulated handling of the 'Fonts and Encoding' page.
- * @class
- */
- function FontSettings() {
- OptionsPage.call(this,
- 'fonts',
- templateData.fontSettingsPageTabTitle,
- 'font-settings');
- }
- cr.addSingletonGetter(FontSettings);
- FontSettings.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * Initialize the page.
- */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- var standardFontRange = $('standard-font-size');
- standardFontRange.valueMap = [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20,
- 22, 24, 26, 28, 30, 32, 34, 36, 40, 44, 48, 56, 64, 72];
- standardFontRange.continuous = false;
- standardFontRange.notifyChange = this.standardRangeChanged_.bind(this);
- standardFontRange.notifyPrefChange =
- this.standardFontSizeChanged_.bind(this);
- var minimumFontRange = $('minimum-font-size');
- minimumFontRange.valueMap = [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
- 18, 20, 22, 24];
- minimumFontRange.continuous = false;
- minimumFontRange.notifyChange = this.minimumRangeChanged_.bind(this);
- minimumFontRange.notifyPrefChange =
- this.minimumFontSizeChanged_.bind(this);
- var placeholder = localStrings.getString('fontSettingsPlaceholder');
- var elements = [$('standard-font-family'), $('serif-font-family'),
- $('sans-serif-font-family'), $('fixed-font-family'),
- $('font-encoding')];
- elements.forEach(function(el) {
- el.appendChild(new Option(placeholder));
- el.setDisabled('noFontsAvailable', true);
- });
- },
- /**
- * Called by the options page when this page has been shown.
- */
- didShowPage: function() {
- // The fonts list may be large so we only load it when this page is
- // loaded for the first time. This makes opening the options window
- // faster and consume less memory if the user never opens the fonts
- // dialog.
- if (!this.hasShown) {
- chrome.send('fetchFontsData');
- this.hasShown = true;
- }
- },
- /**
- * Called as the user changes the standard font size. This allows for
- * reflecting the change in the UI before the preference has been changed.
- * @param {Element} el The slider input element.
- * @param {number} value The mapped value currently set by the slider.
- * @private
- */
- standardRangeChanged_: function(el, value) {
- var fontSampleEl = $('standard-font-sample');
- this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily,
- true);
- fontSampleEl = $('serif-font-sample');
- this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily,
- true);
- fontSampleEl = $('sans-serif-font-sample');
- this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily,
- true);
- fontSampleEl = $('fixed-font-sample');
- this.setUpFontSample_(fontSampleEl,
- value - SIZE_DIFFERENCE_FIXED_STANDARD,
- fontSampleEl.style.fontFamily, false);
- },
- /**
- * Sets the 'default_fixed_font_size' preference when the standard font
- * size has been changed by the user.
- * @param {Element} el The slider input element.
- * @param {number} value The mapped value that has been saved.
- * @private
- */
- standardFontSizeChanged_: function(el, value) {
- Preferences.setIntegerPref('webkit.webprefs.default_fixed_font_size',
- value - SIZE_DIFFERENCE_FIXED_STANDARD, '');
- },
- /**
- * Called as the user changes the miniumum font size. This allows for
- * reflecting the change in the UI before the preference has been changed.
- * @param {Element} el The slider input element.
- * @param {number} value The mapped value currently set by the slider.
- * @private
- */
- minimumRangeChanged_: function(el, value) {
- var fontSampleEl = $('minimum-font-sample');
- this.setUpFontSample_(fontSampleEl, value, fontSampleEl.style.fontFamily,
- true);
- },
- /**
- * Sets the 'minimum_logical_font_size' preference when the minimum font
- * size has been changed by the user.
- * @param {Element} el The slider input element.
- * @param {number} value The mapped value that has been saved.
- * @private
- */
- minimumFontSizeChanged_: function(el, value) {
- Preferences.setIntegerPref('webkit.webprefs.minimum_logical_font_size',
- value, '');
- },
- /**
- * Sets the text, font size and font family of the sample text.
- * @param {Element} el The div containing the sample text.
- * @param {number} size The font size of the sample text.
- * @param {string} font The font family of the sample text.
- * @param {bool} showSize True if the font size should appear in the sample.
- * @private
- */
- setUpFontSample_: function(el, size, font, showSize) {
- var prefix = showSize ? (size + ': ') : '';
- el.textContent = prefix +
- localStrings.getString('fontSettingsLoremIpsum');
- el.style.fontSize = size + 'px';
- if (font)
- el.style.fontFamily = font;
- },
- /**
- * Populates a select list and selects the specified item.
- * @param {Element} element The select element to populate.
- * @param {Array} items The array of items from which to populate.
- * @param {string} selectedValue The selected item.
- * @private
- */
- populateSelect_: function(element, items, selectedValue) {
- // Remove any existing content.
- element.textContent = '';
- // Insert new child nodes into select element.
- var value, text, selected, option;
- for (var i = 0; i < items.length; i++) {
- value = items[i][0];
- text = items[i][1];
- if (text) {
- selected = value == selectedValue;
- element.appendChild(new Option(text, value, false, selected));
- } else {
- element.appendChild(document.createElement('hr'));
- }
- }
- element.setDisabled('noFontsAvailable', false);
- }
- };
- // Chrome callbacks
- FontSettings.setFontsData = function(fonts, encodings, selectedValues) {
- FontSettings.getInstance().populateSelect_($('standard-font-family'), fonts,
- selectedValues[0]);
- FontSettings.getInstance().populateSelect_($('serif-font-family'), fonts,
- selectedValues[1]);
- FontSettings.getInstance().populateSelect_($('sans-serif-font-family'),
- fonts, selectedValues[2]);
- FontSettings.getInstance().populateSelect_($('fixed-font-family'), fonts,
- selectedValues[3]);
- FontSettings.getInstance().populateSelect_($('font-encoding'), encodings,
- selectedValues[4]);
- };
- FontSettings.setUpStandardFontSample = function(font, size) {
- FontSettings.getInstance().setUpFontSample_($('standard-font-sample'), size,
- font, true);
- };
- FontSettings.setUpSerifFontSample = function(font, size) {
- FontSettings.getInstance().setUpFontSample_($('serif-font-sample'), size,
- font, true);
- };
- FontSettings.setUpSansSerifFontSample = function(font, size) {
- FontSettings.getInstance().setUpFontSample_($('sans-serif-font-sample'),
- size, font, true);
- };
- FontSettings.setUpFixedFontSample = function(font, size) {
- FontSettings.getInstance().setUpFontSample_($('fixed-font-sample'),
- size, font, false);
- };
- FontSettings.setUpMinimumFontSample = function(size) {
- // If size is less than 6, represent it as six in the sample to account
- // for the minimum logical font size.
- if (size < 6)
- size = 6;
- FontSettings.getInstance().setUpFontSample_($('minimum-font-sample'), size,
- null, true);
- };
- // Export
- return {
- FontSettings: FontSettings
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- /////////////////////////////////////////////////////////////////////////////
- // HandlerOptions class:
- /**
- * Encapsulated handling of handler options page.
- * @constructor
- */
- function HandlerOptions() {
- this.activeNavTab = null;
- OptionsPage.call(this,
- 'handlers',
- templateData.handlersPageTabTitle,
- 'handler-options');
- }
- cr.addSingletonGetter(HandlerOptions);
- HandlerOptions.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * The handlers list.
- * @type {DeletableItemList}
- * @private
- */
- handlersList_: null,
- /** @inheritDoc */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- this.createHandlersList_();
- },
- /**
- * Creates, decorates and initializes the handlers list.
- * @private
- */
- createHandlersList_: function() {
- this.handlersList_ = $('handlers-list');
- options.HandlersList.decorate(this.handlersList_);
- this.handlersList_.autoExpands = true;
- this.ignoredHandlersList_ = $('ignored-handlers-list');
- options.IgnoredHandlersList.decorate(this.ignoredHandlersList_);
- this.ignoredHandlersList_.autoExpands = true;
- },
- };
- /**
- * Sets the list of handlers shown by the view.
- * @param handlers to be shown in the view.
- */
- HandlerOptions.setHandlers = function(handlers) {
- $('handlers-list').setHandlers(handlers);
- };
- /**
- * Sets the list of ignored handlers shown by the view.
- * @param handlers to be shown in the view.
- */
- HandlerOptions.setIgnoredHandlers = function(handlers) {
- $('ignored-handlers-section').hidden = handlers.length == 0;
- $('ignored-handlers-list').setHandlers(handlers);
- };
- return {
- HandlerOptions: HandlerOptions
- };
- });
- // 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() {
- const ArrayDataModel = cr.ui.ArrayDataModel;
- const List = cr.ui.List;
- const ListItem = cr.ui.ListItem;
- const HandlerOptions = options.HandlerOptions;
- const DeletableItem = options.DeletableItem;
- const DeletableItemList = options.DeletableItemList;
- const localStrings = new LocalStrings();
- /**
- * Creates a new ignored protocol / content handler list item.
- *
- * Accepts values in the form
- * ['mailto', 'http://www.thesite.com/%s', 'The title of the protocol'],
- * @param {Object} entry A dictionary describing the handlers for a given
- * protocol.
- * @constructor
- * @extends {cr.ui.DeletableItemList}
- */
- function IgnoredHandlersListItem(entry) {
- var el = cr.doc.createElement('div');
- el.dataItem = entry;
- el.__proto__ = IgnoredHandlersListItem.prototype;
- el.decorate();
- return el;
- }
- IgnoredHandlersListItem.prototype = {
- __proto__: DeletableItem.prototype,
- /** @inheritDoc */
- decorate: function() {
- DeletableItem.prototype.decorate.call(this);
- // Protocol.
- var protocolElement = document.createElement('div');
- protocolElement.textContent = this.dataItem[0];
- protocolElement.className = 'handlers-type-column';
- this.contentElement_.appendChild(protocolElement);
- // Site title.
- var titleElement = document.createElement('div');
- titleElement.textContent = this.dataItem[2];
- titleElement.className = 'handlers-site-column';
- titleElement.title = this.dataItem[1];
- this.contentElement_.appendChild(titleElement);
- },
- };
- var IgnoredHandlersList = cr.ui.define('list');
- IgnoredHandlersList.prototype = {
- __proto__: DeletableItemList.prototype,
- createItem: function(entry) {
- return new IgnoredHandlersListItem(entry);
- },
- deleteItemAtIndex: function(index) {
- chrome.send('removeIgnoredHandler', [this.dataModel.item(index)]);
- },
- /**
- * The length of the list.
- */
- get length() {
- return this.dataModel.length;
- },
- /**
- * Set the protocol handlers displayed by this list. See
- * IgnoredHandlersListItem for an example of the format the list should
- * take.
- *
- * @param {Object} list A list of ignored protocol handlers.
- */
- setHandlers: function(list) {
- this.dataModel = new ArrayDataModel(list);
- },
- };
- /**
- * Creates a new protocol / content handler list item.
- *
- * Accepts values in the form
- * { protocol: 'mailto',
- * handlers: [
- * ['mailto', 'http://www.thesite.com/%s', 'The title of the protocol'],
- * ...,
- * ],
- * }
- * @param {Object} entry A dictionary describing the handlers for a given
- * protocol.
- * @constructor
- * @extends {cr.ui.ListItem}
- */
- function HandlerListItem(entry) {
- var el = cr.doc.createElement('div');
- el.dataItem = entry;
- el.__proto__ = HandlerListItem.prototype;
- el.decorate();
- return el;
- }
- HandlerListItem.prototype = {
- __proto__: ListItem.prototype,
- buildWidget_: function(data, delegate) {
- // Protocol.
- var protocolElement = document.createElement('div');
- protocolElement.textContent = data.protocol;
- protocolElement.className = 'handlers-type-column';
- this.appendChild(protocolElement);
- // Handler selection.
- var handlerElement = document.createElement('div');
- var selectElement = document.createElement('select');
- var defaultOptionElement = document.createElement('option');
- defaultOptionElement.selected = data.default_handler == -1;
- defaultOptionElement.textContent =
- localStrings.getString('handlers_none_handler');
- defaultOptionElement.value = -1;
- selectElement.appendChild(defaultOptionElement);
- for (var i = 0; i < data.handlers.length; ++i) {
- var optionElement = document.createElement('option');
- optionElement.selected = i == data.default_handler;
- optionElement.textContent = data.handlers[i][2];
- optionElement.value = i;
- selectElement.appendChild(optionElement);
- }
- selectElement.addEventListener('change', function (e) {
- var index = e.target.value;
- if (index == -1) {
- this.classList.add('none');
- delegate.clearDefault(data.protocol);
- } else {
- handlerElement.classList.remove('none');
- delegate.setDefault(data.handlers[index]);
- }
- });
- handlerElement.appendChild(selectElement);
- handlerElement.className = 'handlers-site-column';
- if (data.default_handler == -1)
- this.classList.add('none');
- this.appendChild(handlerElement);
- // Remove link.
- var removeElement = document.createElement('div');
- removeElement.textContent =
- localStrings.getString('handlers_remove_link');
- removeElement.addEventListener('click', function (e) {
- var value = selectElement ? selectElement.value : 0;
- delegate.removeHandler(value, data.handlers[value]);
- });
- removeElement.className = 'handlers-remove-column handlers-remove-link';
- this.appendChild(removeElement);
- },
- /** @inheritDoc */
- decorate: function() {
- ListItem.prototype.decorate.call(this);
- var self = this;
- var delegate = {
- removeHandler: function(index, handler) {
- chrome.send('removeHandler', [handler]);
- },
- setDefault: function(handler) {
- chrome.send('setDefault', [handler]);
- },
- clearDefault: function(protocol) {
- chrome.send('clearDefault', [protocol]);
- },
- };
- this.buildWidget_(this.dataItem, delegate);
- },
- };
- /**
- * Create a new passwords list.
- * @constructor
- * @extends {cr.ui.List}
- */
- var HandlersList = cr.ui.define('list');
- HandlersList.prototype = {
- __proto__: List.prototype,
- /** @inheritDoc */
- createItem: function(entry) {
- return new HandlerListItem(entry);
- },
- /**
- * The length of the list.
- */
- get length() {
- return this.dataModel.length;
- },
- /**
- * Set the protocol handlers displayed by this list.
- * See HandlerListItem for an example of the format the list should take.
- *
- * @param {Object} list A list of protocols with their registered handlers.
- */
- setHandlers: function(list) {
- this.dataModel = new ArrayDataModel(list);
- },
- };
- return {
- IgnoredHandlersListItem: IgnoredHandlersListItem,
- IgnoredHandlersList: IgnoredHandlersList,
- HandlerListItem: HandlerListItem,
- HandlersList: HandlersList,
- };
- });
- // 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 OptionsPage = options.OptionsPage;
- /**
- * ImportDataOverlay class
- * Encapsulated handling of the 'Import Data' overlay page.
- * @class
- */
- function ImportDataOverlay() {
- OptionsPage.call(this,
- 'importData',
- templateData.importDataOverlayTabTitle,
- 'import-data-overlay');
- }
- cr.addSingletonGetter(ImportDataOverlay);
- ImportDataOverlay.prototype = {
- // Inherit from OptionsPage.
- __proto__: OptionsPage.prototype,
- /**
- * Initialize the page.
- */
- initializePage: function() {
- // Call base class implementation to start preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- var self = this;
- var checkboxes =
- document.querySelectorAll('#import-checkboxes input[type=checkbox]');
- for (var i = 0; i < checkboxes.length; i++) {
- checkboxes[i].onchange = function() {
- self.validateCommitButton_();
- };
- }
- $('import-browsers').onchange = function() {
- self.updateCheckboxes_();
- self.validateCommitButton_();
- };
- $('import-data-commit').onclick = function() {
- chrome.send('importData', [
- String($('import-browsers').selectedIndex),
- String($('import-history').checked),
- String($('import-favorites').checked),
- String($('import-passwords').checked),
- String($('import-search').checked)]);
- };
- $('import-data-cancel').onclick = function() {
- ImportDataOverlay.dismiss();
- };
- $('import-data-show-bookmarks-bar').onchange = function() {
- // Note: The callback 'toggleShowBookmarksBar' is handled within the
- // browser options handler -- rather than the import data handler --
- // as the implementation is shared by several clients.
- chrome.send('toggleShowBookmarksBar');
- }
- $('import-data-confirm').onclick = function() {
- ImportDataOverlay.dismiss();
- };
- // Form controls are disabled until the profile list has been loaded.
- self.setControlsSensitive_(false);
- },
- /**
- * Set enabled and checked state of the commit button.
- * @private
- */
- validateCommitButton_: function() {
- var somethingToImport =
- $('import-history').checked || $('import-favorites').checked ||
- $('import-passwords').checked || $('import-search').checked;
- $('import-data-commit').disabled = !somethingToImport;
- },
- /**
- * Sets the sensitivity of all the checkboxes and the commit button.
- * @private
- */
- setControlsSensitive_: function(sensitive) {
- var checkboxes =
- document.querySelectorAll('#import-checkboxes input[type=checkbox]');
- for (var i = 0; i < checkboxes.length; i++)
- this.setUpCheckboxState_(checkboxes[i], sensitive);
- $('import-data-commit').disabled = !sensitive;
- },
- /**
- * Set enabled and checked states a checkbox element.
- * @param {Object} checkbox A checkbox element.
- * @param {boolean} enabled The enabled state of the chekbox.
- * @private
- */
- setUpCheckboxState_: function(checkbox, enabled) {
- checkbox.setDisabled("noProfileData", !enabled);
- },
- /**
- * Update the enabled and checked states of all checkboxes.
- * @private
- */
- updateCheckboxes_: function() {
- var index = $('import-browsers').selectedIndex;
- var browserProfile;
- if (this.browserProfiles.length > index)
- browserProfile = this.browserProfiles[index];
- var importOptions = ['history', 'favorites', 'passwords', 'search'];
- for (var i = 0; i < importOptions.length; i++) {
- var checkbox = $('import-' + importOptions[i]);
- var enable = browserProfile && browserProfile[importOptions[i]];
- this.setUpCheckboxState_(checkbox, enable);
- }
- },
- /**
- * Update the supported browsers popup with given entries.
- * @param {array} browsers List of supported browsers name.
- * @private
- */
- updateSupportedBrowsers_: function(browsers) {
- this.browserProfiles = browsers;
- var browserSelect = $('import-browsers');
- browserSelect.remove(0); // Remove the 'Loading...' option.
- browserSelect.textContent = '';
- var browserCount = browsers.length;
- if (browserCount == 0) {
- var option = new Option(templateData.noProfileFound, 0);
- browserSelect.appendChild(option);
- this.setControlsSensitive_(false);
- } else {
- this.setControlsSensitive_(true);
- for (var i = 0; i < browserCount; i++) {
- var browser = browsers[i]
- var option = new Option(browser['name'], browser['index']);
- browserSelect.appendChild(option);
- }
- this.updateCheckboxes_();
- this.validateCommitButton_();
- }
- },
- /**
- * Clear import prefs set when user checks/unchecks a checkbox so that each
- * checkbox goes back to the default "checked" state (or alternatively, to
- * the state set by a recommended policy).
- * @private
- */
- clearUserPrefs_: function() {
- var importPrefs = ['import_history',
- 'import_bookmarks',
- 'import_saved_passwords',
- 'import_search_engine'];
- for (var i = 0; i < importPrefs.length; i++)
- Preferences.clearPref(importPrefs[i], undefined);
- },
- };
- ImportDataOverlay.clearUserPrefs = function() {
- ImportDataOverlay.getInstance().clearUserPrefs_();
- };
- /**
- * Update the supported browsers popup with given entries.
- * @param {array} list of supported browsers name.
- */
- ImportDataOverlay.updateSupportedBrowsers = function(browsers) {
- ImportDataOverlay.getInstance().updateSupportedBrowsers_(browsers);
- };
- /**
- * Update the UI to reflect whether an import operation is in progress.
- * @param {boolean} state True if an import operation is in progress.
- */
- ImportDataOverlay.setImportingState = function(state) {
- var checkboxes =
- document.querySelectorAll('#import-checkboxes input[type=checkbox]');
- for (var i = 0; i < checkboxes.length; i++)
- checkboxes[i].setDisabled("Importing", state);
- if (!state)
- ImportDataOverlay.getInstance().updateCheckboxes_();
- $('import-browsers').disabled = state;
- $('import-throbber').style.visibility = state ? "visible" : "hidden";
- ImportDataOverlay.getInstance().validateCommitButton_();
- };
- /**
- * Remove the import overlay from display.
- */
- ImportDataOverlay.dismiss = function() {
- ImportDataOverlay.clearUserPrefs();
- OptionsPage.closeOverlay();
- };
- /**
- * Show a message confirming the success of the import operation.
- */
- ImportDataOverlay.confirmSuccess = function() {
- var showBookmarksMessage = $('import-favorites').checked;
- ImportDataOverlay.setImportingState(false);
- $('import-data-configure').hidden = true;
- $('import-data-success').hidden = false;
- $('import-find-your-bookmarks').hidden = !showBookmarksMessage;
- };
- // Export
- return {
- ImportDataOverlay: ImportDataOverlay
- };
- });
- // 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 OptionsPage = options.OptionsPage;
- function InstantConfirmOverlay() {
- OptionsPage.call(this, 'instantConfirm',
- templateData.instantConfirmTitle,
- 'instantConfirmOverlay');
- };
- cr.addSingletonGetter(InstantConfirmOverlay);
- InstantConfirmOverlay.prototype = {
- // Inherit from OptionsPage.
- __proto__: OptionsPage.prototype,
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- $('instantConfirmCancel').onclick = function() {
- OptionsPage.closeOverlay();
- $('instantEnabledCheckbox').checked = false;
- };
- $('instantConfirmOk').onclick = function() {
- OptionsPage.closeOverlay();
- chrome.send('enableInstant');
- };
- },
- };
- // Export
- return {
- InstantConfirmOverlay: InstantConfirmOverlay
- };
- });
- // 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.
- ///////////////////////////////////////////////////////////////////////////////
- // AddLanguageOverlay class:
- cr.define('options', function() {
- const OptionsPage = options.OptionsPage;
- /**
- * Encapsulated handling of ChromeOS add language overlay page.
- * @constructor
- */
- function AddLanguageOverlay() {
- OptionsPage.call(this, 'addLanguage',
- localStrings.getString('add_button'),
- 'add-language-overlay-page');
- }
- cr.addSingletonGetter(AddLanguageOverlay);
- AddLanguageOverlay.prototype = {
- // Inherit AddLanguageOverlay from OptionsPage.
- __proto__: OptionsPage.prototype,
- /**
- * Initializes AddLanguageOverlay page.
- * Calls base class implementation to starts preference initialization.
- */
- initializePage: function() {
- // Call base class implementation to starts preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- // Set up the cancel button.
- $('add-language-overlay-cancel-button').onclick = function(e) {
- OptionsPage.closeOverlay();
- };
- // Create the language list with which users can add a language.
- var addLanguageList = $('add-language-overlay-language-list');
- var languageListData = templateData.languageList;
- for (var i = 0; i < languageListData.length; i++) {
- var language = languageListData[i];
- var displayText = language.displayName;
- // If the native name is different, add it.
- if (language.displayName != language.nativeDisplayName) {
- displayText += ' - ' + language.nativeDisplayName;
- }
- if (cr.isChromeOS) {
- var button = document.createElement('button');
- button.className = 'link-button';
- button.textContent = displayText;
- button.languageCode = language.code;
- var li = document.createElement('li');
- li.languageCode = language.code;
- li.appendChild(button);
- addLanguageList.appendChild(li);
- } else {
- var option = document.createElement('option');
- option.value = language.code;
- option.textContent = displayText;
- addLanguageList.appendChild(option);
- }
- }
- },
- };
- return {
- AddLanguageOverlay: AddLanguageOverlay
- };
- });
- // 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() {
- const ArrayDataModel = cr.ui.ArrayDataModel;
- const DeletableItem = options.DeletableItem;
- const DeletableItemList = options.DeletableItemList;
- const List = cr.ui.List;
- const ListItem = cr.ui.ListItem;
- const ListSingleSelectionModel = cr.ui.ListSingleSelectionModel;
- /**
- * Creates a new Language list item.
- * @param {String} languageCode the languageCode.
- * @constructor
- * @extends {DeletableItem.ListItem}
- */
- function LanguageListItem(languageCode) {
- var el = cr.doc.createElement('li');
- el.__proto__ = LanguageListItem.prototype;
- el.languageCode_ = languageCode;
- el.decorate();
- return el;
- };
- LanguageListItem.prototype = {
- __proto__: DeletableItem.prototype,
- /**
- * The language code of this language.
- * @type {String}
- * @private
- */
- languageCode_: null,
- /** @inheritDoc */
- decorate: function() {
- DeletableItem.prototype.decorate.call(this);
- var languageCode = this.languageCode_;
- var languageOptions = options.LanguageOptions.getInstance();
- this.deletable = languageOptions.languageIsDeletable(languageCode);
- this.languageCode = languageCode;
- this.languageName = cr.doc.createElement('div');
- this.languageName.className = 'language-name';
- this.languageName.textContent =
- LanguageList.getDisplayNameFromLanguageCode(languageCode);
- this.contentElement.appendChild(this.languageName);
- this.title =
- LanguageList.getNativeDisplayNameFromLanguageCode(languageCode);
- this.draggable = true;
- },
- };
- /**
- * Creates a new language list.
- * @param {Object=} opt_propertyBag Optional properties.
- * @constructor
- * @extends {cr.ui.List}
- */
- var LanguageList = cr.ui.define('list');
- /**
- * Gets display name from the given language code.
- * @param {string} languageCode Language code (ex. "fr").
- */
- LanguageList.getDisplayNameFromLanguageCode = function(languageCode) {
- // Build the language code to display name dictionary at first time.
- if (!this.languageCodeToDisplayName_) {
- this.languageCodeToDisplayName_ = {};
- var languageList = templateData.languageList;
- for (var i = 0; i < languageList.length; i++) {
- var language = languageList[i];
- this.languageCodeToDisplayName_[language.code] = language.displayName;
- }
- }
- return this.languageCodeToDisplayName_[languageCode];
- }
- /**
- * Gets native display name from the given language code.
- * @param {string} languageCode Language code (ex. "fr").
- */
- LanguageList.getNativeDisplayNameFromLanguageCode = function(languageCode) {
- // Build the language code to display name dictionary at first time.
- if (!this.languageCodeToNativeDisplayName_) {
- this.languageCodeToNativeDisplayName_ = {};
- var languageList = templateData.languageList;
- for (var i = 0; i < languageList.length; i++) {
- var language = languageList[i];
- this.languageCodeToNativeDisplayName_[language.code] =
- language.nativeDisplayName;
- }
- }
- return this.languageCodeToNativeDisplayName_[languageCode];
- }
- /**
- * Returns true if the given language code is valid.
- * @param {string} languageCode Language code (ex. "fr").
- */
- LanguageList.isValidLanguageCode = function(languageCode) {
- // Having the display name for the language code means that the
- // language code is valid.
- if (LanguageList.getDisplayNameFromLanguageCode(languageCode)) {
- return true;
- }
- return false;
- }
- LanguageList.prototype = {
- __proto__: DeletableItemList.prototype,
- // The list item being dragged.
- draggedItem: null,
- // The drop position information: "below" or "above".
- dropPos: null,
- // The preference is a CSV string that describes preferred languages
- // in Chrome OS. The language list is used for showing the language
- // list in "Language and Input" options page.
- preferredLanguagesPref: 'settings.language.preferred_languages',
- // The preference is a CSV string that describes accept languages used
- // for content negotiation. To be more precise, the list will be used
- // in "Accept-Language" header in HTTP requests.
- acceptLanguagesPref: 'intl.accept_languages',
- /** @inheritDoc */
- decorate: function() {
- DeletableItemList.prototype.decorate.call(this);
- this.selectionModel = new ListSingleSelectionModel;
- // HACK(arv): http://crbug.com/40902
- window.addEventListener('resize', this.redraw.bind(this));
- // Listen to pref change.
- if (cr.isChromeOS) {
- Preferences.getInstance().addEventListener(this.preferredLanguagesPref,
- this.handlePreferredLanguagesPrefChange_.bind(this));
- } else {
- Preferences.getInstance().addEventListener(this.acceptLanguagesPref,
- this.handleAcceptLanguagesPrefChange_.bind(this));
- }
- // Listen to drag and drop events.
- this.addEventListener('dragstart', this.handleDragStart_.bind(this));
- this.addEventListener('dragenter', this.handleDragEnter_.bind(this));
- this.addEventListener('dragover', this.handleDragOver_.bind(this));
- this.addEventListener('drop', this.handleDrop_.bind(this));
- this.addEventListener('dragleave', this.handleDragLeave_.bind(this));
- },
- createItem: function(languageCode) {
- return new LanguageListItem(languageCode);
- },
- /*
- * For each item, determines whether it's deletable.
- */
- updateDeletable: function() {
- var items = this.items;
- for (var i = 0; i < items.length; ++i) {
- var item = items[i];
- var languageCode = item.languageCode;
- var languageOptions = options.LanguageOptions.getInstance();
- item.deletable = languageOptions.languageIsDeletable(languageCode);
- }
- },
- /*
- * Adds a language to the language list.
- * @param {string} languageCode language code (ex. "fr").
- */
- addLanguage: function(languageCode) {
- // It shouldn't happen but ignore the language code if it's
- // null/undefined, or already present.
- if (!languageCode || this.dataModel.indexOf(languageCode) >= 0) {
- return;
- }
- this.dataModel.push(languageCode);
- // Select the last item, which is the language added.
- this.selectionModel.selectedIndex = this.dataModel.length - 1;
- this.savePreference_();
- },
- /*
- * Gets the language codes of the currently listed languages.
- */
- getLanguageCodes: function() {
- return this.dataModel.slice();
- },
- /*
- * Gets the language code of the selected language.
- */
- getSelectedLanguageCode: function() {
- return this.selectedItem;
- },
- /*
- * Selects the language by the given language code.
- * @returns {boolean} True if the operation is successful.
- */
- selectLanguageByCode: function(languageCode) {
- var index = this.dataModel.indexOf(languageCode);
- if (index >= 0) {
- this.selectionModel.selectedIndex = index;
- return true;
- }
- return false;
- },
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- if (index >= 0) {
- this.dataModel.splice(index, 1);
- // Once the selected item is removed, there will be no selected item.
- // Select the item pointed by the lead index.
- index = this.selectionModel.leadIndex;
- this.savePreference_();
- }
- return index;
- },
- /*
- * Computes the target item of drop event.
- * @param {Event} e The drop or dragover event.
- * @private
- */
- getTargetFromDropEvent_ : function(e) {
- var target = e.target;
- // e.target may be an inner element of the list item
- while (target != null && !(target instanceof ListItem)) {
- target = target.parentNode;
- }
- return target;
- },
- /*
- * Handles the dragstart event.
- * @param {Event} e The dragstart event.
- * @private
- */
- handleDragStart_: function(e) {
- var target = e.target;
- // ListItem should be the only draggable element type in the page,
- // but just in case.
- if (target instanceof ListItem) {
- this.draggedItem = target;
- e.dataTransfer.effectAllowed = 'move';
- // We need to put some kind of data in the drag or it will be
- // ignored. Use the display name in case the user drags to a text
- // field or the desktop.
- e.dataTransfer.setData('text/plain', target.title);
- }
- },
- /*
- * Handles the dragenter event.
- * @param {Event} e The dragenter event.
- * @private
- */
- handleDragEnter_: function(e) {
- e.preventDefault();
- },
- /*
- * Handles the dragover event.
- * @param {Event} e The dragover event.
- * @private
- */
- handleDragOver_: function(e) {
- var dropTarget = this.getTargetFromDropEvent_(e);
- // Determines whether the drop target is to accept the drop.
- // The drop is only successful on another ListItem.
- if (!(dropTarget instanceof ListItem) ||
- dropTarget == this.draggedItem) {
- this.hideDropMarker_();
- return;
- }
- // Compute the drop postion. Should we move the dragged item to
- // below or above the drop target?
- var rect = dropTarget.getBoundingClientRect();
- var dy = e.clientY - rect.top;
- var yRatio = dy / rect.height;
- var dropPos = yRatio <= .5 ? 'above' : 'below';
- this.dropPos = dropPos;
- this.showDropMarker_(dropTarget, dropPos);
- e.preventDefault();
- },
- /*
- * Handles the drop event.
- * @param {Event} e The drop event.
- * @private
- */
- handleDrop_: function(e) {
- var dropTarget = this.getTargetFromDropEvent_(e);
- this.hideDropMarker_();
- // Delete the language from the original position.
- var languageCode = this.draggedItem.languageCode;
- var originalIndex = this.dataModel.indexOf(languageCode);
- this.dataModel.splice(originalIndex, 1);
- // Insert the language to the new position.
- var newIndex = this.dataModel.indexOf(dropTarget.languageCode);
- if (this.dropPos == 'below')
- newIndex += 1;
- this.dataModel.splice(newIndex, 0, languageCode);
- // The cursor should move to the moved item.
- this.selectionModel.selectedIndex = newIndex;
- // Save the preference.
- this.savePreference_();
- },
- /*
- * Handles the dragleave event.
- * @param {Event} e The dragleave event
- * @private
- */
- handleDragLeave_ : function(e) {
- this.hideDropMarker_();
- },
- /*
- * Shows and positions the marker to indicate the drop target.
- * @param {HTMLElement} target The current target list item of drop
- * @param {string} pos 'below' or 'above'
- * @private
- */
- showDropMarker_ : function(target, pos) {
- window.clearTimeout(this.hideDropMarkerTimer_);
- var marker = $('language-options-list-dropmarker');
- var rect = target.getBoundingClientRect();
- var markerHeight = 8;
- if (pos == 'above') {
- marker.style.top = (rect.top - markerHeight/2) + 'px';
- } else {
- marker.style.top = (rect.bottom - markerHeight/2) + 'px';
- }
- marker.style.width = rect.width + 'px';
- marker.style.left = rect.left + 'px';
- marker.style.display = 'block';
- },
- /*
- * Hides the drop marker.
- * @private
- */
- hideDropMarker_ : function() {
- // Hide the marker in a timeout to reduce flickering as we move between
- // valid drop targets.
- window.clearTimeout(this.hideDropMarkerTimer_);
- this.hideDropMarkerTimer_ = window.setTimeout(function() {
- $('language-options-list-dropmarker').style.display = '';
- }, 100);
- },
- /**
- * Handles preferred languages pref change.
- * @param {Event} e The change event object.
- * @private
- */
- handlePreferredLanguagesPrefChange_: function(e) {
- var languageCodesInCsv = e.value.value;
- var languageCodes = languageCodesInCsv.split(',');
- // Add the UI language to the initial list of languages. This is to avoid
- // a bug where the UI language would be removed from the preferred
- // language list by sync on first login.
- // See: crosbug.com/14283
- languageCodes.push(navigator.language);
- languageCodes = this.filterBadLanguageCodes_(languageCodes);
- this.load_(languageCodes);
- },
- /**
- * Handles accept languages pref change.
- * @param {Event} e The change event object.
- * @private
- */
- handleAcceptLanguagesPrefChange_: function(e) {
- var languageCodesInCsv = e.value.value;
- var languageCodes = this.filterBadLanguageCodes_(
- languageCodesInCsv.split(','));
- this.load_(languageCodes);
- },
- /**
- * Loads given language list.
- * @param {Array} languageCodes List of language codes.
- * @private
- */
- load_: function(languageCodes) {
- // Preserve the original selected index. See comments below.
- var originalSelectedIndex = (this.selectionModel ?
- this.selectionModel.selectedIndex : -1);
- this.dataModel = new ArrayDataModel(languageCodes);
- if (originalSelectedIndex >= 0 &&
- originalSelectedIndex < this.dataModel.length) {
- // Restore the original selected index if the selected index is
- // valid after the data model is loaded. This is neeeded to keep
- // the selected language after the languge is added or removed.
- this.selectionModel.selectedIndex = originalSelectedIndex;
- // The lead index should be updated too.
- this.selectionModel.leadIndex = originalSelectedIndex;
- } else if (this.dataModel.length > 0){
- // Otherwise, select the first item if it's not empty.
- // Note that ListSingleSelectionModel won't select an item
- // automatically, hence we manually select the first item here.
- this.selectionModel.selectedIndex = 0;
- }
- },
- /**
- * Saves the preference.
- */
- savePreference_: function() {
- // Encode the language codes into a CSV string.
- if (cr.isChromeOS)
- Preferences.setStringPref(this.preferredLanguagesPref,
- this.dataModel.slice().join(','));
- // Save the same language list as accept languages preference as
- // well, but we need to expand the language list, to make it more
- // acceptable. For instance, some web sites don't understand 'en-US'
- // but 'en'. See crosbug.com/9884.
- var acceptLanguages = this.expandLanguageCodes(this.dataModel.slice());
- Preferences.setStringPref(this.acceptLanguagesPref,
- acceptLanguages.join(','));
- cr.dispatchSimpleEvent(this, 'save');
- },
- /**
- * Expands language codes to make these more suitable for Accept-Language.
- * Example: ['en-US', 'ja', 'en-CA'] => ['en-US', 'en', 'ja', 'en-CA'].
- * 'en' won't appear twice as this function eliminates duplicates.
- * @param {Array} languageCodes List of language codes.
- * @private
- */
- expandLanguageCodes: function(languageCodes) {
- var expandedLanguageCodes = [];
- var seen = {}; // Used to eliminiate duplicates.
- for (var i = 0; i < languageCodes.length; i++) {
- var languageCode = languageCodes[i];
- if (!(languageCode in seen)) {
- expandedLanguageCodes.push(languageCode);
- seen[languageCode] = true;
- }
- var parts = languageCode.split('-');
- if (!(parts[0] in seen)) {
- expandedLanguageCodes.push(parts[0]);
- seen[parts[0]] = true;
- }
- }
- return expandedLanguageCodes;
- },
- /**
- * Filters bad language codes in case bad language codes are
- * stored in the preference. Removes duplicates as well.
- * @param {Array} languageCodes List of language codes.
- * @private
- */
- filterBadLanguageCodes_: function(languageCodes) {
- var filteredLanguageCodes = [];
- var seen = {};
- for (var i = 0; i < languageCodes.length; i++) {
- // Check if the the language code is valid, and not
- // duplicate. Otherwise, skip it.
- if (LanguageList.isValidLanguageCode(languageCodes[i]) &&
- !(languageCodes[i] in seen)) {
- filteredLanguageCodes.push(languageCodes[i]);
- seen[languageCodes[i]] = true;
- }
- }
- return filteredLanguageCodes;
- },
- };
- return {
- LanguageList: LanguageList,
- LanguageListItem: LanguageListItem
- };
- });
- // 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.
- // TODO(kochi): Generalize the notification as a component and put it
- // in js/cr/ui/notification.js .
- cr.define('options', function() {
- const OptionsPage = options.OptionsPage;
- const LanguageList = options.LanguageList;
- // Some input methods like Chinese Pinyin have config pages.
- // This is the map of the input method names to their config page names.
- const INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME = {
- 'mozc': 'languageMozc',
- 'mozc-chewing': 'languageChewing',
- 'mozc-dv': 'languageMozc',
- 'mozc-hangul': 'languageHangul',
- 'mozc-jp': 'languageMozc',
- 'pinyin': 'languagePinyin',
- 'pinyin-dv': 'languagePinyin',
- };
- /////////////////////////////////////////////////////////////////////////////
- // LanguageOptions class:
- /**
- * Encapsulated handling of ChromeOS language options page.
- * @constructor
- */
- function LanguageOptions(model) {
- OptionsPage.call(this, 'languages', templateData.languagePageTabTitle,
- 'languagePage');
- }
- cr.addSingletonGetter(LanguageOptions);
- // Inherit LanguageOptions from OptionsPage.
- LanguageOptions.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * Initializes LanguageOptions page.
- * Calls base class implementation to starts preference initialization.
- */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- var languageOptionsList = $('language-options-list');
- LanguageList.decorate(languageOptionsList);
- languageOptionsList.addEventListener('change',
- this.handleLanguageOptionsListChange_.bind(this));
- languageOptionsList.addEventListener('save',
- this.handleLanguageOptionsListSave_.bind(this));
- this.addEventListener('visibleChange',
- this.handleVisibleChange_.bind(this));
- if (cr.isChromeOS) {
- this.initializeInputMethodList_();
- this.initializeLanguageCodeToInputMethodIdsMap_();
- }
- Preferences.getInstance().addEventListener(this.spellCheckDictionaryPref,
- this.handleSpellCheckDictionaryPrefChange_.bind(this));
- // Set up add button.
- $('language-options-add-button').onclick = function(e) {
- // Add the language without showing the overlay if it's specified in
- // the URL hash (ex. lang_add=ja). Used for automated testing.
- var match = document.location.hash.match(/\blang_add=([\w-]+)/);
- if (match) {
- var addLanguageCode = match[1];
- $('language-options-list').addLanguage(addLanguageCode);
- } else {
- OptionsPage.navigateToPage('addLanguage');
- }
- };
- if (cr.isChromeOS) {
- // Listen to user clicks on the add language list.
- var addLanguageList = $('add-language-overlay-language-list');
- addLanguageList.addEventListener('click',
- this.handleAddLanguageListClick_.bind(this));
- } else {
- // Listen to add language dialog ok button.
- var addLanguageOkButton = $('add-language-overlay-ok-button');
- addLanguageOkButton.addEventListener('click',
- this.handleAddLanguageOkButtonClick_.bind(this));
- // Show experimental features if enabled.
- if (templateData.experimentalSpellCheckFeatures == 'true')
- $('auto-spell-correction-option').hidden = false;
- // Handle spell check enable/disable.
- Preferences.getInstance().addEventListener(this.enableSpellCheckPref,
- this.updateEnableSpellCheck_.bind(this));
- }
- // Listen to user clicks on the "Change touch keyboard settings..."
- // button (if it exists).
- var virtualKeyboardButton = $('language-options-virtual-keyboard');
- if (virtualKeyboardButton) {
- // TODO(yusukes): would be better to hide the button if no virtual
- // keyboard is registered.
- virtualKeyboardButton.onclick = function(e) {
- OptionsPage.navigateToPage('virtualKeyboards');
- };
- }
- },
- // The preference is a boolean that enables/disables spell checking.
- enableSpellCheckPref: 'browser.enable_spellchecking',
- // The preference is a CSV string that describes preload engines
- // (i.e. active input methods).
- preloadEnginesPref: 'settings.language.preload_engines',
- // The list of preload engines, like ['mozc', 'pinyin'].
- preloadEngines_: [],
- // The preference is a string that describes the spell check
- // dictionary language, like "en-US".
- spellCheckDictionaryPref: 'spellcheck.dictionary',
- spellCheckDictionary_: "",
- // The map of language code to input method IDs, like:
- // {'ja': ['mozc', 'mozc-jp'], 'zh-CN': ['pinyin'], ...}
- languageCodeToInputMethodIdsMap_: {},
- /**
- * Initializes the input method list.
- */
- initializeInputMethodList_: function() {
- var inputMethodList = $('language-options-input-method-list');
- var inputMethodListData = templateData.inputMethodList;
- // Add all input methods, but make all of them invisible here. We'll
- // change the visibility in handleLanguageOptionsListChange_() based
- // on the selected language. Note that we only have less than 100
- // input methods, so creating DOM nodes at once here should be ok.
- for (var i = 0; i < inputMethodListData.length; i++) {
- var inputMethod = inputMethodListData[i];
- var input = document.createElement('input');
- input.type = 'checkbox';
- input.inputMethodId = inputMethod.id;
- // Listen to user clicks.
- input.addEventListener('click',
- this.handleCheckboxClick_.bind(this));
- var label = document.createElement('label');
- label.appendChild(input);
- // Adding a space between the checkbox and the text. This is a bit
- // dirty, but we rely on a space character for all other checkboxes.
- label.appendChild(document.createTextNode(
- ' ' + inputMethod.displayName));
- label.style.display = 'none';
- label.languageCodeSet = inputMethod.languageCodeSet;
- // Add the configure button if the config page is present for this
- // input method.
- if (inputMethod.id in INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME) {
- var pageName = INPUT_METHOD_ID_TO_CONFIG_PAGE_NAME[inputMethod.id];
- var button = this.createConfigureInputMethodButton_(inputMethod.id,
- pageName);
- label.appendChild(button);
- }
- inputMethodList.appendChild(label);
- }
- // Listen to pref change once the input method list is initialized.
- Preferences.getInstance().addEventListener(this.preloadEnginesPref,
- this.handlePreloadEnginesPrefChange_.bind(this));
- },
- /**
- * Creates a configure button for the given input method ID.
- * @param {string} inputMethodId Input method ID (ex. "pinyin").
- * @param {string} pageName Name of the config page (ex. "languagePinyin").
- * @private
- */
- createConfigureInputMethodButton_: function(inputMethodId, pageName) {
- var button = document.createElement('button');
- button.textContent = localStrings.getString('configure');
- button.onclick = function(e) {
- // Prevent the default action (i.e. changing the checked property
- // of the checkbox). The button click here should not be handled
- // as checkbox click.
- e.preventDefault();
- chrome.send('inputMethodOptionsOpen', [inputMethodId]);
- OptionsPage.navigateToPage(pageName);
- }
- return button;
- },
- /**
- * Handles OptionsPage's visible property change event.
- * @param {Event} e Property change event.
- * @private
- */
- handleVisibleChange_: function(e) {
- if (this.visible) {
- $('language-options-list').redraw();
- chrome.send('languageOptionsOpen');
- }
- },
- /**
- * Handles languageOptionsList's change event.
- * @param {Event} e Change event.
- * @private
- */
- handleLanguageOptionsListChange_: function(e) {
- var languageOptionsList = $('language-options-list');
- var languageCode = languageOptionsList.getSelectedLanguageCode();
- // Select the language if it's specified in the URL hash (ex. lang=ja).
- // Used for automated testing.
- var match = document.location.hash.match(/\blang=([\w-]+)/);
- if (match) {
- var specifiedLanguageCode = match[1];
- if (languageOptionsList.selectLanguageByCode(specifiedLanguageCode)) {
- languageCode = specifiedLanguageCode;
- }
- }
- this.updateSelectedLanguageName_(languageCode);
- if (cr.isWindows || cr.isChromeOS)
- this.updateUiLanguageButton_(languageCode);
- this.updateSpellCheckLanguageButton_(languageCode);
- if (cr.isChromeOS)
- this.updateInputMethodList_(languageCode);
- this.updateLanguageListInAddLanguageOverlay_();
- },
- /**
- * Handles languageOptionsList's save event.
- * @param {Event} e Save event.
- * @private
- */
- handleLanguageOptionsListSave_: function(e) {
- if (cr.isChromeOS) {
- // Sort the preload engines per the saved languages before save.
- this.preloadEngines_ = this.sortPreloadEngines_(this.preloadEngines_);
- this.savePreloadEnginesPref_();
- }
- },
- /**
- * Sorts preloadEngines_ by languageOptionsList's order.
- * @param {Array} preloadEngines List of preload engines.
- * @return {Array} Returns sorted preloadEngines.
- * @private
- */
- sortPreloadEngines_: function(preloadEngines) {
- // For instance, suppose we have two languages and associated input
- // methods:
- //
- // - Korean: hangul
- // - Chinese: pinyin
- //
- // The preloadEngines preference should look like "hangul,pinyin".
- // If the user reverse the order, the preference should be reorderd
- // to "pinyin,hangul".
- var languageOptionsList = $('language-options-list');
- var languageCodes = languageOptionsList.getLanguageCodes();
- // Convert the list into a dictonary for simpler lookup.
- var preloadEngineSet = {};
- for (var i = 0; i < preloadEngines.length; i++) {
- preloadEngineSet[preloadEngines[i]] = true;
- }
- // Create the new preload engine list per the language codes.
- var newPreloadEngines = [];
- for (var i = 0; i < languageCodes.length; i++) {
- var languageCode = languageCodes[i];
- var inputMethodIds = this.languageCodeToInputMethodIdsMap_[
- languageCode];
- // Check if we have active input methods associated with the language.
- for (var j = 0; j < inputMethodIds.length; j++) {
- var inputMethodId = inputMethodIds[j];
- if (inputMethodId in preloadEngineSet) {
- // If we have, add it to the new engine list.
- newPreloadEngines.push(inputMethodId);
- // And delete it from the set. This is necessary as one input
- // method can be associated with more than one language thus
- // we should avoid having duplicates in the new list.
- delete preloadEngineSet[inputMethodId];
- }
- }
- }
- return newPreloadEngines;
- },
- /**
- * Initializes the map of language code to input method IDs.
- * @private
- */
- initializeLanguageCodeToInputMethodIdsMap_: function() {
- var inputMethodList = templateData.inputMethodList;
- for (var i = 0; i < inputMethodList.length; i++) {
- var inputMethod = inputMethodList[i];
- for (var languageCode in inputMethod.languageCodeSet) {
- if (languageCode in this.languageCodeToInputMethodIdsMap_) {
- this.languageCodeToInputMethodIdsMap_[languageCode].push(
- inputMethod.id);
- } else {
- this.languageCodeToInputMethodIdsMap_[languageCode] =
- [inputMethod.id];
- }
- }
- }
- },
- /**
- * Updates the currently selected language name.
- * @param {string} languageCode Language code (ex. "fr").
- * @private
- */
- updateSelectedLanguageName_: function(languageCode) {
- var languageDisplayName = LanguageList.getDisplayNameFromLanguageCode(
- languageCode);
- var languageNativeDisplayName =
- LanguageList.getNativeDisplayNameFromLanguageCode(languageCode);
- // If the native name is different, add it.
- if (languageDisplayName != languageNativeDisplayName) {
- languageDisplayName += ' - ' + languageNativeDisplayName;
- }
- // Update the currently selected language name.
- var languageName = $('language-options-language-name');
- if (languageDisplayName) {
- languageName.hidden = false;
- languageName.textContent = languageDisplayName;
- } else {
- languageName.hidden = true;
- }
- },
- /**
- * Updates the UI language button.
- * @param {string} languageCode Language code (ex. "fr").
- * @private
- */
- updateUiLanguageButton_: function(languageCode) {
- var uiLanguageButton = $('language-options-ui-language-button');
- // Check if the language code matches the current UI language.
- if (languageCode == templateData.currentUiLanguageCode) {
- // If it matches, the button just says that the UI language is
- // currently in use.
- uiLanguageButton.textContent =
- localStrings.getString('is_displayed_in_this_language');
- // Make it look like a text label.
- uiLanguageButton.className = 'text-button';
- // Remove the event listner.
- uiLanguageButton.onclick = undefined;
- } else if (languageCode in templateData.uiLanguageCodeSet) {
- // If the language is supported as UI language, users can click on
- // the button to change the UI language.
- if (cr.commandLine.options['--bwsi']) {
- // In the guest mode for ChromeOS, changing UI language does not make
- // sense because it does not take effect after browser restart.
- uiLanguageButton.hidden = true;
- } else {
- uiLanguageButton.textContent =
- localStrings.getString('display_in_this_language');
- uiLanguageButton.className = '';
- // Send the change request to Chrome.
- uiLanguageButton.onclick = function(e) {
- chrome.send('uiLanguageChange', [languageCode]);
- }
- }
- if (cr.isChromeOS) {
- $('language-options-ui-restart-button').onclick = function(e) {
- chrome.send('uiLanguageRestart');
- }
- }
- } else {
- // If the language is not supported as UI language, the button
- // just says that Chromium OS cannot be displayed in this language.
- uiLanguageButton.textContent =
- localStrings.getString('cannot_be_displayed_in_this_language');
- uiLanguageButton.className = 'text-button';
- uiLanguageButton.onclick = undefined;
- }
- uiLanguageButton.style.display = 'block';
- $('language-options-ui-notification-bar').style.display = 'none';
- },
- /**
- * Updates the spell check language button.
- * @param {string} languageCode Language code (ex. "fr").
- * @private
- */
- updateSpellCheckLanguageButton_: function(languageCode) {
- var display = 'block';
- var spellCheckLanguageButton = $(
- 'language-options-spell-check-language-button');
- // Check if the language code matches the current spell check language.
- if (languageCode == this.spellCheckDictionary_) {
- // If it matches, the button just says that the spell check language is
- // currently in use.
- spellCheckLanguageButton.textContent =
- localStrings.getString('is_used_for_spell_checking');
- // Make it look like a text label.
- spellCheckLanguageButton.className = 'text-button';
- // Remove the event listner.
- spellCheckLanguageButton.onclick = undefined;
- } else if (languageCode in templateData.spellCheckLanguageCodeSet) {
- // If the language is supported as spell check language, users can
- // click on the button to change the spell check language.
- spellCheckLanguageButton.textContent =
- localStrings.getString('use_this_for_spell_checking');
- spellCheckLanguageButton.className = '';
- spellCheckLanguageButton.languageCode = languageCode;
- // Add an event listner to the click event.
- spellCheckLanguageButton.addEventListener('click',
- this.handleSpellCheckLanguageButtonClick_.bind(this));
- } else if (!languageCode) {
- display = 'none';
- } else {
- // If the language is not supported as spell check language, the
- // button just says that this language cannot be used for spell
- // checking.
- spellCheckLanguageButton.textContent =
- localStrings.getString('cannot_be_used_for_spell_checking');
- spellCheckLanguageButton.className = 'text-button';
- spellCheckLanguageButton.onclick = undefined;
- }
- spellCheckLanguageButton.style.display = display;
- $('language-options-ui-notification-bar').style.display = 'none';
- },
- /**
- * Updates the input method list.
- * @param {string} languageCode Language code (ex. "fr").
- * @private
- */
- updateInputMethodList_: function(languageCode) {
- // Give one of the checkboxes or buttons focus, if it's specified in the
- // URL hash (ex. focus=mozc). Used for automated testing.
- var focusInputMethodId = -1;
- var match = document.location.hash.match(/\bfocus=([\w:-]+)\b/);
- if (match) {
- focusInputMethodId = match[1];
- }
- // Change the visibility of the input method list. Input methods that
- // matches |languageCode| will become visible.
- var inputMethodList = $('language-options-input-method-list');
- var labels = inputMethodList.querySelectorAll('label');
- for (var i = 0; i < labels.length; i++) {
- var label = labels[i];
- if (languageCode in label.languageCodeSet) {
- label.style.display = 'block';
- var input = label.childNodes[0];
- // Give it focus if the ID matches.
- if (input.inputMethodId == focusInputMethodId) {
- input.focus();
- }
- } else {
- label.style.display = 'none';
- }
- }
- if (focusInputMethodId == 'add') {
- $('language-options-add-button').focus();
- }
- },
- /**
- * Updates the language list in the add language overlay.
- * @param {string} languageCode Language code (ex. "fr").
- * @private
- */
- updateLanguageListInAddLanguageOverlay_: function(languageCode) {
- // Change the visibility of the language list in the add language
- // overlay. Languages that are already active will become invisible,
- // so that users don't add the same language twice.
- var languageOptionsList = $('language-options-list');
- var languageCodes = languageOptionsList.getLanguageCodes();
- var languageCodeSet = {};
- for (var i = 0; i < languageCodes.length; i++) {
- languageCodeSet[languageCodes[i]] = true;
- }
- var addLanguageList = $('add-language-overlay-language-list');
- var lis = addLanguageList.querySelectorAll('li');
- for (var i = 0; i < lis.length; i++) {
- // The first child button knows the language code.
- var button = lis[i].childNodes[0];
- if (button.languageCode in languageCodeSet) {
- lis[i].style.display = 'none';
- } else {
- lis[i].style.display = 'block';
- }
- }
- },
- /**
- * Handles preloadEnginesPref change.
- * @param {Event} e Change event.
- * @private
- */
- handlePreloadEnginesPrefChange_: function(e) {
- var value = e.value.value;
- this.preloadEngines_ = this.filterBadPreloadEngines_(value.split(','));
- this.updateCheckboxesFromPreloadEngines_();
- $('language-options-list').updateDeletable();
- },
- /**
- * Handles input method checkbox's click event.
- * @param {Event} e Click event.
- * @private
- */
- handleCheckboxClick_ : function(e) {
- var checkbox = e.target;
- if (this.preloadEngines_.length == 1 && !checkbox.checked) {
- // Don't allow disabling the last input method.
- this.showNotification_(
- localStrings.getString('please_add_another_input_method'),
- localStrings.getString('ok_button'));
- checkbox.checked = true;
- return;
- }
- if (checkbox.checked) {
- chrome.send('inputMethodEnable', [checkbox.inputMethodId]);
- } else {
- chrome.send('inputMethodDisable', [checkbox.inputMethodId]);
- }
- this.updatePreloadEnginesFromCheckboxes_();
- this.preloadEngines_ = this.sortPreloadEngines_(this.preloadEngines_);
- this.savePreloadEnginesPref_();
- },
- /**
- * Handles add language list's click event.
- * @param {Event} e Click event.
- */
- handleAddLanguageListClick_ : function(e) {
- var languageOptionsList = $('language-options-list');
- var languageCode = e.target.languageCode;
- // languageCode can be undefined, if click was made on some random
- // place in the overlay, rather than a button. Ignore it.
- if (!languageCode) {
- return;
- }
- languageOptionsList.addLanguage(languageCode);
- var inputMethodIds = this.languageCodeToInputMethodIdsMap_[languageCode];
- // Enable the first input method for the language added.
- if (inputMethodIds && inputMethodIds[0] &&
- // Don't add the input method it's already present. This can
- // happen if the same input method is shared among multiple
- // languages (ex. English US keyboard is used for English US and
- // Filipino).
- this.preloadEngines_.indexOf(inputMethodIds[0]) == -1) {
- this.preloadEngines_.push(inputMethodIds[0]);
- this.updateCheckboxesFromPreloadEngines_();
- this.savePreloadEnginesPref_();
- }
- OptionsPage.closeOverlay();
- },
- /**
- * Handles add language dialog ok button.
- */
- handleAddLanguageOkButtonClick_ : function() {
- var languagesSelect = $('add-language-overlay-language-list');
- var selectedIndex = languagesSelect.selectedIndex;
- if (selectedIndex >= 0) {
- var selection = languagesSelect.options[selectedIndex];
- $('language-options-list').addLanguage(String(selection.value));
- OptionsPage.closeOverlay();
- }
- },
- /**
- * Checks if languageCode is deletable or not.
- * @param {String} languageCode the languageCode to check for deletability.
- */
- languageIsDeletable: function(languageCode) {
- // Don't allow removing the language if it's as UI language.
- if (languageCode == templateData.currentUiLanguageCode)
- return false;
- return (!cr.isChromeOS ||
- this.canDeleteLanguage_(languageCode));
- },
- /**
- * Handles browse.enable_spellchecking change.
- * @param {Event} e Change event.
- * @private
- */
- updateEnableSpellCheck_: function() {
- var value = !$('enable-spell-check').checked;
- $('language-options-spell-check-language-button').disabled = value;
- },
- /**
- * Handles spellCheckDictionaryPref change.
- * @param {Event} e Change event.
- * @private
- */
- handleSpellCheckDictionaryPrefChange_: function(e) {
- var languageCode = e.value.value
- this.spellCheckDictionary_ = languageCode;
- var languageOptionsList = $('language-options-list');
- var selectedLanguageCode = languageOptionsList.getSelectedLanguageCode();
- this.updateSpellCheckLanguageButton_(selectedLanguageCode);
- },
- /**
- * Handles spellCheckLanguageButton click.
- * @param {Event} e Click event.
- * @private
- */
- handleSpellCheckLanguageButtonClick_: function(e) {
- var languageCode = e.target.languageCode;
- // Save the preference.
- Preferences.setStringPref(this.spellCheckDictionaryPref,
- languageCode);
- chrome.send('spellCheckLanguageChange', [languageCode]);
- },
- /**
- * Checks whether it's possible to remove the language specified by
- * languageCode and returns true if possible. This function returns false
- * if the removal causes the number of preload engines to be zero.
- *
- * @param {string} languageCode Language code (ex. "fr").
- * @return {boolean} Returns true on success.
- * @private
- */
- canDeleteLanguage_: function(languageCode) {
- // First create the set of engines to be removed from input methods
- // associated with the language code.
- var enginesToBeRemovedSet = {};
- var inputMethodIds = this.languageCodeToInputMethodIdsMap_[languageCode];
- for (var i = 0; i < inputMethodIds.length; i++) {
- enginesToBeRemovedSet[inputMethodIds[i]] = true;
- }
- // Then eliminate engines that are also used for other active languages.
- // For instance, if "xkb:us::eng" is used for both English and Filipino.
- var languageCodes = $('language-options-list').getLanguageCodes();
- for (var i = 0; i < languageCodes.length; i++) {
- // Skip the target language code.
- if (languageCodes[i] == languageCode) {
- continue;
- }
- // Check if input methods used in this language are included in
- // enginesToBeRemovedSet. If so, eliminate these from the set, so
- // we don't remove this time.
- var inputMethodIdsForAnotherLanguage =
- this.languageCodeToInputMethodIdsMap_[languageCodes[i]];
- for (var j = 0; j < inputMethodIdsForAnotherLanguage.length; j++) {
- var inputMethodId = inputMethodIdsForAnotherLanguage[j];
- if (inputMethodId in enginesToBeRemovedSet) {
- delete enginesToBeRemovedSet[inputMethodId];
- }
- }
- }
- // Update the preload engine list with the to-be-removed set.
- var newPreloadEngines = [];
- for (var i = 0; i < this.preloadEngines_.length; i++) {
- if (!(this.preloadEngines_[i] in enginesToBeRemovedSet)) {
- newPreloadEngines.push(this.preloadEngines_[i]);
- }
- }
- // Don't allow this operation if it causes the number of preload
- // engines to be zero.
- return (newPreloadEngines.length > 0);
- },
- /**
- * Saves the preload engines preference.
- * @private
- */
- savePreloadEnginesPref_: function() {
- Preferences.setStringPref(this.preloadEnginesPref,
- this.preloadEngines_.join(','));
- },
- /**
- * Updates the checkboxes in the input method list from the preload
- * engines preference.
- * @private
- */
- updateCheckboxesFromPreloadEngines_: function() {
- // Convert the list into a dictonary for simpler lookup.
- var dictionary = {};
- for (var i = 0; i < this.preloadEngines_.length; i++) {
- dictionary[this.preloadEngines_[i]] = true;
- }
- var inputMethodList = $('language-options-input-method-list');
- var checkboxes = inputMethodList.querySelectorAll('input');
- for (var i = 0; i < checkboxes.length; i++) {
- checkboxes[i].checked = (checkboxes[i].inputMethodId in dictionary);
- }
- },
- /**
- * Updates the preload engines preference from the checkboxes in the
- * input method list.
- * @private
- */
- updatePreloadEnginesFromCheckboxes_: function() {
- this.preloadEngines_ = [];
- var inputMethodList = $('language-options-input-method-list');
- var checkboxes = inputMethodList.querySelectorAll('input');
- for (var i = 0; i < checkboxes.length; i++) {
- if (checkboxes[i].checked) {
- this.preloadEngines_.push(checkboxes[i].inputMethodId);
- }
- }
- var languageOptionsList = $('language-options-list');
- languageOptionsList.updateDeletable();
- },
- /**
- * Filters bad preload engines in case bad preload engines are
- * stored in the preference. Removes duplicates as well.
- * @param {Array} preloadEngines List of preload engines.
- * @private
- */
- filterBadPreloadEngines_: function(preloadEngines) {
- // Convert the list into a dictonary for simpler lookup.
- var dictionary = {};
- for (var i = 0; i < templateData.inputMethodList.length; i++) {
- dictionary[templateData.inputMethodList[i].id] = true;
- }
- var filteredPreloadEngines = [];
- var seen = {};
- for (var i = 0; i < preloadEngines.length; i++) {
- // Check if the preload engine is present in the
- // dictionary, and not duplicate. Otherwise, skip it.
- if (preloadEngines[i] in dictionary && !(preloadEngines[i] in seen)) {
- filteredPreloadEngines.push(preloadEngines[i]);
- seen[preloadEngines[i]] = true;
- }
- }
- return filteredPreloadEngines;
- },
- // TODO(kochi): This is an adapted copy from new_tab.js.
- // If this will go as final UI, refactor this to share the component with
- // new new tab page.
- /**
- * Shows notification
- * @private
- */
- notificationTimeout_: null,
- showNotification_ : function(text, actionText, opt_delay) {
- var notificationElement = $('notification');
- var actionLink = notificationElement.querySelector('.link-color');
- var delay = opt_delay || 10000;
- function show() {
- window.clearTimeout(this.notificationTimeout_);
- notificationElement.classList.add('show');
- document.body.classList.add('notification-shown');
- }
- function hide() {
- window.clearTimeout(this.notificationTimeout_);
- notificationElement.classList.remove('show');
- document.body.classList.remove('notification-shown');
- // Prevent tabbing to the hidden link.
- actionLink.tabIndex = -1;
- // Setting tabIndex to -1 only prevents future tabbing to it. If,
- // however, the user switches window or a tab and then moves back to
- // this tab the element may gain focus. We therefore make sure that we
- // blur the element so that the element focus is not restored when
- // coming back to this window.
- actionLink.blur();
- }
- function delayedHide() {
- this.notificationTimeout_ = window.setTimeout(hide, delay);
- }
- notificationElement.firstElementChild.textContent = text;
- actionLink.textContent = actionText;
- actionLink.onclick = hide;
- actionLink.onkeydown = function(e) {
- if (e.keyIdentifier == 'Enter') {
- hide();
- }
- };
- notificationElement.onmouseover = show;
- notificationElement.onmouseout = delayedHide;
- actionLink.onfocus = show;
- actionLink.onblur = delayedHide;
- // Enable tabbing to the link now that it is shown.
- actionLink.tabIndex = 0;
- show();
- delayedHide();
- }
- };
- /**
- * Chrome callback for when the UI language preference is saved.
- */
- LanguageOptions.uiLanguageSaved = function() {
- $('language-options-ui-language-button').style.display = 'none';
- $('language-options-ui-notification-bar').style.display = 'block';
- };
- // Export
- return {
- LanguageOptions: LanguageOptions
- };
- });
- // 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 OptionsPage = options.OptionsPage;
- var ArrayDataModel = cr.ui.ArrayDataModel;
- const localStrings = new LocalStrings();
- /**
- * ManageProfileOverlay class
- * Encapsulated handling of the 'Manage profile...' overlay page.
- * @constructor
- * @class
- */
- function ManageProfileOverlay() {
- OptionsPage.call(this,
- 'manageProfile',
- templateData.manageProfileOverlayTabTitle,
- 'manage-profile-overlay');
- };
- cr.addSingletonGetter(ManageProfileOverlay);
- ManageProfileOverlay.prototype = {
- // Inherit from OptionsPage.
- __proto__: OptionsPage.prototype,
- // Info about the currently managed/deleted profile.
- profileInfo_: null,
- // An object containing all known profile names.
- profileNames_: {},
- // The currently selected icon in the icon grid.
- iconGridSelectedURL_: null,
- /**
- * Initialize the page.
- */
- initializePage: function() {
- // Call base class implementation to start preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- var self = this;
- var iconGrid = $('manage-profile-icon-grid');
- options.ProfilesIconGrid.decorate(iconGrid);
- iconGrid.addEventListener('change', function(e) {
- self.onIconGridSelectionChanged_();
- });
- $('manage-profile-name').oninput = this.onNameChanged_.bind(this);
- $('manage-profile-cancel').onclick =
- $('delete-profile-cancel').onclick = function(event) {
- OptionsPage.closeOverlay();
- };
- $('manage-profile-ok').onclick = function(event) {
- OptionsPage.closeOverlay();
- self.submitManageChanges_();
- };
- $('delete-profile-ok').onclick = function(event) {
- OptionsPage.closeOverlay();
- chrome.send('deleteProfile', [self.profileInfo_.filePath]);
- };
- },
- /** @inheritDoc */
- didShowPage: function() {
- chrome.send('requestDefaultProfileIcons');
- // Use the hash to specify the profile index.
- var hash = location.hash;
- if (hash) {
- $('manage-profile-overlay-manage').hidden = false;
- $('manage-profile-overlay-delete').hidden = true;
- ManageProfileOverlay.getInstance().hideErrorBubble_();
- chrome.send('requestProfileInfo', [parseInt(hash.slice(1), 10)]);
- }
- $('manage-profile-name').focus();
- },
- /**
- * Set the profile info used in the dialog.
- * @param {Object} profileInfo An object of the form:
- * profileInfo = {
- * name: "Profile Name",
- * iconURL: "chrome://path/to/icon/image",
- * filePath: "/path/to/profile/data/on/disk"
- * isCurrentProfile: false,
- * };
- * @private
- */
- setProfileInfo_: function(profileInfo) {
- this.iconGridSelectedURL_ = profileInfo.iconURL;
- this.profileInfo_ = profileInfo;
- $('manage-profile-name').value = profileInfo.name;
- $('manage-profile-icon-grid').selectedItem = profileInfo.iconURL;
- },
- /**
- * Sets the name of the currently edited profile.
- * @private
- */
- setProfileName_: function(name) {
- if (this.profileInfo_)
- this.profileInfo_.name = name;
- $('manage-profile-name').value = name;
- },
- /**
- * Set an array of default icon URLs. These will be added to the grid that
- * the user will use to choose their profile icon.
- * @param {Array.<string>} iconURLs An array of icon URLs.
- * @private
- */
- receiveDefaultProfileIcons_: function(iconURLs) {
- $('manage-profile-icon-grid').dataModel = new ArrayDataModel(iconURLs);
- // Changing the dataModel resets the selectedItem. Re-select it, if there
- // is one.
- if (this.profileInfo_)
- $('manage-profile-icon-grid').selectedItem = this.profileInfo_.iconURL;
- var grid = $('manage-profile-icon-grid');
- // Recalculate the measured item size.
- grid.measured_ = null;
- grid.columns = 0;
- grid.redraw();
- },
- /**
- * Set a dictionary of all profile names. These are used to prevent the
- * user from naming two profiles the same.
- * @param {Object} profileNames A dictionary of profile names.
- * @private
- */
- receiveProfileNames_: function(profileNames) {
- this.profileNames_ = profileNames;
- },
- /**
- * Display the error bubble, with |errorText| in the bubble.
- * @param {string} errorText The localized string id to display as an error.
- * @private
- */
- showErrorBubble_: function(errorText) {
- var nameErrorEl = $('manage-profile-error-bubble');
- nameErrorEl.hidden = false;
- nameErrorEl.textContent = localStrings.getString(errorText);
- $('manage-profile-ok').disabled = true;
- },
- /**
- * Hide the error bubble.
- * @private
- */
- hideErrorBubble_: function() {
- $('manage-profile-error-bubble').hidden = true;
- $('manage-profile-ok').disabled = false;
- },
- /**
- * oninput callback for <input> field.
- * @param event The event object
- * @private
- */
- onNameChanged_: function(event) {
- var newName = event.target.value;
- var oldName = this.profileInfo_.name;
- if (newName == oldName) {
- this.hideErrorBubble_();
- } else if (this.profileNames_[newName] != undefined) {
- this.showErrorBubble_('manageProfilesDuplicateNameError');
- } else {
- this.hideErrorBubble_();
- var nameIsValid = $('manage-profile-name').validity.valid;
- $('manage-profile-ok').disabled = !nameIsValid;
- }
- },
- /**
- * Called when the user clicks "OK". Saves the newly changed profile info.
- * @private
- */
- submitManageChanges_: function() {
- var name = $('manage-profile-name').value;
- var iconURL = $('manage-profile-icon-grid').selectedItem;
- chrome.send('setProfileNameAndIcon',
- [this.profileInfo_.filePath, name, iconURL]);
- },
- /**
- * Called when the selected icon in the icon grid changes.
- * @private
- */
- onIconGridSelectionChanged_: function() {
- var iconURL = $('manage-profile-icon-grid').selectedItem;
- if (!iconURL || iconURL == this.iconGridSelectedURL_)
- return;
- this.iconGridSelectedURL_ = iconURL;
- chrome.send('profileIconSelectionChanged',
- [this.profileInfo_.filePath, iconURL]);
- },
- /**
- * Display the "Manage Profile" dialog.
- * @param {Object} profileInfo The profile object of the profile to manage.
- * @private
- */
- showManageDialog_: function(profileInfo) {
- ManageProfileOverlay.setProfileInfo(profileInfo);
- $('manage-profile-overlay-manage').hidden = false;
- $('manage-profile-overlay-delete').hidden = true;
- ManageProfileOverlay.getInstance().hideErrorBubble_();
- // Intentionally don't show the URL in the location bar as we don't want
- // people trying to navigate here by hand.
- OptionsPage.showPageByName('manageProfile', false);
- },
- /**
- * Display the "Delete Profile" dialog.
- * @param {Object} profileInfo The profile object of the profile to delete.
- * @private
- */
- showDeleteDialog_: function(profileInfo) {
- ManageProfileOverlay.setProfileInfo(profileInfo);
- $('manage-profile-overlay-manage').hidden = true;
- $('manage-profile-overlay-delete').hidden = false;
- $('delete-profile-message').textContent =
- localStrings.getStringF('deleteProfileMessage', profileInfo.name);
- $('delete-profile-message').style.backgroundImage = 'url("' +
- profileInfo.iconURL + '")';
- // Intentionally don't show the URL in the location bar as we don't want
- // people trying to navigate here by hand.
- OptionsPage.showPageByName('manageProfile', false);
- },
- };
- // Forward public APIs to private implementations.
- [
- 'receiveDefaultProfileIcons',
- 'receiveProfileNames',
- 'setProfileInfo',
- 'setProfileName',
- 'showManageDialog',
- 'showDeleteDialog',
- ].forEach(function(name) {
- ManageProfileOverlay[name] = function(value) {
- ManageProfileOverlay.getInstance()[name + '_'](value);
- };
- });
- // Export
- return {
- ManageProfileOverlay: ManageProfileOverlay
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- /**
- * PackExtensionOverlay class
- * Encapsulated handling of the 'Pack Extension' overlay page.
- * @constructor
- */
- function PackExtensionOverlay() {
- OptionsPage.call(this, 'packExtensionOverlay',
- templateData.packExtensionOverlayTabTitle,
- 'packExtensionOverlay');
- }
- cr.addSingletonGetter(PackExtensionOverlay);
- PackExtensionOverlay.prototype = {
- // Inherit PackExtensionOverlay from OptionsPage.
- __proto__: OptionsPage.prototype,
- /**
- * Initialize the page.
- */
- initializePage: function() {
- // Call base class implementation to starts preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- $('packExtensionDismiss').onclick = function(event) {
- OptionsPage.closeOverlay();
- };
- $('packExtensionCommit').onclick = function(event) {
- var extensionPath = $('extensionRootDir').value;
- var privateKeyPath = $('extensionPrivateKey').value;
- chrome.send('pack', [extensionPath, privateKeyPath]);
- };
- $('browseExtensionDir').addEventListener('click',
- this.handleBrowseExtensionDir_.bind(this));
- $('browsePrivateKey').addEventListener('click',
- this.handleBrowsePrivateKey_.bind(this));
- },
- /**
- * Utility function which asks the C++ to show a platform-specific file
- * select dialog, and fire |callback| with the |filePath| that resulted.
- * |selectType| can be either 'file' or 'folder'. |operation| can be 'load',
- * 'packRoot', or 'pem' which are signals to the C++ to do some
- * operation-specific configuration.
- * @private
- */
- showFileDialog_: function(selectType, operation, callback) {
- handleFilePathSelected = function(filePath) {
- callback(filePath);
- handleFilePathSelected = function() {};
- };
- chrome.send('extensionSettingsSelectFilePath', [selectType, operation]);
- },
- /**
- * Handles the showing of the extension directory browser.
- * @param {Event} e Change event.
- * @private
- */
- handleBrowseExtensionDir_: function(e) {
- this.showFileDialog_('folder', 'load', function(filePath) {
- $('extensionRootDir').value = filePath;
- });
- },
- /**
- * Handles the showing of the extension private key file.
- * @param {Event} e Change event.
- * @private
- */
- handleBrowsePrivateKey_: function(e) {
- this.showFileDialog_('file', 'load', function(filePath) {
- $('extensionPrivateKey').value = filePath;
- });
- },
- };
- // Export
- return {
- PackExtensionOverlay: PackExtensionOverlay
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- const ArrayDataModel = cr.ui.ArrayDataModel;
- /////////////////////////////////////////////////////////////////////////////
- // PasswordManager class:
- /**
- * Encapsulated handling of password and exceptions page.
- * @constructor
- */
- function PasswordManager() {
- this.activeNavTab = null;
- OptionsPage.call(this,
- 'passwords',
- templateData.passwordsPageTabTitle,
- 'password-manager');
- }
- cr.addSingletonGetter(PasswordManager);
- PasswordManager.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * The saved passwords list.
- * @type {DeletableItemList}
- * @private
- */
- savedPasswordsList_: null,
- /**
- * The password exceptions list.
- * @type {DeletableItemList}
- * @private
- */
- passwordExceptionsList_: null,
- /**
- * The timer id of the timer set on search query change events.
- * @type {number}
- * @private
- */
- queryDelayTimerId_: 0,
- /**
- * The most recent search query, or null if the query is empty.
- * @type {?string}
- * @private
- */
- lastQuery_: null,
- /** @inheritDoc */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- $('password-search-box').addEventListener('search',
- this.handleSearchQueryChange_.bind(this));
- this.createSavedPasswordsList_();
- this.createPasswordExceptionsList_();
- },
- /** @inheritDoc */
- canShowPage: function() {
- return !PersonalOptions.disablePasswordManagement();
- },
- /** @inheritDoc */
- didShowPage: function() {
- // Updating the password lists may cause a blocking platform dialog pop up
- // (Mac, Linux), so we delay this operation until the page is shown.
- chrome.send('updatePasswordLists');
- $('password-search-box').focus();
- },
- /**
- * Creates, decorates and initializes the saved passwords list.
- * @private
- */
- createSavedPasswordsList_: function() {
- this.savedPasswordsList_ = $('saved-passwords-list');
- options.passwordManager.PasswordsList.decorate(this.savedPasswordsList_);
- this.savedPasswordsList_.autoExpands = true;
- },
- /**
- * Creates, decorates and initializes the password exceptions list.
- * @private
- */
- createPasswordExceptionsList_: function() {
- this.passwordExceptionsList_ = $('password-exceptions-list');
- options.passwordManager.PasswordExceptionsList.decorate(
- this.passwordExceptionsList_);
- this.passwordExceptionsList_.autoExpands = true;
- },
- /**
- * Handles search query changes.
- * @param {!Event} e The event object.
- * @private
- */
- handleSearchQueryChange_: function(e) {
- if (this.queryDelayTimerId_)
- window.clearTimeout(this.queryDelayTimerId_);
- // Searching cookies uses a timeout of 500ms. We use a shorter timeout
- // because there are probably fewer passwords and we want the UI to be
- // snappier since users will expect that it's "less work."
- this.queryDelayTimerId_ = window.setTimeout(
- this.searchPasswords_.bind(this), 250);
- },
- /**
- * Search passwords using text in |password-search-box|.
- * @private
- */
- searchPasswords_: function() {
- this.queryDelayTimerId_ = 0;
- var filter = $('password-search-box').value;
- filter = (filter == '') ? null : filter;
- if (this.lastQuery_ != filter) {
- this.lastQuery_ = filter;
- // Searching for passwords has the side effect of requerying the
- // underlying password store. This is done intentionally, as on OS X and
- // Linux they can change from outside and we won't be notified of it.
- chrome.send('updatePasswordLists');
- }
- },
- /**
- * Updates the visibility of the list and empty list placeholder.
- * @param {!List} list The list to toggle visilibility for.
- */
- updateListVisibility_: function(list) {
- var empty = list.dataModel.length == 0;
- var listPlaceHolderID = list.id + '-empty-placeholder';
- list.hidden = empty;
- $(listPlaceHolderID).hidden = !empty;
- },
- /**
- * Updates the data model for the saved passwords list with the values from
- * |entries|.
- * @param {Array} entries The list of saved password data.
- */
- setSavedPasswordsList_: function(entries) {
- if (this.lastQuery_) {
- // Implement password searching here in javascript, rather than in C++.
- // The number of saved passwords shouldn't be too big for us to handle.
- var query = this.lastQuery_;
- var filter = function(entry, index, list) {
- // Search both URL and username.
- if (entry[0].indexOf(query) >= 0 || entry[1].indexOf(query) >= 0) {
- // Keep the original index so we can delete correctly. See also
- // deleteItemAtIndex() in password_manager_list.js that uses this.
- entry[3] = index;
- return true;
- }
- return false;
- };
- entries = entries.filter(filter);
- }
- this.savedPasswordsList_.dataModel = new ArrayDataModel(entries);
- this.updateListVisibility_(this.savedPasswordsList_);
- },
- /**
- * Updates the data model for the password exceptions list with the values
- * from |entries|.
- * @param {Array} entries The list of password exception data.
- */
- setPasswordExceptionsList_: function(entries) {
- this.passwordExceptionsList_.dataModel = new ArrayDataModel(entries);
- this.updateListVisibility_(this.passwordExceptionsList_);
- },
- };
- /**
- * Call to remove a saved password.
- * @param rowIndex indicating the row to remove.
- */
- PasswordManager.removeSavedPassword = function(rowIndex) {
- chrome.send('removeSavedPassword', [String(rowIndex)]);
- };
- /**
- * Call to remove a password exception.
- * @param rowIndex indicating the row to remove.
- */
- PasswordManager.removePasswordException = function(rowIndex) {
- chrome.send('removePasswordException', [String(rowIndex)]);
- };
- /**
- * Call to remove all saved passwords.
- * @param tab contentType of the tab currently on.
- */
- PasswordManager.removeAllPasswords = function() {
- chrome.send('removeAllSavedPasswords');
- };
- /**
- * Call to remove all saved passwords.
- * @param tab contentType of the tab currently on.
- */
- PasswordManager.removeAllPasswordExceptions = function() {
- chrome.send('removeAllPasswordExceptions');
- };
- PasswordManager.setSavedPasswordsList = function(entries) {
- PasswordManager.getInstance().setSavedPasswordsList_(entries);
- };
- PasswordManager.setPasswordExceptionsList = function(entries) {
- PasswordManager.getInstance().setPasswordExceptionsList_(entries);
- };
- // Export
- return {
- PasswordManager: PasswordManager
- };
- });
- // 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.passwordManager', function() {
- const ArrayDataModel = cr.ui.ArrayDataModel;
- const DeletableItemList = options.DeletableItemList;
- const DeletableItem = options.DeletableItem;
- const List = cr.ui.List;
- /**
- * Creates a new passwords list item.
- * @param {Array} entry An array of the form [url, username, password]. When
- * the list has been filtered, a fourth element [index] may be present.
- * @constructor
- * @extends {cr.ui.ListItem}
- */
- function PasswordListItem(entry, showPasswords) {
- var el = cr.doc.createElement('div');
- el.dataItem = entry;
- el.__proto__ = PasswordListItem.prototype;
- el.decorate(showPasswords);
- return el;
- }
- PasswordListItem.prototype = {
- __proto__: DeletableItem.prototype,
- /** @inheritDoc */
- decorate: function(showPasswords) {
- DeletableItem.prototype.decorate.call(this);
- // The URL of the site.
- var urlLabel = this.ownerDocument.createElement('div');
- urlLabel.classList.add('favicon-cell');
- urlLabel.classList.add('weakrtl');
- urlLabel.classList.add('url');
- urlLabel.setAttribute('title', this.url);
- urlLabel.textContent = this.url;
- urlLabel.style.backgroundImage = url('chrome://favicon/' + this.url);
- this.contentElement.appendChild(urlLabel);
- // The stored username.
- var usernameLabel = this.ownerDocument.createElement('div');
- usernameLabel.className = 'name';
- usernameLabel.textContent = this.username;
- this.contentElement.appendChild(usernameLabel);
- // The stored password.
- var passwordInputDiv = this.ownerDocument.createElement('div');
- passwordInputDiv.className = 'password';
- // The password input field.
- var passwordInput = this.ownerDocument.createElement('input');
- passwordInput.type = 'password';
- passwordInput.className = 'inactive-password';
- passwordInput.readOnly = true;
- passwordInput.value = showPasswords ? this.password : '********';
- passwordInputDiv.appendChild(passwordInput);
- // The show/hide button.
- if (showPasswords) {
- var button = this.ownerDocument.createElement('button');
- button.hidden = true;
- button.classList.add('password-button');
- button.textContent = localStrings.getString('passwordShowButton');
- button.addEventListener('click', this.onClick_, true);
- passwordInputDiv.appendChild(button);
- }
- this.contentElement.appendChild(passwordInputDiv);
- },
- /** @inheritDoc */
- selectionChanged: function() {
- var passwordInput = this.querySelector('input[type=password]');
- var textInput = this.querySelector('input[type=text]');
- var input = passwordInput || textInput;
- var button = input.nextSibling;
- // |button| doesn't exist when passwords can't be shown.
- if (!button)
- return;
- if (this.selected) {
- input.classList.remove('inactive-password');
- button.hidden = false;
- } else {
- input.classList.add('inactive-password');
- button.hidden = true;
- }
- },
- /**
- * On-click event handler. Swaps the type of the input field from password
- * to text and back.
- * @private
- */
- onClick_: function(event) {
- // The password is the input element previous to the button.
- var button = event.currentTarget;
- var passwordInput = button.previousSibling;
- if (passwordInput.type == 'password') {
- passwordInput.type = 'text';
- button.textContent = localStrings.getString('passwordHideButton');
- } else {
- passwordInput.type = 'password';
- button.textContent = localStrings.getString('passwordShowButton');
- }
- },
- /**
- * Get and set the URL for the entry.
- * @type {string}
- */
- get url() {
- return this.dataItem[0];
- },
- set url(url) {
- this.dataItem[0] = url;
- },
- /**
- * Get and set the username for the entry.
- * @type {string}
- */
- get username() {
- return this.dataItem[1];
- },
- set username(username) {
- this.dataItem[1] = username;
- },
- /**
- * Get and set the password for the entry.
- * @type {string}
- */
- get password() {
- return this.dataItem[2];
- },
- set password(password) {
- this.dataItem[2] = password;
- },
- };
- /**
- * Creates a new PasswordExceptions list item.
- * @param {Array} entry A pair of the form [url, username].
- * @constructor
- * @extends {Deletable.ListItem}
- */
- function PasswordExceptionsListItem(entry) {
- var el = cr.doc.createElement('div');
- el.dataItem = entry;
- el.__proto__ = PasswordExceptionsListItem.prototype;
- el.decorate();
- return el;
- }
- PasswordExceptionsListItem.prototype = {
- __proto__: DeletableItem.prototype,
- /**
- * Call when an element is decorated as a list item.
- */
- decorate: function() {
- DeletableItem.prototype.decorate.call(this);
- // The URL of the site.
- var urlLabel = this.ownerDocument.createElement('div');
- urlLabel.className = 'url';
- urlLabel.classList.add('favicon-cell');
- urlLabel.classList.add('weakrtl');
- urlLabel.textContent = this.url;
- urlLabel.style.backgroundImage = url('chrome://favicon/' + this.url);
- this.contentElement.appendChild(urlLabel);
- },
- /**
- * Get the url for the entry.
- * @type {string}
- */
- get url() {
- return this.dataItem;
- },
- set url(url) {
- this.dataItem = url;
- },
- };
- /**
- * Create a new passwords list.
- * @constructor
- * @extends {cr.ui.List}
- */
- var PasswordsList = cr.ui.define('list');
- PasswordsList.prototype = {
- __proto__: DeletableItemList.prototype,
- /**
- * Whether passwords can be revealed or not.
- * @type {boolean}
- * @private
- */
- showPasswords_: true,
- /** @inheritDoc */
- decorate: function() {
- DeletableItemList.prototype.decorate.call(this);
- Preferences.getInstance().addEventListener(
- "profile.password_manager_allow_show_passwords",
- this.onPreferenceChanged_.bind(this));
- },
- /**
- * Listener for changes on the preference.
- * @param {Event} event The preference update event.
- * @private
- */
- onPreferenceChanged_: function(event) {
- this.showPasswords_ = event.value.value;
- this.redraw();
- },
- /** @inheritDoc */
- createItem: function(entry) {
- return new PasswordListItem(entry, this.showPasswords_);
- },
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- var item = this.dataModel.item(index);
- if (item && item.length > 3) {
- // The fourth element, if present, is the original index to delete.
- index = item[3];
- }
- PasswordManager.removeSavedPassword(index);
- },
- /**
- * The length of the list.
- */
- get length() {
- return this.dataModel.length;
- },
- };
- /**
- * Create a new passwords list.
- * @constructor
- * @extends {cr.ui.List}
- */
- var PasswordExceptionsList = cr.ui.define('list');
- PasswordExceptionsList.prototype = {
- __proto__: DeletableItemList.prototype,
- /** @inheritDoc */
- createItem: function(entry) {
- return new PasswordExceptionsListItem(entry);
- },
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- PasswordManager.removePasswordException(index);
- },
- /**
- * The length of the list.
- */
- get length() {
- return this.dataModel.length;
- },
- };
- return {
- PasswordListItem: PasswordListItem,
- PasswordExceptionsListItem: PasswordExceptionsListItem,
- PasswordsList: PasswordsList,
- PasswordExceptionsList: PasswordExceptionsList,
- };
- });
- // 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 OptionsPage = options.OptionsPage;
- var ArrayDataModel = cr.ui.ArrayDataModel;
- /**
- * Encapsulated handling of personal options page.
- * @constructor
- */
- function PersonalOptions() {
- OptionsPage.call(this, 'personal',
- templateData.personalPageTabTitle,
- 'personal-page');
- if (cr.isChromeOS) {
- // Username (canonical email) of the currently logged in user or
- // |kGuestUser| if a guest session is active.
- this.username_ = localStrings.getString('username');
- }
- }
- cr.addSingletonGetter(PersonalOptions);
- PersonalOptions.prototype = {
- // Inherit PersonalOptions from OptionsPage.
- __proto__: options.OptionsPage.prototype,
- // State variables.
- syncEnabled: false,
- syncSetupCompleted: false,
- // Initialize PersonalOptions page.
- initializePage: function() {
- // Call base class implementation to start preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- var self = this;
- // Sync.
- $('sync-action-link').onclick = function(event) {
- SyncSetupOverlay.showErrorUI();
- };
- $('start-stop-sync').onclick = function(event) {
- if (self.syncSetupCompleted)
- SyncSetupOverlay.showStopSyncingUI();
- else
- SyncSetupOverlay.showSetupUI();
- };
- $('customize-sync').onclick = function(event) {
- SyncSetupOverlay.showSetupUI();
- };
- // Profiles.
- var profilesList = $('profiles-list');
- options.personal_options.ProfileList.decorate(profilesList);
- profilesList.autoExpands = true;
- profilesList.onchange = self.setProfileViewButtonsStatus_;
- $('profiles-create').onclick = function(event) {
- chrome.send('createProfile');
- };
- $('profiles-manage').onclick = function(event) {
- var selectedProfile = self.getSelectedProfileItem_();
- if (selectedProfile)
- ManageProfileOverlay.showManageDialog(selectedProfile);
- };
- $('profiles-delete').onclick = function(event) {
- var selectedProfile = self.getSelectedProfileItem_();
- if (selectedProfile)
- ManageProfileOverlay.showDeleteDialog(selectedProfile);
- };
- // Passwords.
- $('manage-passwords').onclick = function(event) {
- OptionsPage.navigateToPage('passwords');
- OptionsPage.showTab($('passwords-nav-tab'));
- chrome.send('coreOptionsUserMetricsAction',
- ['Options_ShowPasswordManager']);
- };
- // Autofill.
- $('autofill-settings').onclick = function(event) {
- OptionsPage.navigateToPage('autofill');
- chrome.send('coreOptionsUserMetricsAction',
- ['Options_ShowAutofillSettings']);
- };
- if (cr.isChromeOS && cr.commandLine.options['--bwsi']) {
- // Hide Autofill options for the guest user.
- $('autofill-section').hidden = true;
- }
- // Appearance.
- $('themes-reset').onclick = function(event) {
- chrome.send('themesReset');
- };
- if (!cr.isChromeOS) {
- $('import-data').onclick = function(event) {
- // Make sure that any previous import success message is hidden, and
- // we're showing the UI to import further data.
- $('import-data-configure').hidden = false;
- $('import-data-success').hidden = true;
- OptionsPage.navigateToPage('importData');
- chrome.send('coreOptionsUserMetricsAction', ['Import_ShowDlg']);
- };
- if ($('themes-GTK-button')) {
- $('themes-GTK-button').onclick = function(event) {
- chrome.send('themesSetGTK');
- };
- }
- } else {
- $('change-picture-button').onclick = function(event) {
- OptionsPage.navigateToPage('changePicture');
- };
- this.updateAccountPicture_();
- if (cr.commandLine.options['--bwsi']) {
- // Disable the screen lock checkbox and change-picture-button in
- // guest mode.
- $('enable-screen-lock').disabled = true;
- $('change-picture-button').disabled = true;
- }
- }
- if (PersonalOptions.disablePasswordManagement()) {
- // Disable the Password Manager in guest mode.
- $('passwords-offersave').disabled = true;
- $('passwords-neversave').disabled = true;
- $('passwords-offersave').value = false;
- $('passwords-neversave').value = true;
- $('manage-passwords').disabled = true;
- }
- $('mac-passwords-warning').hidden =
- !(localStrings.getString('macPasswordsWarning'));
- if (PersonalOptions.disableAutofillManagement()) {
- $('autofill-settings').disabled = true;
- // Disable and turn off autofill.
- var autofillEnabled = $('autofill-enabled');
- autofillEnabled.disabled = true;
- autofillEnabled.checked = false;
- cr.dispatchSimpleEvent(autofillEnabled, 'change');
- }
- },
- setSyncEnabled_: function(enabled) {
- this.syncEnabled = enabled;
- },
- setAutoLoginVisible_ : function(visible) {
- $('enable-auto-login-checkbox').hidden = !visible;
- },
- setSyncSetupCompleted_: function(completed) {
- this.syncSetupCompleted = completed;
- $('customize-sync').hidden = !completed;
- },
- setSyncStatus_: function(status) {
- var statusSet = status != '';
- $('sync-overview').hidden = statusSet;
- $('sync-status').hidden = !statusSet;
- $('sync-status-text').innerHTML = status;
- },
- setSyncStatusErrorVisible_: function(visible) {
- visible ? $('sync-status').classList.add('sync-error') :
- $('sync-status').classList.remove('sync-error');
- },
- setCustomizeSyncButtonEnabled_: function(enabled) {
- $('customize-sync').disabled = !enabled;
- },
- setSyncActionLinkEnabled_: function(enabled) {
- $('sync-action-link').disabled = !enabled;
- },
- setSyncActionLinkLabel_: function(status) {
- $('sync-action-link').textContent = status;
- // link-button does is not zero-area when the contents of the button are
- // empty, so explicitly hide the element.
- $('sync-action-link').hidden = !status.length;
- },
- /**
- * Display or hide the profiles section of the page. This is used for
- * multi-profile settings.
- * @param {boolean} visible True to show the section.
- * @private
- */
- setProfilesSectionVisible_: function(visible) {
- $('profiles-section').hidden = !visible;
- },
- /**
- * Get the selected profile item from the profile list. This also works
- * correctly if the list is not displayed.
- * @return {Object} the profile item object, or null if nothing is selected.
- * @private
- */
- getSelectedProfileItem_: function() {
- var profilesList = $('profiles-list');
- if (profilesList.hidden) {
- if (profilesList.dataModel.length > 0)
- return profilesList.dataModel.item(0);
- } else {
- return profilesList.selectedItem;
- }
- return null;
- },
- /**
- * Helper function to set the status of profile view buttons to disabled or
- * enabled, depending on the number of profiles and selection status of the
- * profiles list.
- */
- setProfileViewButtonsStatus_: function() {
- var profilesList = $('profiles-list');
- var selectedProfile = profilesList.selectedItem;
- var hasSelection = selectedProfile != null;
- var hasSingleProfile = profilesList.dataModel.length == 1;
- $('profiles-manage').disabled = !hasSelection ||
- !selectedProfile.isCurrentProfile;
- $('profiles-delete').disabled = !hasSelection && !hasSingleProfile;
- },
- /**
- * Display the correct dialog layout, depending on how many profiles are
- * available.
- * @param {number} numProfiles The number of profiles to display.
- */
- setProfileViewSingle_: function(numProfiles) {
- var hasSingleProfile = numProfiles == 1;
- $('profiles-list').hidden = hasSingleProfile;
- $('profiles-single-message').hidden = !hasSingleProfile;
- $('profiles-manage').hidden = hasSingleProfile;
- $('profiles-delete').textContent = hasSingleProfile ?
- templateData.profilesDeleteSingle :
- templateData.profilesDelete;
- },
- /**
- * Adds all |profiles| to the list.
- * @param {Array.<Object>} An array of profile info objects.
- * each object is of the form:
- * profileInfo = {
- * name: "Profile Name",
- * iconURL: "chrome://path/to/icon/image",
- * filePath: "/path/to/profile/data/on/disk",
- * isCurrentProfile: false
- * };
- */
- setProfilesInfo_: function(profiles) {
- this.setProfileViewSingle_(profiles.length);
- // add it to the list, even if the list is hidden so we can access it
- // later.
- $('profiles-list').dataModel = new ArrayDataModel(profiles);
- this.setProfileViewButtonsStatus_();
- },
- setStartStopButtonVisible_: function(visible) {
- $('start-stop-sync').hidden = !visible;
- },
- setStartStopButtonEnabled_: function(enabled) {
- $('start-stop-sync').disabled = !enabled;
- },
- setStartStopButtonLabel_: function(label) {
- $('start-stop-sync').textContent = label;
- },
- setGtkThemeButtonEnabled_: function(enabled) {
- if (!cr.isChromeOS && navigator.platform.match(/linux|BSD/i)) {
- $('themes-GTK-button').disabled = !enabled;
- }
- },
- setThemesResetButtonEnabled_: function(enabled) {
- $('themes-reset').disabled = !enabled;
- },
- hideSyncSection_: function() {
- $('sync-section').hidden = true;
- },
- /**
- * Get the start/stop sync button DOM element.
- * @return {DOMElement} The start/stop sync button.
- * @private
- */
- getStartStopSyncButton_: function() {
- return $('start-stop-sync');
- },
- /**
- * (Re)loads IMG element with current user account picture.
- */
- updateAccountPicture_: function() {
- $('account-picture').src =
- 'chrome://userimage/' + this.username_ +
- '?id=' + (new Date()).getTime();
- },
- };
- /**
- * Returns whether the user should be able to manage (view and edit) their
- * stored passwords. Password management is disabled in guest mode.
- * @return {boolean} True if password management should be disabled.
- */
- PersonalOptions.disablePasswordManagement = function() {
- return cr.commandLine.options['--bwsi'];
- };
- /**
- * Returns whether the user should be able to manage autofill settings.
- * @return {boolean} True if password management should be disabled.
- */
- PersonalOptions.disableAutofillManagement = function() {
- return cr.commandLine.options['--bwsi'];
- };
- if (cr.isChromeOS) {
- /**
- * Returns username (canonical email) of the user logged in (ChromeOS only).
- * @return {string} user email.
- */
- PersonalOptions.getLoggedInUsername = function() {
- return PersonalOptions.getInstance().username_;
- };
- }
- // Forward public APIs to private implementations.
- [
- 'getStartStopSyncButton',
- 'hideSyncSection',
- 'setAutoLoginVisible',
- 'setCustomizeSyncButtonEnabled',
- 'setGtkThemeButtonEnabled',
- 'setProfilesInfo',
- 'setProfilesSectionVisible',
- 'setStartStopButtonEnabled',
- 'setStartStopButtonLabel',
- 'setStartStopButtonVisible',
- 'setSyncActionLinkEnabled',
- 'setSyncActionLinkLabel',
- 'setSyncEnabled',
- 'setSyncSetupCompleted',
- 'setSyncStatus',
- 'setSyncStatusErrorVisible',
- 'setThemesResetButtonEnabled',
- 'updateAccountPicture',
- ].forEach(function(name) {
- PersonalOptions[name] = function(value) {
- return PersonalOptions.getInstance()[name + '_'](value);
- };
- });
- // Export
- return {
- PersonalOptions: PersonalOptions
- };
- });
- // 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.personal_options', function() {
- const DeletableItem = options.DeletableItem;
- const DeletableItemList = options.DeletableItemList;
- const ListSingleSelectionModel = cr.ui.ListSingleSelectionModel;
- var localStrings = new LocalStrings();
- /**
- * Creates a new profile list item.
- * @param {Object} profileInfo The profile this item respresents.
- * @constructor
- * @extends {cr.ui.DeletableItem}
- */
- function ProfileListItem(profileInfo) {
- var el = cr.doc.createElement('div');
- el.profileInfo_ = profileInfo;
- ProfileListItem.decorate(el);
- return el;
- }
- /**
- * Decorates an element as a profile list item.
- * @param {!HTMLElement} el The element to decorate.
- */
- ProfileListItem.decorate = function(el) {
- el.__proto__ = ProfileListItem.prototype;
- el.decorate();
- };
- ProfileListItem.prototype = {
- __proto__: DeletableItem.prototype,
- /**
- * Get the filepath for this profile list item.
- * @return the file path of this item.
- */
- get profilePath() {
- return this.profileInfo_.filePath;
- },
- /** @inheritDoc */
- decorate: function() {
- DeletableItem.prototype.decorate.call(this);
- var profileInfo = this.profileInfo_;
- var iconEl = this.ownerDocument.createElement('img');
- iconEl.className = 'profile-img';
- iconEl.src = profileInfo.iconURL;
- this.contentElement.appendChild(iconEl);
- var nameEl = this.ownerDocument.createElement('div');
- if (profileInfo.isCurrentProfile)
- nameEl.classList.add('profile-item-current');
- this.contentElement.appendChild(nameEl);
- var displayName = profileInfo.name;
- if (profileInfo.isCurrentProfile)
- displayName = localStrings.getStringF(
- 'profilesListItemCurrent',
- profileInfo.name)
- nameEl.textContent = displayName;
- },
- };
- var ProfileList = cr.ui.define('list');
- ProfileList.prototype = {
- __proto__: DeletableItemList.prototype,
- /** @inheritDoc */
- decorate: function() {
- DeletableItemList.prototype.decorate.call(this);
- this.selectionModel = new ListSingleSelectionModel();
- },
- /** @inheritDoc */
- createItem: function(pageInfo) {
- var item = new ProfileListItem(pageInfo);
- return item;
- },
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- ManageProfileOverlay.showDeleteDialog(this.dataModel.item(index));
- },
- /** @inheritDoc */
- activateItemAtIndex: function(index) {
- // Don't allow the user to edit a profile that is not current.
- var profileInfo = this.dataModel.item(index);
- if (profileInfo.isCurrentProfile)
- ManageProfileOverlay.showManageDialog(profileInfo);
- },
- };
- return {
- ProfileList: ProfileList
- };
- });
- // 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() {
- const ListItem = cr.ui.ListItem;
- const Grid = cr.ui.Grid;
- const ListSingleSelectionModel = cr.ui.ListSingleSelectionModel;
- /**
- * Creates a new profile icon grid item.
- * @param {Object} iconURL The profile icon URL.
- * @constructor
- * @extends {cr.ui.GridItem}
- */
- function ProfilesIconGridItem(iconURL) {
- var el = cr.doc.createElement('span');
- el.iconURL_ = iconURL;
- ProfilesIconGridItem.decorate(el);
- return el;
- }
- /**
- * Decorates an element as a profile grid item.
- * @param {!HTMLElement} el The element to decorate.
- */
- ProfilesIconGridItem.decorate = function(el) {
- el.__proto__ = ProfilesIconGridItem.prototype;
- el.decorate();
- };
- ProfilesIconGridItem.prototype = {
- __proto__: ListItem.prototype,
- /** @inheritDoc */
- decorate: function() {
- ListItem.prototype.decorate.call(this);
- var imageEl = cr.doc.createElement('img');
- imageEl.className = 'profile-icon';
- imageEl.src = this.iconURL_;
- this.appendChild(imageEl);
- this.className = 'profile-icon-grid-item';
- },
- };
- var ProfilesIconGrid = cr.ui.define('grid');
- ProfilesIconGrid.prototype = {
- __proto__: Grid.prototype,
- /** @inheritDoc */
- decorate: function() {
- Grid.prototype.decorate.call(this);
- this.selectionModel = new ListSingleSelectionModel();
- },
- /** @inheritDoc */
- createItem: function(iconURL) {
- return new ProfilesIconGridItem(iconURL);
- },
- };
- return {
- ProfilesIconGrid: ProfilesIconGrid
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- const ArrayDataModel = cr.ui.ArrayDataModel;
- /**
- * Encapsulated handling of search engine management page.
- * @constructor
- */
- function SearchEngineManager() {
- this.activeNavTab = null;
- OptionsPage.call(this, 'searchEngines',
- templateData.searchEngineManagerPageTabTitle,
- 'search-engine-manager-page');
- }
- cr.addSingletonGetter(SearchEngineManager);
- SearchEngineManager.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * List for default search engine options.
- * @private
- */
- defaultsList_: null,
- /**
- * List for other search engine options.
- * @private
- */
- othersList_: null,
- /**
- * List for extension keywords.
- * @private
- extensionList_ : null,
- /** inheritDoc */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- this.defaultsList_ = $('default-search-engine-list');
- this.setUpList_(this.defaultsList_);
- this.othersList_ = $('other-search-engine-list');
- this.setUpList_(this.othersList_);
- this.extensionList_ = $('extension-keyword-list');
- this.setUpList_(this.extensionList_);
- },
- /**
- * Sets up the given list as a search engine list
- * @param {List} list The list to set up.
- * @private
- */
- setUpList_: function(list) {
- options.search_engines.SearchEngineList.decorate(list);
- list.autoExpands = true;
- },
- /**
- * Updates the search engine list with the given entries.
- * @private
- * @param {Array} defaultEngines List of possible default search engines.
- * @param {Array} otherEngines List of other search engines.
- * @param {Array} keywords List of keywords from extensions.
- */
- updateSearchEngineList_: function(defaultEngines, otherEngines, keywords) {
- this.defaultsList_.dataModel = new ArrayDataModel(defaultEngines);
- otherEngines = otherEngines.map(function(x) {
- return [x, x['name'].toLocaleLowerCase()];
- }).sort(function(a,b){
- return a[1].localeCompare(b[1]);
- }).map(function(x){
- return x[0];
- });
- var othersModel = new ArrayDataModel(otherEngines);
- // Add a "new engine" row.
- othersModel.push({
- 'modelIndex': '-1',
- 'canBeEdited': true
- });
- this.othersList_.dataModel = othersModel;
- if (keywords.length > 0) {
- $('extension-keyword-div').hidden = false;
- var extensionsModel = new ArrayDataModel(keywords);
- this.extensionList_.dataModel = extensionsModel;
- } else {
- $('extension-keyword-div').hidden = true;
- }
- },
- };
- SearchEngineManager.updateSearchEngineList = function(defaultEngines,
- otherEngines,
- keywords) {
- SearchEngineManager.getInstance().updateSearchEngineList_(defaultEngines,
- otherEngines,
- keywords);
- };
- SearchEngineManager.validityCheckCallback = function(validity, modelIndex) {
- // Forward to both lists; the one without a matching modelIndex will ignore
- // it.
- SearchEngineManager.getInstance().defaultsList_.validationComplete(
- validity, modelIndex);
- SearchEngineManager.getInstance().othersList_.validationComplete(
- validity, modelIndex);
- };
- // Export
- return {
- SearchEngineManager: SearchEngineManager
- };
- });
- // 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.search_engines', function() {
- const InlineEditableItemList = options.InlineEditableItemList;
- const InlineEditableItem = options.InlineEditableItem;
- const ListSelectionController = cr.ui.ListSelectionController;
- /**
- * Creates a new search engine list item.
- * @param {Object} searchEnigne The search engine this represents.
- * @constructor
- * @extends {cr.ui.ListItem}
- */
- function SearchEngineListItem(searchEngine) {
- var el = cr.doc.createElement('div');
- el.searchEngine_ = searchEngine;
- SearchEngineListItem.decorate(el);
- return el;
- }
- /**
- * Decorates an element as a search engine list item.
- * @param {!HTMLElement} el The element to decorate.
- */
- SearchEngineListItem.decorate = function(el) {
- el.__proto__ = SearchEngineListItem.prototype;
- el.decorate();
- };
- SearchEngineListItem.prototype = {
- __proto__: InlineEditableItem.prototype,
- /**
- * Input field for editing the engine name.
- * @type {HTMLElement}
- * @private
- */
- nameField_: null,
- /**
- * Input field for editing the engine keyword.
- * @type {HTMLElement}
- * @private
- */
- keywordField_: null,
- /**
- * Input field for editing the engine url.
- * @type {HTMLElement}
- * @private
- */
- urlField_: null,
- /**
- * Whether or not an input validation request is currently outstanding.
- * @type {boolean}
- * @private
- */
- waitingForValidation_: false,
- /**
- * Whether or not the current set of input is known to be valid.
- * @type {boolean}
- * @private
- */
- currentlyValid_: false,
- /** @inheritDoc */
- decorate: function() {
- InlineEditableItem.prototype.decorate.call(this);
- var engine = this.searchEngine_;
- if (engine['modelIndex'] == '-1') {
- this.isPlaceholder = true;
- engine['name'] = '';
- engine['keyword'] = '';
- engine['url'] = '';
- }
- this.currentlyValid_ = !this.isPlaceholder;
- if (engine['default'])
- this.classList.add('default');
- this.deletable = engine['canBeRemoved'];
- // Construct the name column.
- var nameColEl = this.ownerDocument.createElement('div');
- nameColEl.className = 'name-column';
- nameColEl.classList.add('weakrtl');
- this.contentElement.appendChild(nameColEl);
- // Add the favicon.
- var faviconDivEl = this.ownerDocument.createElement('div');
- faviconDivEl.className = 'favicon';
- var imgEl = this.ownerDocument.createElement('img');
- imgEl.src = 'chrome://favicon/iconurl/' + engine['iconURL'];
- faviconDivEl.appendChild(imgEl);
- nameColEl.appendChild(faviconDivEl);
- var nameEl = this.createEditableTextCell(engine['displayName']);
- nameEl.classList.add('weakrtl');
- nameColEl.appendChild(nameEl);
- // Then the keyword column.
- var keywordEl = this.createEditableTextCell(engine['keyword']);
- keywordEl.className = 'keyword-column';
- keywordEl.classList.add('weakrtl');
- this.contentElement.appendChild(keywordEl);
- // And the URL column.
- var urlEl = this.createEditableTextCell(engine['url']);
- var urlWithButtonEl = this.ownerDocument.createElement('div');
- urlWithButtonEl.appendChild(urlEl);
- urlWithButtonEl.className = 'url-column';
- urlWithButtonEl.classList.add('weakrtl');
- this.contentElement.appendChild(urlWithButtonEl);
- // Add the Make Default button. Temporary until drag-and-drop re-ordering
- // is implemented. When this is removed, remove the extra div above.
- if (engine['canBeDefault']) {
- var makeDefaultButtonEl = this.ownerDocument.createElement('button');
- makeDefaultButtonEl.className = 'raw-button custom-appearance';
- makeDefaultButtonEl.textContent =
- templateData.makeDefaultSearchEngineButton;
- makeDefaultButtonEl.onclick = function(e) {
- chrome.send('managerSetDefaultSearchEngine', [engine['modelIndex']]);
- };
- // Don't select the row when clicking the button.
- makeDefaultButtonEl.onmousedown = function(e) {
- e.stopPropagation();
- };
- urlWithButtonEl.appendChild(makeDefaultButtonEl);
- }
- // Do final adjustment to the input fields.
- this.nameField_ = nameEl.querySelector('input');
- // The editable field uses the raw name, not the display name.
- this.nameField_.value = engine['name'];
- this.keywordField_ = keywordEl.querySelector('input');
- this.urlField_ = urlEl.querySelector('input');
- if (engine['urlLocked'])
- this.urlField_.disabled = true;
- if (this.isPlaceholder) {
- this.nameField_.placeholder =
- localStrings.getString('searchEngineTableNamePlaceholder');
- this.keywordField_.placeholder =
- localStrings.getString('searchEngineTableKeywordPlaceholder');
- this.urlField_.placeholder =
- localStrings.getString('searchEngineTableURLPlaceholder');
- }
- var fields = [ this.nameField_, this.keywordField_, this.urlField_ ];
- for (var i = 0; i < fields.length; i++) {
- fields[i].oninput = this.startFieldValidation_.bind(this);
- }
- // Listen for edit events.
- if (engine['canBeEdited']) {
- this.addEventListener('edit', this.onEditStarted_.bind(this));
- this.addEventListener('canceledit', this.onEditCancelled_.bind(this));
- this.addEventListener('commitedit', this.onEditCommitted_.bind(this));
- } else {
- this.editable = false;
- }
- },
- /** @inheritDoc */
- get currentInputIsValid() {
- return !this.waitingForValidation_ && this.currentlyValid_;
- },
- /** @inheritDoc */
- get hasBeenEdited() {
- var engine = this.searchEngine_;
- return this.nameField_.value != engine['name'] ||
- this.keywordField_.value != engine['keyword'] ||
- this.urlField_.value != engine['url'];
- },
- /**
- * Called when entering edit mode; starts an edit session in the model.
- * @param {Event} e The edit event.
- * @private
- */
- onEditStarted_: function(e) {
- var editIndex = this.searchEngine_['modelIndex'];
- chrome.send('editSearchEngine', [String(editIndex)]);
- this.startFieldValidation_();
- },
- /**
- * Called when committing an edit; updates the model.
- * @param {Event} e The end event.
- * @private
- */
- onEditCommitted_: function(e) {
- chrome.send('searchEngineEditCompleted', this.getInputFieldValues_());
- },
- /**
- * Called when cancelling an edit; informs the model and resets the control
- * states.
- * @param {Event} e The cancel event.
- * @private
- */
- onEditCancelled_: function() {
- chrome.send('searchEngineEditCancelled');
- // The name field has been automatically set to match the display name,
- // but it should use the raw name instead.
- this.nameField_.value = this.searchEngine_['name'];
- this.currentlyValid_ = !this.isPlaceholder;
- },
- /**
- * Returns the input field values as an array suitable for passing to
- * chrome.send. The order of the array is important.
- * @private
- * @return {array} The current input field values.
- */
- getInputFieldValues_: function() {
- return [ this.nameField_.value,
- this.keywordField_.value,
- this.urlField_.value ];
- },
- /**
- * Begins the process of asynchronously validing the input fields.
- * @private
- */
- startFieldValidation_: function() {
- this.waitingForValidation_ = true;
- var args = this.getInputFieldValues_();
- args.push(this.searchEngine_['modelIndex']);
- chrome.send('checkSearchEngineInfoValidity', args);
- },
- /**
- * Callback for the completion of an input validition check.
- * @param {Object} validity A dictionary of validitation results.
- */
- validationComplete: function(validity) {
- this.waitingForValidation_ = false;
- // TODO(stuartmorgan): Implement the full validation UI with
- // checkmark/exclamation mark icons and tooltips showing the errors.
- if (validity['name']) {
- this.nameField_.setCustomValidity('');
- } else {
- this.nameField_.setCustomValidity(
- templateData.editSearchEngineInvalidTitleToolTip);
- }
- if (validity['keyword']) {
- this.keywordField_.setCustomValidity('');
- } else {
- this.keywordField_.setCustomValidity(
- templateData.editSearchEngineInvalidKeywordToolTip);
- }
- if (validity['url']) {
- this.urlField_.setCustomValidity('');
- } else {
- this.urlField_.setCustomValidity(
- templateData.editSearchEngineInvalidURLToolTip);
- }
- this.currentlyValid_ = validity['name'] && validity['keyword'] &&
- validity['url'];
- },
- };
- var SearchEngineList = cr.ui.define('list');
- SearchEngineList.prototype = {
- __proto__: InlineEditableItemList.prototype,
- /** @inheritDoc */
- createItem: function(searchEngine) {
- return new SearchEngineListItem(searchEngine);
- },
- /** @inheritDoc */
- deleteItemAtIndex: function(index) {
- var modelIndex = this.dataModel.item(index)['modelIndex']
- chrome.send('removeSearchEngine', [String(modelIndex)]);
- },
- /**
- * Passes the results of an input validation check to the requesting row
- * if it's still being edited.
- * @param {number} modelIndex The model index of the item that was checked.
- * @param {Object} validity A dictionary of validitation results.
- */
- validationComplete: function(validity, modelIndex) {
- // If it's not still being edited, it no longer matters.
- var currentSelection = this.selectedItem;
- if (!currentSelection)
- return;
- var listItem = this.getListItem(currentSelection);
- if (listItem.editing && currentSelection['modelIndex'] == modelIndex)
- listItem.validationComplete(validity);
- },
- };
- // Export
- return {
- SearchEngineList: SearchEngineList
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- /**
- * Encapsulated handling of a search bubble.
- * @constructor
- */
- function SearchBubble(text) {
- var el = cr.doc.createElement('div');
- SearchBubble.decorate(el);
- el.textContent = text;
- return el;
- }
- SearchBubble.decorate = function(el) {
- el.__proto__ = SearchBubble.prototype;
- el.decorate();
- };
- SearchBubble.prototype = {
- __proto__: HTMLDivElement.prototype,
- decorate: function() {
- this.className = 'search-bubble';
- // We create a timer to periodically update the position of the bubbles.
- // While this isn't all that desirable, it's the only sure-fire way of
- // making sure the bubbles stay in the correct location as sections
- // may dynamically change size at any time.
- var self = this;
- this.intervalId = setInterval(this.updatePosition.bind(this), 250);
- },
- /**
- * Attach the bubble to the element.
- */
- attachTo: function(element) {
- var parent = element.parentElement;
- if (!parent)
- return;
- if (parent.tagName == 'TD') {
- // To make absolute positioning work inside a table cell we need
- // to wrap the bubble div into another div with position:relative.
- // This only works properly if the element is the first child of the
- // table cell which is true for all options pages.
- this.wrapper = cr.doc.createElement('div');
- this.wrapper.className = 'search-bubble-wrapper';
- this.wrapper.appendChild(this);
- parent.insertBefore(this.wrapper, element);
- } else {
- parent.insertBefore(this, element);
- }
- },
- /**
- * Clear the interval timer and remove the element from the page.
- */
- dispose: function() {
- clearInterval(this.intervalId);
- var child = this.wrapper || this;
- var parent = child.parentNode;
- if (parent)
- parent.removeChild(child);
- },
- /**
- * Update the position of the bubble. Called at creation time and then
- * periodically while the bubble remains visible.
- */
- updatePosition: function() {
- // This bubble is 'owned' by the next sibling.
- var owner = (this.wrapper || this).nextSibling;
- // If there isn't an offset parent, we have nothing to do.
- if (!owner.offsetParent)
- return;
- // Position the bubble below the location of the owner.
- var left = owner.offsetLeft + owner.offsetWidth / 2 -
- this.offsetWidth / 2;
- var top = owner.offsetTop + owner.offsetHeight;
- // Update the position in the CSS. Cache the last values for
- // best performance.
- if (left != this.lastLeft) {
- this.style.left = left + 'px';
- this.lastLeft = left;
- }
- if (top != this.lastTop) {
- this.style.top = top + 'px';
- this.lastTop = top;
- }
- }
- }
- /**
- * Encapsulated handling of the search page.
- * @constructor
- */
- function SearchPage() {
- OptionsPage.call(this, 'search', templateData.searchPageTabTitle,
- 'searchPage');
- }
- cr.addSingletonGetter(SearchPage);
- SearchPage.prototype = {
- // Inherit SearchPage from OptionsPage.
- __proto__: OptionsPage.prototype,
- /**
- * A boolean to prevent recursion. Used by setSearchText_().
- * @type {Boolean}
- * @private
- */
- insideSetSearchText_: false,
- /**
- * Initialize the page.
- */
- initializePage: function() {
- // Call base class implementation to start preference initialization.
- OptionsPage.prototype.initializePage.call(this);
- var self = this;
- // Create a search field element.
- var searchField = document.createElement('input');
- searchField.id = 'search-field';
- searchField.type = 'search';
- searchField.incremental = true;
- searchField.placeholder = localStrings.getString('searchPlaceholder');
- searchField.setAttribute('aria-label', searchField.placeholder);
- this.searchField = searchField;
- // Replace the contents of the navigation tab with the search field.
- self.tab.textContent = '';
- self.tab.appendChild(searchField);
- self.tab.onclick = self.tab.onkeydown = self.tab.onkeypress = undefined;
- self.tab.tabIndex = -1;
- self.tab.setAttribute('role', '');
- // Don't allow the focus on the search navbar. http://crbug.com/77989
- self.tab.onfocus = self.tab.blur;
- // Handle search events. (No need to throttle, WebKit's search field
- // will do that automatically.)
- searchField.onsearch = function(e) {
- self.setSearchText_(this.value);
- };
- // We update the history stack every time the search field blurs. This way
- // we get a history entry for each search, roughly, but not each letter
- // typed.
- searchField.onblur = function(e) {
- var query = SearchPage.canonicalizeQuery(searchField.value);
- if (!query)
- return;
- // Don't push the same page onto the history stack more than once (if
- // the user clicks in the search field and away several times).
- var currentHash = location.hash;
- var newHash = '#' + escape(query);
- if (currentHash == newHash)
- return;
- // If there is no hash on the current URL, the history entry has no
- // search query. Replace the history entry with no search with an entry
- // that does have a search. Otherwise, add it onto the history stack.
- var historyFunction = currentHash ? window.history.pushState :
- window.history.replaceState;
- historyFunction.call(
- window.history,
- {pageName: self.name},
- self.title,
- '/' + self.name + newHash);
- };
- // Install handler for key presses.
- document.addEventListener('keydown',
- this.keyDownEventHandler_.bind(this));
- // Focus the search field by default.
- searchField.focus();
- },
- /**
- * @inheritDoc
- */
- get sticky() {
- return true;
- },
- /**
- * Called after this page has shown.
- */
- didShowPage: function() {
- // This method is called by the Options page after all pages have
- // had their visibilty attribute set. At this point we can perform the
- // search specific DOM manipulation.
- this.setSearchActive_(true);
- },
- /**
- * Called before this page will be hidden.
- */
- willHidePage: function() {
- // This method is called by the Options page before all pages have
- // their visibilty attribute set. Before that happens, we need to
- // undo the search specific DOM manipulation that was performed in
- // didShowPage.
- this.setSearchActive_(false);
- },
- /**
- * Update the UI to reflect whether we are in a search state.
- * @param {boolean} active True if we are on the search page.
- * @private
- */
- setSearchActive_: function(active) {
- // It's fine to exit if search wasn't active and we're not going to
- // activate it now.
- if (!this.searchActive_ && !active)
- return;
- this.searchActive_ = active;
- if (active) {
- var hash = location.hash;
- if (hash)
- this.searchField.value = unescape(hash.slice(1));
- } else {
- // Just wipe out any active search text since it's no longer relevant.
- this.searchField.value = '';
- }
- var pagesToSearch = this.getSearchablePages_();
- for (var key in pagesToSearch) {
- var page = pagesToSearch[key];
- if (!active)
- page.visible = false;
- // Update the visible state of all top-level elements that are not
- // sections (ie titles, button strips). We do this before changing
- // the page visibility to avoid excessive re-draw.
- for (var i = 0, childDiv; childDiv = page.pageDiv.children[i]; i++) {
- if (childDiv.classList.contains('displaytable')) {
- childDiv.setAttribute('searching', active ? 'true' : 'false');
- for (var j = 0, subDiv; subDiv = childDiv.children[j]; j++) {
- if (active) {
- if (subDiv.tagName != 'SECTION')
- subDiv.classList.add('search-hidden');
- } else {
- subDiv.classList.remove('search-hidden');
- }
- }
- } else {
- if (active)
- childDiv.classList.add('search-hidden');
- else
- childDiv.classList.remove('search-hidden');
- }
- }
- if (active) {
- // When search is active, remove the 'hidden' tag. This tag may have
- // been added by the OptionsPage.
- page.pageDiv.hidden = false;
- }
- }
- if (active) {
- this.setSearchText_(this.searchField.value);
- } else {
- // After hiding all page content, remove any search results.
- this.unhighlightMatches_();
- this.removeSearchBubbles_();
- }
- },
- /**
- * Set the current search criteria.
- * @param {string} text Search text.
- * @private
- */
- setSearchText_: function(text) {
- // Prevent recursive execution of this method.
- if (this.insideSetSearchText_) return;
- this.insideSetSearchText_ = true;
- // Cleanup the search query string.
- text = SearchPage.canonicalizeQuery(text);
- // Notify listeners about the new search query, some pages may wish to
- // show/hide elements based on the query.
- var event = new cr.Event('searchChanged');
- event.searchText = text;
- this.dispatchEvent(event);
- // Toggle the search page if necessary.
- if (text.length) {
- if (!this.searchActive_)
- OptionsPage.navigateToPage(this.name);
- } else {
- if (this.searchActive_)
- OptionsPage.showDefaultPage();
- this.insideSetSearchText_ = false;
- return;
- }
- var foundMatches = false;
- var bubbleControls = [];
- // Remove any prior search results.
- this.unhighlightMatches_();
- this.removeSearchBubbles_();
- // Generate search text by applying lowercase and escaping any characters
- // that would be problematic for regular expressions.
- var searchText =
- text.toLowerCase().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
- // Generate a regular expression and replace string for hilighting
- // search terms.
- var regEx = new RegExp('(' + searchText + ')', 'ig');
- var replaceString = '<span class="search-highlighted">$1</span>';
- // Initialize all sections. If the search string matches a title page,
- // show sections for that page.
- var page, pageMatch, childDiv, length;
- var pagesToSearch = this.getSearchablePages_();
- for (var key in pagesToSearch) {
- page = pagesToSearch[key];
- pageMatch = false;
- if (searchText.length) {
- pageMatch = this.performReplace_(regEx, replaceString, page.tab);
- }
- if (pageMatch)
- foundMatches = true;
- var elements = page.pageDiv.querySelectorAll('.displaytable > section');
- for (var i = 0, node; node = elements[i]; i++) {
- if (pageMatch)
- node.classList.remove('search-hidden');
- else
- node.classList.add('search-hidden');
- }
- }
- if (searchText.length) {
- // Search all top-level sections for anchored string matches.
- for (var key in pagesToSearch) {
- page = pagesToSearch[key];
- var elements =
- page.pageDiv.querySelectorAll('.displaytable > section');
- for (var i = 0, node; node = elements[i]; i++) {
- if (this.performReplace_(regEx, replaceString, node)) {
- node.classList.remove('search-hidden');
- foundMatches = true;
- }
- }
- }
- // Search all sub-pages, generating an array of top-level sections that
- // we need to make visible.
- var subPagesToSearch = this.getSearchableSubPages_();
- var control, node;
- for (var key in subPagesToSearch) {
- page = subPagesToSearch[key];
- if (this.performReplace_(regEx, replaceString, page.pageDiv)) {
- // Reveal the section for this search result.
- section = page.associatedSection;
- if (section)
- section.classList.remove('search-hidden');
- // Identify any controls that should have bubbles.
- var controls = page.associatedControls;
- if (controls) {
- length = controls.length;
- for (var i = 0; i < length; i++)
- bubbleControls.push(controls[i]);
- }
- foundMatches = true;
- }
- }
- }
- // Configure elements on the search results page based on search results.
- if (foundMatches)
- $('searchPageNoMatches').classList.add('search-hidden');
- else
- $('searchPageNoMatches').classList.remove('search-hidden');
- // Create search balloons for sub-page results.
- length = bubbleControls.length;
- for (var i = 0; i < length; i++)
- this.createSearchBubble_(bubbleControls[i], text);
- // Cleanup the recursion-prevention variable.
- this.insideSetSearchText_ = false;
- },
- /**
- * Performs a string replacement based on a regex and replace string.
- * @param {RegEx} regex A regular expression for finding search matches.
- * @param {String} replace A string to apply the replace operation.
- * @param {Element} element An HTML container element.
- * @returns {Boolean} true if the element was changed.
- * @private
- */
- performReplace_: function(regex, replace, element) {
- var found = false;
- var div, child, tmp;
- // Walk the tree, searching each TEXT node.
- var walker = document.createTreeWalker(element,
- NodeFilter.SHOW_TEXT,
- null,
- false);
- var node = walker.nextNode();
- while (node) {
- // Perform a search and replace on the text node value.
- var newValue = node.nodeValue.replace(regex, replace);
- if (newValue != node.nodeValue) {
- // The text node has changed so that means we found at least one
- // match.
- found = true;
- // Create a temporary div element and set the innerHTML to the new
- // value.
- div = document.createElement('div');
- div.innerHTML = newValue;
- // Insert all the child nodes of the temporary div element into the
- // document, before the original node.
- child = div.firstChild;
- while (child = div.firstChild) {
- node.parentNode.insertBefore(child, node);
- };
- // Delete the old text node and advance the walker to the next
- // node.
- tmp = node;
- node = walker.nextNode();
- tmp.parentNode.removeChild(tmp);
- } else {
- node = walker.nextNode();
- }
- }
- return found;
- },
- /**
- * Removes all search highlight tags from the document.
- * @private
- */
- unhighlightMatches_: function() {
- // Find all search highlight elements.
- var elements = document.querySelectorAll('.search-highlighted');
- // For each element, remove the highlighting.
- var parent, i;
- for (var i = 0, node; node = elements[i]; i++) {
- parent = node.parentNode;
- // Replace the highlight element with the first child (the text node).
- parent.replaceChild(node.firstChild, node);
- // Normalize the parent so that multiple text nodes will be combined.
- parent.normalize();
- }
- },
- /**
- * Creates a search result bubble attached to an element.
- * @param {Element} element An HTML element, usually a button.
- * @param {string} text A string to show in the bubble.
- * @private
- */
- createSearchBubble_: function(element, text) {
- // avoid appending multiple bubbles to a button.
- var sibling = element.previousElementSibling;
- if (sibling && (sibling.classList.contains('search-bubble') ||
- sibling.classList.contains('search-bubble-wrapper')))
- return;
- var parent = element.parentElement;
- if (parent) {
- var bubble = new SearchBubble(text);
- bubble.attachTo(element);
- bubble.updatePosition();
- }
- },
- /**
- * Removes all search match bubbles.
- * @private
- */
- removeSearchBubbles_: function() {
- var elements = document.querySelectorAll('.search-bubble');
- var length = elements.length;
- for (var i = 0; i < length; i++)
- elements[i].dispose();
- },
- /**
- * Builds a list of top-level pages to search. Omits the search page and
- * all sub-pages.
- * @returns {Array} An array of pages to search.
- * @private
- */
- getSearchablePages_: function() {
- var name, page, pages = [];
- for (name in OptionsPage.registeredPages) {
- if (name != this.name) {
- page = OptionsPage.registeredPages[name];
- if (!page.parentPage)
- pages.push(page);
- }
- }
- return pages;
- },
- /**
- * Builds a list of sub-pages (and overlay pages) to search. Ignore pages
- * that have no associated controls.
- * @returns {Array} An array of pages to search.
- * @private
- */
- getSearchableSubPages_: function() {
- var name, pageInfo, page, pages = [];
- for (name in OptionsPage.registeredPages) {
- page = OptionsPage.registeredPages[name];
- if (page.parentPage && page.associatedSection)
- pages.push(page);
- }
- for (name in OptionsPage.registeredOverlayPages) {
- page = OptionsPage.registeredOverlayPages[name];
- if (page.associatedSection && page.pageDiv != undefined)
- pages.push(page);
- }
- return pages;
- },
- /**
- * A function to handle key press events.
- * @return {Event} a keydown event.
- * @private
- */
- keyDownEventHandler_: function(event) {
- const ESCAPE_KEY_CODE = 27;
- const FORWARD_SLASH_KEY_CODE = 191;
- switch(event.keyCode) {
- case ESCAPE_KEY_CODE:
- if (event.target == this.searchField) {
- this.setSearchText_('');
- this.searchField.blur();
- event.stopPropagation();
- event.preventDefault();
- }
- break;
- case FORWARD_SLASH_KEY_CODE:
- if (!/INPUT|SELECT|BUTTON|TEXTAREA/.test(event.target.tagName) &&
- !event.ctrlKey && !event.altKey) {
- this.searchField.focus();
- event.stopPropagation();
- event.preventDefault();
- }
- break;
- }
- },
- };
- /**
- * Standardizes a user-entered text query by removing extra whitespace.
- * @param {string} The user-entered text.
- * @return {string} The trimmed query.
- */
- SearchPage.canonicalizeQuery = function(text) {
- // Trim beginning and ending whitespace.
- return text.replace(/^\s+|\s+$/g, '');
- };
- // Export
- return {
- SearchPage: SearchPage
- };
- });
- // 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() {
- const OptionsPage = options.OptionsPage;
- // Variable to track if a captcha challenge was issued. If this gets set to
- // true, it stays that way until we are told about successful login from
- // the browser. This means subsequent errors (like invalid password) are
- // rendered in the captcha state, which is basically identical except we
- // don't show the top error blurb "Error Signing in" or the "Create
- // account" link.
- var captchaChallengeActive_ = false;
- // True if the synced account uses a custom passphrase.
- var usePassphrase_ = false;
- // True if the synced account uses 'encrypt everything'.
- var useEncryptEverything_ = false;
- /**
- * SyncSetupOverlay class
- * Encapsulated handling of the 'Sync Setup' overlay page.
- * @class
- */
- function SyncSetupOverlay() {
- OptionsPage.call(this, 'syncSetup',
- templateData.syncSetupOverlayTitle,
- 'sync-setup-overlay');
- }
- cr.addSingletonGetter(SyncSetupOverlay);
- SyncSetupOverlay.prototype = {
- __proto__: OptionsPage.prototype,
- /**
- * Initializes the page.
- */
- initializePage: function() {
- OptionsPage.prototype.initializePage.call(this);
- var self = this;
- $('gaia-login-form').onsubmit = function() {
- self.sendCredentialsAndClose_();
- return false;
- };
- $('google-option').onchange = $('explicit-option').onchange = function() {
- self.onPassphraseRadioChanged_();
- };
- $('choose-datatypes-cancel').onclick =
- $('sync-setup-cancel').onclick =
- $('confirm-everything-cancel').onclick =
- $('stop-syncing-cancel').onclick = function() {
- self.closeOverlay_();
- };
- $('confirm-everything-ok').onclick = function() {
- self.sendConfiguration_();
- };
- $('stop-syncing-ok').onclick = function() {
- chrome.send('stopSyncing');
- self.closeOverlay_();
- };
- },
- showOverlay_: function() {
- OptionsPage.navigateToPage('syncSetup');
- },
- closeOverlay_: function() {
- OptionsPage.closeOverlay();
- },
- /** @inheritDoc */
- didShowPage: function() {
- chrome.send('SyncSetupAttachHandler');
- },
- /** @inheritDoc */
- didClosePage: function() {
- chrome.send('SyncSetupDidClosePage');
- },
- getEncryptionRadioCheckedValue_: function() {
- var f = $('choose-data-types-form');
- for (var i = 0; i < f.encrypt.length; ++i) {
- if (f.encrypt[i].checked) {
- return f.encrypt[i].value;
- }
- }
- return undefined;
- },
- getPassphraseRadioCheckedValue_: function() {
- var f = $('choose-data-types-form');
- for (var i = 0; i < f.option.length; ++i) {
- if (f.option[i].checked) {
- return f.option[i].value;
- }
- }
- return undefined;
- },
- disableEncryptionRadioGroup_: function() {
- var f = $('choose-data-types-form');
- for (var i = 0; i < f.encrypt.length; ++i)
- f.encrypt[i].disabled = true;
- },
- onPassphraseRadioChanged_: function() {
- var visible = this.getPassphraseRadioCheckedValue_() == "explicit";
- $('sync-custom-passphrase').hidden = !visible;
- },
- checkAllDataTypeCheckboxes_: function() {
- var checkboxes = document.getElementsByName("dataTypeCheckbox");
- for (var i = 0; i < checkboxes.length; i++) {
- // Only check the visible ones (since there's no way to uncheck
- // the invisible ones).
- if (checkboxes[i].parentElement.className == "sync-item-show") {
- checkboxes[i].checked = true;
- }
- }
- },
- setDataTypeCheckboxesEnabled_: function(enabled) {
- var checkboxes = document.getElementsByName("dataTypeCheckbox");
- var labels = document.getElementsByName("dataTypeLabel");
- for (var i = 0; i < checkboxes.length; i++) {
- checkboxes[i].disabled = !enabled;
- if (checkboxes[i].disabled) {
- labels[i].className = "sync-label-inactive";
- } else {
- labels[i].className = "sync-label-active";
- }
- }
- },
- setCheckboxesToKeepEverythingSynced_: function(value) {
- this.setDataTypeCheckboxesEnabled_(!value);
- if (value)
- this.checkAllDataTypeCheckboxes_();
- },
- // Returns true if at least one data type is enabled and no data types are
- // checked. (If all data type checkboxes are disabled, it's because "keep
- // everything synced" is checked.)
- noDataTypesChecked_: function() {
- var checkboxes = document.getElementsByName("dataTypeCheckbox");
- var atLeastOneChecked = false;
- var atLeastOneEnabled = false;
- for (var i = 0; i < checkboxes.length; i++) {
- if (!checkboxes[i].disabled &&
- checkboxes[i].parentElement.className == "sync-item-show") {
- atLeastOneEnabled = true;
- if (checkboxes[i].checked) {
- atLeastOneChecked = true;
- }
- }
- }
- return atLeastOneEnabled && !atLeastOneChecked;
- },
- checkPassphraseMatch_: function() {
- var emptyError = $('empty-error');
- var mismatchError = $('mismatch-error');
- emptyError.hidden = true;
- mismatchError.hidden = true;
- var f = $('choose-data-types-form');
- if (this.getPassphraseRadioCheckedValue_() != "explicit" ||
- $('google-option').disabled)
- return true;
- var customPassphrase = $('custom-passphrase');
- if (customPassphrase.value.length == 0) {
- emptyError.hidden = false;
- return false;
- }
- var confirmPassphrase = $('confirm-passphrase');
- if (confirmPassphrase.value != customPassphrase.value) {
- mismatchError.hidden = false;
- return false;
- }
- return true;
- },
- sendConfiguration_: function() {
- // Trying to submit, so hide previous errors.
- $('error-text').hidden = true;
- if (this.noDataTypesChecked_()) {
- $('error-text').hidden = false;
- return;
- }
- var f = $('choose-data-types-form');
- var syncAll = $('sync-select-datatypes').selectedIndex == 0;
- var encryptAllData = this.getEncryptionRadioCheckedValue_() == 'all';
- var usePassphrase;
- var customPassphrase;
- var googlePassphrase = false;
- if (!$('sync-existing-passphrase-container').hidden) {
- // If we were prompted for an existing passphrase, use it.
- customPassphrase = f.passphrase.value;
- usePassphrase = true;
- // If we were displaying the "enter your old google password" prompt,
- // then that means this is the user's google password.
- googlePassphrase = !$('google-passphrase-needed-body').hidden;
- // We allow an empty passphrase, in case the user has disabled
- // all their encrypted datatypes. In that case, the PSS will accept
- // the passphrase and finish configuration. If the user has enabled
- // encrypted datatypes, the PSS will prompt again specifying that the
- // passphrase failed.
- } else if (!$('google-option').disabled &&
- this.getPassphraseRadioCheckedValue_() == 'explicit') {
- // The user is setting a custom passphrase for the first time.
- if (!this.checkPassphraseMatch_())
- return;
- customPassphrase = $('custom-passphrase').value;
- usePassphrase = true;
- } else {
- // The user is not setting a custom passphrase.
- usePassphrase = false;
- }
- // Don't allow the user to tweak the settings once we send the
- // configuration to the backend.
- this.setInputElementsDisabledState_(true);
- this.animateDisableLink_($('use-default-link'), true, null);
- // These values need to be kept in sync with where they are read in
- // SyncSetupFlow::GetDataTypeChoiceData().
- var result = JSON.stringify({
- "syncAllDataTypes": syncAll,
- "syncBookmarks": syncAll || $('bookmarks-checkbox').checked,
- "syncPreferences": syncAll || $('preferences-checkbox').checked,
- "syncThemes": syncAll || $('themes-checkbox').checked,
- "syncPasswords": syncAll || $('passwords-checkbox').checked,
- "syncAutofill": syncAll || $('autofill-checkbox').checked,
- "syncExtensions": syncAll || $('extensions-checkbox').checked,
- "syncTypedUrls": syncAll || $('typed-urls-checkbox').checked,
- "syncApps": syncAll || $('apps-checkbox').checked,
- "syncSessions": syncAll || $('sessions-checkbox').checked,
- "encryptAllData": encryptAllData,
- "usePassphrase": usePassphrase,
- "isGooglePassphrase": googlePassphrase,
- "passphrase": customPassphrase
- });
- chrome.send('SyncSetupConfigure', [result]);
- },
- /**
- * Sets the disabled property of all input elements within the 'Customize
- * Sync Preferences' screen. This is used to prohibit the user from changing
- * the inputs after confirming the customized sync preferences, or resetting
- * the state when re-showing the dialog.
- * @param disabled True if controls should be set to disabled.
- * @private
- */
- setInputElementsDisabledState_: function(disabled) {
- var configureElements =
- $('customize-sync-preferences').querySelectorAll('input');
- for (var i = 0; i < configureElements.length; i++)
- configureElements[i].disabled = disabled;
- $('sync-select-datatypes').disabled = disabled;
- var self = this;
- this.animateDisableLink_($('customize-link'), disabled, function() {
- self.showCustomizePage_(null, true);
- });
- },
- /**
- * Animate a link being enabled/disabled. The link is hidden by animating
- * its opacity, but to ensure the user doesn't click it during that time,
- * its onclick handler is changed to null as well.
- * @param elt The anchor element to enable/disable.
- * @param disabled True if the link should be disabled.
- * @param enabledFunction The onclick handler when the link is enabled.
- * @private
- */
- animateDisableLink_: function(elt, disabled, enabledFunction) {
- if (disabled) {
- elt.classList.add('transparent');
- elt.onclick = null;
- elt.addEventListener('webkitTransitionEnd', function f(e) {
- if (e.propertyName != 'opacity')
- return;
- elt.removeEventListener('webkitTransitionEnd', f);
- elt.classList.remove('transparent');
- elt.hidden = true;
- });
- } else {
- elt.hidden = false;
- elt.onclick = enabledFunction;
- }
- },
- setChooseDataTypesCheckboxes_: function(args) {
- var datatypeSelect = document.getElementById('sync-select-datatypes');
- datatypeSelect.selectedIndex = args.syncAllDataTypes ? 0 : 1;
- $('bookmarks-checkbox').checked = args.syncBookmarks;
- $('preferences-checkbox').checked = args.syncPreferences;
- $('themes-checkbox').checked = args.syncThemes;
- if (args.passwordsRegistered) {
- $('passwords-checkbox').checked = args.syncPasswords;
- $('passwords-item').className = "sync-item-show";
- } else {
- $('passwords-item').className = "sync-item-hide";
- }
- if (args.autofillRegistered) {
- $('autofill-checkbox').checked = args.syncAutofill;
- $('autofill-item').className = "sync-item-show";
- } else {
- $('autofill-item').className = "sync-item-hide";
- }
- if (args.extensionsRegistered) {
- $('extensions-checkbox').checked = args.syncExtensions;
- $('extensions-item').className = "sync-item-show";
- } else {
- $('extensions-item').className = "sync-item-hide";
- }
- if (args.typedUrlsRegistered) {
- $('typed-urls-checkbox').checked = args.syncTypedUrls;
- $('omnibox-item').className = "sync-item-show";
- } else {
- $('omnibox-item').className = "sync-item-hide";
- }
- if (args.appsRegistered) {
- $('apps-checkbox').checked = args.syncApps;
- $('apps-item').className = "sync-item-show";
- } else {
- $('apps-item').className = "sync-item-hide";
- }
- if (args.sessionsRegistered) {
- $('sessions-checkbox').checked = args.syncSessions;
- $('sessions-item').className = "sync-item-show";
- } else {
- $('sessions-item').className = "sync-item-hide";
- }
- this.setCheckboxesToKeepEverythingSynced_(args.syncAllDataTypes);
- },
- setEncryptionRadios_: function(args) {
- if (args['encryptAllData']) {
- $('encrypt-all-option').checked = true;
- this.disableEncryptionRadioGroup_();
- } else {
- $('encrypt-sensitive-option').checked = true;
- }
- },
- setPassphraseRadios_: function(args) {
- if (args['usePassphrase']) {
- $('explicit-option').checked = true;
- // The passphrase, once set, cannot be unset, but we show a reset link.
- $('explicit-option').disabled = true;
- $('google-option').disabled = true;
- $('sync-custom-passphrase').hidden = true;
- } else {
- $('google-option').checked = true;
- }
- },
- setCheckboxesAndErrors_: function(args) {
- this.setChooseDataTypesCheckboxes_(args);
- this.setEncryptionRadios_(args);
- this.setPassphraseRadios_(args);
- },
- showConfigure_: function(args) {
- var datatypeSelect = document.getElementById('sync-select-datatypes');
- var self = this;
- datatypeSelect.onchange = function() {
- var syncAll = this.selectedIndex == 0;
- self.setCheckboxesToKeepEverythingSynced_(syncAll);
- };
- this.resetPage_('sync-setup-configure');
- $('sync-setup-configure').hidden = false;
- // onsubmit is changed when submitting a passphrase. Reset it to its
- // default.
- $('choose-data-types-form').onsubmit = function() {
- self.sendConfiguration_();
- return false;
- };
- if (args) {
- if (!args['encryptionEnabled'])
- $('customize-sync-encryption').hidden = true;
- this.setCheckboxesAndErrors_(args);
- this.useEncryptEverything_ = args['encryptAllData'];
- // Whether to display the 'Sync everything' confirmation page or the
- // customize data types page.
- var syncAllDataTypes = args['syncAllDataTypes'];
- this.usePassphrase_ = args['usePassphrase'];
- if (args['showSyncEverythingPage'] == false || this.usePassphrase_ ||
- syncAllDataTypes == false || args['show_passphrase']) {
- this.showCustomizePage_(args, syncAllDataTypes);
- } else {
- this.showSyncEverythingPage_();
- }
- }
- },
- showSyncEverythingPage_: function() {
- $('confirm-sync-preferences').hidden = false;
- $('customize-sync-preferences').hidden = true;
- // Reset the selection to 'Sync everything'.
- $('sync-select-datatypes').selectedIndex = 0;
- // The default state is to sync everything.
- this.setCheckboxesToKeepEverythingSynced_(true);
- // Encrypt passwords is the default, but don't set it if the previously
- // synced account is already set to encrypt everything.
- if (!this.useEncryptEverything_)
- $('encrypt-sensitive-option').checked = true;
- // If the account is not synced with a custom passphrase, reset the
- // passphrase radio when switching to the 'Sync everything' page.
- if (!this.usePassphrase_) {
- $('google-option').checked = true;
- $('sync-custom-passphrase').hidden = true;
- }
- $('confirm-everything-ok').focus();
- },
- /**
- * Reveals the UI for entering a custom passphrase during initial setup.
- * This happens if the user has previously enabled a custom passphrase on a
- * different machine.
- * @param {Array} args The args that contain the passphrase UI
- * configuration.
- * @private
- */
- showPassphraseContainer_: function(args) {
- // Once we require a passphrase, we prevent the user from returning to
- // the Sync Everything pane.
- $('use-default-link').hidden = true;
- $('sync-custom-passphrase-container').hidden = true;
- $('sync-existing-passphrase-container').hidden = false;
- $('passphrase-rejected-body').hidden = true;
- $('normal-body').hidden = true;
- $('google-passphrase-needed-body').hidden = true;
- // Display the correct prompt to the user depending on what type of
- // passphrase is needed.
- if (args["need_google_passphrase"])
- $('google-passphrase-needed-body').hidden = false;
- else if (args["passphrase_creation_rejected"])
- $('passphrase-rejected-body').hidden = false;
- else
- $('normal-body').hidden = false;
- $('incorrect-passphrase').hidden = !args["passphrase_setting_rejected"];
- $('sync-passphrase-warning').hidden = false;
- $('passphrase').focus();
- },
- showCustomizePage_: function(args, syncEverything) {
- $('confirm-sync-preferences').hidden = true;
- $('customize-sync-preferences').hidden = false;
- $('sync-custom-passphrase-container').hidden = false;
- $('sync-existing-passphrase-container').hidden = true;
- // If the user has selected the 'Customize' page on initial set up, it's
- // likely he intends to change the data types. Select the
- // 'Choose data types' option in this case.
- var index = syncEverything ? 0 : 1;
- document.getElementById('sync-select-datatypes').selectedIndex = index;
- this.setDataTypeCheckboxesEnabled_(!syncEverything);
- // The passphrase input may need to take over focus from the OK button, so
- // set focus before that logic.
- $('choose-datatypes-ok').focus();
- if (args && args['show_passphrase']) {
- this.showPassphraseContainer_(args);
- } else {
- // We only show the "Use Default" link if we're not prompting for an
- // existing passphrase.
- var self = this;
- this.animateDisableLink_($('use-default-link'), false, function() {
- self.showSyncEverythingPage_();
- });
- }
- },
- attach_: function() {
- chrome.send('SyncSetupAttachHandler');
- },
- showSyncSetupPage_: function(page, args) {
- if (page == 'settingUp') {
- this.setThrobbersVisible_(true);
- return;
- } else {
- this.setThrobbersVisible_(false);
- }
- // Hide an existing visible overlay.
- var overlay = $('sync-setup-overlay');
- for (var i = 0; i < overlay.children.length; i++)
- overlay.children[i].hidden = true;
- this.setInputElementsDisabledState_(false);
- if (page == 'login')
- this.showGaiaLogin_(args);
- else if (page == 'configure' || page == 'passphrase')
- this.showConfigure_(args);
- if (page == 'done')
- this.closeOverlay_();
- else
- this.showOverlay_();
- },
- /**
- * Changes the visibility of throbbers on this page.
- * @param {boolean} visible Whether or not to set all throbber nodes
- * visible.
- */
- setThrobbersVisible_: function(visible) {
- var throbbers = document.getElementsByClassName("throbber");
- for (var i = 0; i < throbbers.length; i++)
- throbbers[i].style.visibility = visible ? "visible" : "hidden";
- },
- loginSetFocus_: function() {
- var email = $('gaia-email');
- var passwd = $('gaia-passwd');
- if (email && (email.value == null || email.value == "")) {
- email.focus();
- } else if (passwd) {
- passwd.focus();
- }
- },
- /**
- * Get the login email text input DOM element.
- * @return {DOMElement} The login email text input.
- * @private
- */
- getLoginEmail_: function() {
- return $('gaia-email');
- },
- /**
- * Get the login password text input DOM element.
- * @return {DOMElement} The login password text input.
- * @private
- */
- getLoginPasswd_: function() {
- return $('gaia-passwd');
- },
- /**
- * Get the sign in button DOM element.
- * @return {DOMElement} The sign in button.
- * @private
- */
- getSignInButton_: function() {
- return $('sign-in');
- },
- showAccessCodeRequired_: function() {
- $('password-row').hidden = true;
- $('email-row').hidden = true;
- $('access-code-input-row').hidden = false;
- $('access-code').disabled = false;
- $('access-code').focus();
- },
- showCaptcha_: function(args) {
- this.captchaChallengeActive_ = true;
- // The captcha takes up lots of space, so make room.
- $('top-blurb-error').hidden = true;
- $('create-account-div').hidden = true;
- // It's showtime for the captcha now.
- $('captcha-div').hidden = false;
- $('gaia-email').disabled = true;
- $('gaia-passwd').disabled = false;
- $('captcha-value').disabled = false;
- $('captcha-wrapper').style.backgroundImage = url(args.captchaUrl);
- },
- /**
- * Reset the state of all descendant elements of a root element to their
- * initial state.
- * The initial state is specified by adding a class to the descendant
- * element in sync_setup_overlay.html.
- * @param pageElementId The root page element id.
- * @private
- */
- resetPage_: function(pageElementId) {
- var page = $(pageElementId);
- var forEach = function(arr, fn) {
- var length = arr.length;
- for (var i = 0; i < length; i++) {
- fn(arr[i]);
- }
- };
- forEach(page.getElementsByClassName('reset-hidden'),
- function(elt) { elt.hidden = true; });
- forEach(page.getElementsByClassName('reset-shown'),
- function(elt) { elt.hidden = false; });
- forEach(page.getElementsByClassName('reset-disabled'),
- function(elt) { elt.disabled = true; });
- forEach(page.getElementsByClassName('reset-enabled'),
- function(elt) { elt.disabled = false; });
- forEach(page.getElementsByClassName('reset-value'),
- function(elt) { elt.value = ''; });
- forEach(page.getElementsByClassName('reset-opaque'),
- function(elt) { elt.classList.remove('transparent'); });
- },
- showGaiaLogin_: function(args) {
- this.resetPage_('sync-setup-login');
- $('sync-setup-login').hidden = false;
- var f = $('gaia-login-form');
- var email = $('gaia-email');
- var passwd = $('gaia-passwd');
- if (f) {
- if (args.user != undefined) {
- if (email.value != args.user)
- passwd.value = ""; // Reset the password field
- email.value = args.user;
- }
- if (!args.editable_user) {
- email.hidden = true;
- var span = $('email-readonly');
- span.textContent = email.value;
- span.hidden = false;
- $('create-account-div').hidden = true;
- }
- f.accessCode.disabled = true;
- }
- if (1 == args.error) {
- var access_code = document.getElementById('access-code');
- if (access_code.value && access_code.value != "") {
- $('errormsg-0-access-code').hidden = false;
- this.showAccessCodeRequired_();
- } else {
- $('errormsg-1-password').hidden = false;
- }
- this.setBlurbError_(args.error_message);
- } else if (3 == args.error) {
- $('errormsg-0-connection').hidden = false;
- this.setBlurbError_(args.error_message);
- } else if (4 == args.error) {
- this.showCaptcha_(args);
- } else if (7 == args.error) {
- this.setBlurbError_(localStrings.getString('serviceUnavailableError'));
- } else if (8 == args.error) {
- this.showAccessCodeRequired_();
- } else if (args.error_message) {
- this.setBlurbError_(args.error_message);
- }
- if (args.fatalError) {
- $('errormsg-fatal').hidden = false;
- $('sign-in').disabled = true;
- return;
- }
- $('sign-in').disabled = false;
- $('sign-in').value = templateData['signin'];
- this.loginSetFocus_();
- },
- resetErrorVisibility_: function() {
- $("errormsg-0-email").hidden = true;
- $("errormsg-0-password").hidden = true;
- $("errormsg-1-password").hidden = true;
- $("errormsg-0-connection").hidden = true;
- $("errormsg-0-access-code").hidden = true;
- },
- setBlurbError_: function(error_message) {
- if (this.captchaChallengeActive_)
- return; // No blurb in captcha challenge mode.
- if (error_message) {
- $('error-signing-in').hidden = true;
- $('error-custom').hidden = false;
- $('error-custom').textContent = error_message;
- } else {
- $('error-signing-in').hidden = false;
- $('error-custom').hidden = true;
- }
- $('top-blurb-error').hidden = false;
- $('gaia-email').disabled = false;
- $('gaia-passwd').disabled = false;
- },
- matchesASPRegex_: function(toMatch) {
- var noSpaces = /[a-z]{16}/;
- var withSpaces = /([a-z]{4}\s){3}[a-z]{4}/;
- if (toMatch.match(noSpaces) || toMatch.match(withSpaces))
- return true;
- return false;
- },
- setErrorVisibility_: function() {
- this.resetErrorVisibility_();
- var f = $('gaia-login-form');
- var email = $('gaia-email');
- var passwd = $('gaia-passwd');
- if (null == email.value || "" == email.value) {
- $('errormsg-0-email').hidden = false;
- this.setBlurbError_();
- return false;
- }
- // Don't enforce password being non-blank when checking access code (it
- // will have been cleared when the page was displayed).
- if (f.accessCode.disabled && (null == passwd.value ||
- "" == passwd.value)) {
- $('errormsg-0-password').hidden = false;
- this.setBlurbError_();
- return false;
- }
- if (!f.accessCode.disabled && (null == f.accessCode.value ||
- "" == f.accessCode.value)) {
- $('errormsg-0-password').hidden = false;
- return false;
- }
- if (f.accessCode.disabled && this.matchesASPRegex_(passwd.value) &&
- $('asp-warning-div').hidden) {
- $('asp-warning-div').hidden = false;
- $('gaia-passwd').value = "";
- return false;
- }
- return true;
- },
- sendCredentialsAndClose_: function() {
- if (!this.setErrorVisibility_()) {
- return false;
- }
- $('gaia-email').disabled = true;
- $('gaia-passwd').disabled = true;
- $('captcha-value').disabled = true;
- $('access-code').disabled = true;
- this.setThrobbersVisible_(true);
- var f = $('gaia-login-form');
- var email = $('gaia-email');
- var passwd = $('gaia-passwd');
- var result = JSON.stringify({"user" : email.value,
- "pass" : passwd.value,
- "captcha" : f.captchaValue.value,
- "access_code" : f.accessCode.value});
- $('sign-in').disabled = true;
- chrome.send('SyncSetupSubmitAuth', [result]);
- },
- showSuccessAndClose_: function() {
- $('sign-in').value = localStrings.getString('loginSuccess');
- setTimeout(this.closeOverlay_, 1600);
- },
- showSuccessAndSettingUp_: function() {
- $('sign-in').value = localStrings.getString('settingUp');
- $('top-blurb-error').hidden = true;
- },
- /**
- * Displays the stop syncing dialog.
- * @private
- */
- showStopSyncingUI_: function() {
- // Hide any visible children of the overlay.
- var overlay = $('sync-setup-overlay');
- for (var i = 0; i < overlay.children.length; i++)
- overlay.children[i].hidden = true;
- // Bypass OptionsPage.navigateToPage because it will call didShowPage
- // which will set its own visible page, based on the flow state.
- this.visible = true;
- $('sync-setup-stop-syncing').hidden = false;
- $('stop-syncing-cancel').focus();
- },
- /**
- * Steps into the appropriate Sync Setup error UI.
- * @private
- */
- showErrorUI_: function() {
- chrome.send('SyncSetupShowErrorUI');
- },
- /**
- * Determines the appropriate page to show in the Sync Setup UI based on
- * the state of the Sync backend.
- * @private
- */
- showSetupUI_: function() {
- chrome.send('SyncSetupShowSetupUI');
- },
- /**
- * Hides the outer elements of the login UI. This is used by the sync promo
- * to customize the look of the login box.
- */
- hideOuterLoginUI_: function() {
- $('sync-setup-overlay-title').hidden = true;
- $('sync-setup-cancel').hidden = true;
- }
- };
- // These get methods should only be called by the WebUI tests.
- SyncSetupOverlay.getLoginEmail = function() {
- return SyncSetupOverlay.getInstance().getLoginEmail_();
- };
- SyncSetupOverlay.getLoginPasswd = function() {
- return SyncSetupOverlay.getInstance().getLoginPasswd_();
- };
- SyncSetupOverlay.getSignInButton = function() {
- return SyncSetupOverlay.getInstance().getSignInButton_();
- };
- // These methods are for general consumption.
- SyncSetupOverlay.showErrorUI = function() {
- SyncSetupOverlay.getInstance().showErrorUI_();
- };
- SyncSetupOverlay.showSetupUI = function() {
- SyncSetupOverlay.getInstance().showSetupUI_();
- };
- SyncSetupOverlay.showSyncSetupPage = function(page, args) {
- SyncSetupOverlay.getInstance().showSyncSetupPage_(page, args);
- };
- SyncSetupOverlay.showSuccessAndClose = function() {
- SyncSetupOverlay.getInstance().showSuccessAndClose_();
- };
- SyncSetupOverlay.showSuccessAndSettingUp = function() {
- SyncSetupOverlay.getInstance().showSuccessAndSettingUp_();
- };
- SyncSetupOverlay.showStopSyncingUI = function() {
- SyncSetupOverlay.getInstance().showStopSyncingUI_();
- };
- // Export
- return {
- SyncSetupOverlay: SyncSetupOverlay
- };
- });
- // 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.
- var AddLanguageOverlay = options.AddLanguageOverlay;
- var AdvancedOptions = options.AdvancedOptions;
- var AlertOverlay = options.AlertOverlay;
- var AutofillEditAddressOverlay = options.AutofillEditAddressOverlay;
- var AutofillEditCreditCardOverlay = options.AutofillEditCreditCardOverlay;
- var AutofillOptions = options.AutofillOptions;
- var BrowserOptions = options.BrowserOptions;
- var ClearBrowserDataOverlay = options.ClearBrowserDataOverlay;
- var ContentSettings = options.ContentSettings;
- var ContentSettingsExceptionsArea =
- options.contentSettings.ContentSettingsExceptionsArea;
- var CookiesView = options.CookiesView;
- var ExtensionSettings = options.ExtensionSettings;
- var FontSettings = options.FontSettings;
- var HandlerOptions = options.HandlerOptions;
- var ImportDataOverlay = options.ImportDataOverlay;
- var IntentsView = options.IntentsView;
- var InstantConfirmOverlay = options.InstantConfirmOverlay;
- var LanguageOptions = options.LanguageOptions;
- var OptionsPage = options.OptionsPage;
- var PackExtensionOverlay = options.PackExtensionOverlay;
- var PasswordManager = options.PasswordManager;
- var PersonalOptions = options.PersonalOptions;
- var Preferences = options.Preferences;
- var ManageProfileOverlay = options.ManageProfileOverlay;
- var ProxyOptions = options.ProxyOptions;
- var SearchEngineManager = options.SearchEngineManager;
- var SearchPage = options.SearchPage;
- var SyncSetupOverlay = options.SyncSetupOverlay;
- var VirtualKeyboardManager = options.VirtualKeyboardManager;
- /**
- * DOMContentLoaded handler, sets up the page.
- */
- function load() {
- // Decorate the existing elements in the document.
- cr.ui.decorate('input[pref][type=checkbox]', options.PrefCheckbox);
- cr.ui.decorate('input[pref][type=number]', options.PrefNumber);
- cr.ui.decorate('input[pref][type=radio]', options.PrefRadio);
- cr.ui.decorate('input[pref][type=range]', options.PrefRange);
- cr.ui.decorate('select[pref]', options.PrefSelect);
- cr.ui.decorate('input[pref][type=text]', options.PrefTextField);
- cr.ui.decorate('input[pref][type=url]', options.PrefTextField);
- cr.ui.decorate('button[pref]', options.PrefButton);
- cr.ui.decorate('#content-settings-page input[type=radio]:not(.handler-radio)',
- options.ContentSettingsRadio);
- cr.ui.decorate('#content-settings-page input[type=radio].handler-radio',
- options.HandlersEnabledRadio);
- cr.ui.decorate('span.controlled-setting-indicator',
- options.ControlledSettingIndicator);
- var menuOffPattern = /(^\?|&)menu=off($|&)/;
- var menuDisabled = menuOffPattern.test(window.location.search);
- // document.documentElement.setAttribute('hide-menu', menuDisabled);
- // We can't use an attribute on the html element because of webkit bug
- // 12519. Instead, we add a class.
- if (menuDisabled)
- document.documentElement.classList.add('hide-menu');
- localStrings = new LocalStrings();
- OptionsPage.register(SearchPage.getInstance());
- OptionsPage.register(BrowserOptions.getInstance());
- OptionsPage.registerSubPage(SearchEngineManager.getInstance(),
- BrowserOptions.getInstance(),
- [$('defaultSearchManageEnginesButton')]);
- OptionsPage.register(PersonalOptions.getInstance());
- OptionsPage.registerSubPage(AutofillOptions.getInstance(),
- PersonalOptions.getInstance(),
- [$('autofill-settings')]);
- OptionsPage.registerSubPage(PasswordManager.getInstance(),
- PersonalOptions.getInstance(),
- [$('manage-passwords')]);
- if (cr.isChromeOS) {
- OptionsPage.register(SystemOptions.getInstance());
- OptionsPage.registerSubPage(AboutPage.getInstance(),
- SystemOptions.getInstance());
- OptionsPage.registerSubPage(LanguageOptions.getInstance(),
- SystemOptions.getInstance(),
- [$('language-button')]);
- OptionsPage.registerSubPage(
- new OptionsPage('languageChewing',
- templateData.languageChewingPageTabTitle,
- 'languageChewingPage'),
- LanguageOptions.getInstance());
- OptionsPage.registerSubPage(
- new OptionsPage('languageHangul',
- templateData.languageHangulPageTabTitle,
- 'languageHangulPage'),
- LanguageOptions.getInstance());
- OptionsPage.registerSubPage(
- new OptionsPage('languageMozc',
- templateData.languageMozcPageTabTitle,
- 'languageMozcPage'),
- LanguageOptions.getInstance());
- OptionsPage.registerSubPage(
- new OptionsPage('languagePinyin',
- templateData.languagePinyinPageTabTitle,
- 'languagePinyinPage'),
- LanguageOptions.getInstance());
- // Only use the VirtualKeyboardManager if the keyboard DOM elements (which
- // it will assume exists) are present (i.e. if we were built with
- // USE_VIRTUAL_KEYBOARD).
- if ($('language-options-virtual-keyboard')) {
- OptionsPage.registerSubPage(VirtualKeyboardManager.getInstance(),
- LanguageOptions.getInstance());
- }
- OptionsPage.register(InternetOptions.getInstance());
- }
- OptionsPage.register(AdvancedOptions.getInstance());
- OptionsPage.registerSubPage(ContentSettings.getInstance(),
- AdvancedOptions.getInstance(),
- [$('privacyContentSettingsButton')]);
- OptionsPage.registerSubPage(ContentSettingsExceptionsArea.getInstance(),
- ContentSettings.getInstance());
- OptionsPage.registerSubPage(CookiesView.getInstance(),
- ContentSettings.getInstance(),
- [$('privacyContentSettingsButton'),
- $('show-cookies-button')]);
- // If HandlerOptions is null it means it got compiled out.
- if (HandlerOptions) {
- OptionsPage.registerSubPage(HandlerOptions.getInstance(),
- ContentSettings.getInstance(),
- [$('manage-handlers-button')]);
- }
- if (IntentsView && $('manage-intents-button')) {
- OptionsPage.registerSubPage(IntentsView.getInstance(),
- ContentSettings.getInstance(),
- [$('manage-intents-button')]);
- }
- OptionsPage.registerSubPage(FontSettings.getInstance(),
- AdvancedOptions.getInstance(),
- [$('fontSettingsCustomizeFontsButton')]);
- if (!cr.isChromeOS) {
- OptionsPage.registerSubPage(LanguageOptions.getInstance(),
- AdvancedOptions.getInstance(),
- [$('language-button')]);
- }
- if (!cr.isWindows && !cr.isMac) {
- OptionsPage.registerSubPage(CertificateManager.getInstance(),
- AdvancedOptions.getInstance(),
- [$('certificatesManageButton')]);
- OptionsPage.registerOverlay(CertificateRestoreOverlay.getInstance(),
- CertificateManager.getInstance());
- OptionsPage.registerOverlay(CertificateBackupOverlay.getInstance(),
- CertificateManager.getInstance());
- OptionsPage.registerOverlay(CertificateEditCaTrustOverlay.getInstance(),
- CertificateManager.getInstance());
- OptionsPage.registerOverlay(CertificateImportErrorOverlay.getInstance(),
- CertificateManager.getInstance());
- }
- OptionsPage.registerOverlay(AddLanguageOverlay.getInstance(),
- LanguageOptions.getInstance());
- OptionsPage.registerOverlay(AlertOverlay.getInstance());
- OptionsPage.registerOverlay(AutofillEditAddressOverlay.getInstance(),
- AutofillOptions.getInstance());
- OptionsPage.registerOverlay(AutofillEditCreditCardOverlay.getInstance(),
- AutofillOptions.getInstance());
- OptionsPage.registerOverlay(ClearBrowserDataOverlay.getInstance(),
- AdvancedOptions.getInstance(),
- [$('privacyClearDataButton')]);
- OptionsPage.registerOverlay(ImportDataOverlay.getInstance(),
- PersonalOptions.getInstance());
- OptionsPage.registerOverlay(InstantConfirmOverlay.getInstance(),
- BrowserOptions.getInstance());
- OptionsPage.registerOverlay(SyncSetupOverlay.getInstance(),
- PersonalOptions.getInstance());
- OptionsPage.registerOverlay(ManageProfileOverlay.getInstance(),
- PersonalOptions.getInstance());
- OptionsPage.register(ExtensionSettings.getInstance());
- OptionsPage.registerOverlay(PackExtensionOverlay.getInstance(),
- ExtensionSettings.getInstance());
- if (cr.isChromeOS) {
- OptionsPage.register(AccountsOptions.getInstance());
- OptionsPage.registerSubPage(ProxyOptions.getInstance(),
- InternetOptions.getInstance());
- OptionsPage.registerSubPage(ChangePictureOptions.getInstance(),
- PersonalOptions.getInstance(),
- [$('change-picture-button')]);
- OptionsPage.registerOverlay(DetailsInternetPage.getInstance(),
- InternetOptions.getInstance());
- var languageModifierKeysOverlay = new OptionsPage(
- 'languageCustomizeModifierKeysOverlay',
- localStrings.getString('languageCustomizeModifierKeysOverlay'),
- 'languageCustomizeModifierKeysOverlay')
- $('languageCustomizeModifierKeysOverleyDismissButton').onclick =
- function() {
- OptionsPage.closeOverlay();
- };
- OptionsPage.registerOverlay(languageModifierKeysOverlay,
- SystemOptions.getInstance(),
- [$('modifier-keys-button')]);
- }
- Preferences.getInstance().initialize();
- OptionsPage.initialize();
- var path = document.location.pathname;
- if (path.length > 1) {
- // Skip starting slash and remove trailing slash (if any).
- var pageName = path.slice(1).replace(/\/$/, '');
- // Proxy page is now per network and only reachable from internet details.
- if (pageName != 'proxy') {
- // Show page, but don't update history (there's already an entry for it).
- OptionsPage.showPageByName(pageName, false);
- }
- } else {
- OptionsPage.showDefaultPage();
- }
- var subpagesNavTabs = document.querySelectorAll('.subpages-nav-tabs');
- for(var i = 0; i < subpagesNavTabs.length; i++) {
- subpagesNavTabs[i].onclick = function(event) {
- OptionsPage.showTab(event.srcElement);
- }
- }
- // Allow platform specific CSS rules.
- cr.enablePlatformSpecificCSSRules();
- if (navigator.plugins['Shockwave Flash'])
- document.documentElement.setAttribute('hasFlashPlugin', '');
- // Clicking on the Settings title brings up the 'Basics' page.
- $('navbar-content-title').onclick = function() {
- OptionsPage.navigateToPage(BrowserOptions.getInstance().name);
- };
- }
- document.addEventListener('DOMContentLoaded', load);
- window.onpopstate = function(e) {
- options.OptionsPage.setState(e.state);
- };
- window.onbeforeunload = function() {
- options.OptionsPage.willClose();
- };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement