Javascript  |  237行  |  9.07 KB

// 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 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__: cr.ui.BubbleButton.prototype,

    /**
     * Decorates the base element to show the proper icon.
     */
    decorate: function() {
      cr.ui.BubbleButton.prototype.decorate.call(this);
      this.classList.add('controlled-setting-indicator');

      // If there is a pref, track its controlledBy and recommendedValue
      // properties in order to be able to bring up the correct bubble.
      if (this.pref) {
        Preferences.getInstance().addEventListener(
            this.pref, this.handlePrefChange.bind(this));
        this.resetHandler = this.clearAssociatedPref_;
      }
    },

    /**
     * The given handler will be called when the user clicks on the 'reset to
     * recommended value' link shown in the indicator bubble. The |this| object
     * will be the indicator itself.
     * @param {function()} handler The handler to be called.
     */
    set resetHandler(handler) {
      this.resetHandler_ = handler;
    },

    /**
     * Clears the preference associated with this indicator.
     * @private
     */
    clearAssociatedPref_: function() {
      Preferences.clearPref(this.pref, !this.dialogPref);
    },

    /* Handle changes to the associated pref by hiding any currently visible
     * bubble and updating the controlledBy property.
     * @param {Event} event Pref change event.
     */
    handlePrefChange: function(event) {
      OptionsPage.hideBubble();
      if (event.value.controlledBy) {
        if (!this.value || String(event.value.value) == this.value) {
          this.controlledBy = event.value.controlledBy;
          if (event.value.extension) {
            if (this.pref == 'session.restore_on_startup' ||
                this.pref == 'homepage_is_newtabpage') {
              // Special case for the restore on startup, which is implied
              // by the startup pages settings being controlled by an
              // extension, and same for the home page as NTP, so we don't want
              // to show two buttons in these cases.
              // TODO(mad): Find a better way to handle this.
              this.controlledBy = null;
            } else {
              this.extensionId = event.value.extension.id;
              this.extensionIcon = event.value.extension.icon;
              this.extensionName = event.value.extension.name;
            }
          }
        } else {
          this.controlledBy = null;
        }
      } else if (event.value.recommendedValue != undefined) {
        this.controlledBy =
            !this.value || String(event.value.recommendedValue) == this.value ?
            'hasRecommendation' : null;
      } else {
        this.controlledBy = null;
      }
    },

    /**
     * Open or close a bubble with further information about the pref.
     * @private
     */
    toggleBubble_: function() {
      if (this.showingBubble) {
        OptionsPage.hideBubble();
      } else {
        var self = this;

        // Construct the bubble text.
        if (this.hasAttribute('plural')) {
          var defaultStrings = {
            'policy': loadTimeData.getString('controlledSettingsPolicy'),
            'extension': loadTimeData.getString('controlledSettingsExtension'),
            'extensionWithName': loadTimeData.getString(
                'controlledSettingsExtensionWithName'),
          };
        } else {
          var defaultStrings = {
            'policy': loadTimeData.getString('controlledSettingPolicy'),
            'extension': loadTimeData.getString('controlledSettingExtension'),
            'extensionWithName': loadTimeData.getString(
                'controlledSettingExtensionWithName'),
            'recommended':
                loadTimeData.getString('controlledSettingRecommended'),
            'hasRecommendation':
                loadTimeData.getString('controlledSettingHasRecommendation'),
          };
          if (cr.isChromeOS) {
            defaultStrings.owner =
                loadTimeData.getString('controlledSettingOwner');
          }
        }

        // No controller, no bubble.
        if (!this.controlledBy || !(this.controlledBy in defaultStrings))
          return;

        var text = defaultStrings[this.controlledBy];
        if (this.controlledBy == 'extension' && this.extensionName)
          text = defaultStrings.extensionWithName;

        // Apply text overrides.
        if (this.hasAttribute('text' + this.controlledBy))
          text = this.getAttribute('text' + this.controlledBy);

        // Create the DOM tree.
        var content = document.createElement('div');
        content.className = 'controlled-setting-bubble-content';
        content.setAttribute('controlled-by', this.controlledBy);
        content.textContent = text;

        if (this.controlledBy == 'hasRecommendation' && this.resetHandler_ &&
            !this.readOnly) {
          var container = document.createElement('div');
          var action = document.createElement('button');
          action.classList.add('link-button');
          action.classList.add('controlled-setting-bubble-action');
          action.textContent =
              loadTimeData.getString('controlledSettingFollowRecommendation');
          action.addEventListener('click', function(event) {
            self.resetHandler_();
          });
          container.appendChild(action);
          content.appendChild(container);
        } else if (this.controlledBy == 'extension' && this.extensionName) {
          var extensionContainer =
              $('extension-controlled-settings-bubble-template').
                  cloneNode(true);
          // No need for an id anymore, and thus remove to avoid id collision.
          extensionContainer.removeAttribute('id');
          extensionContainer.hidden = false;

          var extensionName = extensionContainer.querySelector(
              '.controlled-setting-bubble-extension-name');
          extensionName.textContent = this.extensionName;
          extensionName.style.backgroundImage =
              'url("' + this.extensionIcon + '")';

          var manageLink = extensionContainer.querySelector(
              '.controlled-setting-bubble-extension-manage-link');
          manageLink.onclick = function() {
            uber.invokeMethodOnWindow(
                window.top, 'showPage', {pageId: 'extensions'});
          };

          var disableButton = extensionContainer.querySelector('button');
          var extensionId = this.extensionId;
          disableButton.onclick = function() {
            chrome.send('disableExtension', [extensionId]);
          };
          content.appendChild(extensionContainer);
        }

        OptionsPage.showBubble(content, this.image, this, this.location);
      }
    },
  };

  /**
   * The name of the associated preference.
   * @type {string}
   */
  cr.defineProperty(ControlledSettingIndicator, 'pref', cr.PropertyKind.ATTR);

  /**
   * Whether this indicator is part of a dialog. If so, changes made to the
   * associated preference take effect in the settings UI immediately but are
   * only actually committed when the user confirms the dialog. If the user
   * cancels the dialog instead, the changes are rolled back in the settings UI
   * and never committed.
   * @type {boolean}
   */
  cr.defineProperty(ControlledSettingIndicator, 'dialogPref',
                    cr.PropertyKind.BOOL_ATTR);

  /**
   * The value of the associated preference that the indicator represents. If
   * this is not set, the indicator will be visible whenever any value is
   * enforced or recommended. If it is set, the indicator will be visible only
   * when the enforced or recommended value matches the value it represents.
   * This allows multiple indicators to be created for a set of radio buttons,
   * ensuring that only one of them is visible at a time.
   */
  cr.defineProperty(ControlledSettingIndicator, 'value',
                    cr.PropertyKind.ATTR);

  /**
   * The status of the associated preference:
   * - 'policy':            A specific value is enfoced by policy.
   * - 'extension':         A specific value is enforced by an extension.
   * - 'recommended':       A value is recommended by policy. The user could
   *                        override this recommendation but has not done so.
   * - 'hasRecommendation': A value is recommended by policy. The user has
   *                        overridden this recommendation.
   * - unset:               The value is controlled by the user alone.
   * @type {string}
   */
  cr.defineProperty(ControlledSettingIndicator, 'controlledBy',
                    cr.PropertyKind.ATTR);

  // Export.
  return {
    ControlledSettingIndicator: ControlledSettingIndicator
  };
});