123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- // Copyright 2009 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview Definition for goog.tweak.Registry.
- * Most clients should not use this class directly, but instead use the API
- * defined in tweak.js. One possible use case for directly using TweakRegistry
- * is to register tweaks that are not known at compile time.
- *
- * @author agrieve@google.com (Andrew Grieve)
- */
- goog.provide('goog.tweak.Registry');
- goog.require('goog.array');
- goog.require('goog.asserts');
- goog.require('goog.log');
- goog.require('goog.string');
- goog.require('goog.tweak.BasePrimitiveSetting');
- goog.require('goog.tweak.BaseSetting');
- goog.require('goog.tweak.BooleanSetting');
- goog.require('goog.tweak.NumericSetting');
- goog.require('goog.tweak.StringSetting');
- goog.require('goog.uri.utils');
- /**
- * Singleton that manages all tweaks. This should be instantiated only from
- * goog.tweak.getRegistry().
- * @param {string} queryParams Value of window.location.search.
- * @param {!Object<string|number|boolean>} compilerOverrides Default value
- * overrides set by the compiler.
- * @constructor
- * @final
- */
- goog.tweak.Registry = function(queryParams, compilerOverrides) {
- /**
- * A map of entry id -> entry object
- * @type {!Object<!goog.tweak.BaseEntry>}
- * @private
- */
- this.entryMap_ = {};
- /**
- * The map of query params to use when initializing entry settings.
- * @type {!Object<string>}
- * @private
- */
- this.parsedQueryParams_ = goog.tweak.Registry.parseQueryParams(queryParams);
- /**
- * List of callbacks to call when a new entry is registered.
- * @type {!Array<!Function>}
- * @private
- */
- this.onRegisterListeners_ = [];
- /**
- * A map of entry ID -> default value override for overrides set by the
- * compiler.
- * @type {!Object<string|number|boolean>}
- * @private
- */
- this.compilerDefaultValueOverrides_ = compilerOverrides;
- /**
- * A map of entry ID -> default value override for overrides set by
- * goog.tweak.overrideDefaultValue().
- * @type {!Object<string|number|boolean>}
- * @private
- */
- this.defaultValueOverrides_ = {};
- };
- /**
- * The logger for this class.
- * @type {goog.log.Logger}
- * @private
- */
- goog.tweak.Registry.prototype.logger_ =
- goog.log.getLogger('goog.tweak.Registry');
- /**
- * Simple parser for query params. Makes all keys lower-case.
- * @param {string} queryParams The part of the url between the ? and the #.
- * @return {!Object<string>} map of key->value.
- */
- goog.tweak.Registry.parseQueryParams = function(queryParams) {
- // Strip off the leading ? and split on &.
- var parts = queryParams.substr(1).split('&');
- var ret = {};
- for (var i = 0, il = parts.length; i < il; ++i) {
- var entry = parts[i].split('=');
- if (entry[0]) {
- ret[goog.string.urlDecode(entry[0]).toLowerCase()] =
- goog.string.urlDecode(entry[1] || '');
- }
- }
- return ret;
- };
- /**
- * Registers the given tweak setting/action.
- * @param {goog.tweak.BaseEntry} entry The entry.
- */
- goog.tweak.Registry.prototype.register = function(entry) {
- var id = entry.getId();
- var oldBaseEntry = this.entryMap_[id];
- if (oldBaseEntry) {
- if (oldBaseEntry == entry) {
- goog.log.warning(this.logger_, 'Tweak entry registered twice: ' + id);
- return;
- }
- goog.asserts.fail(
- 'Tweak entry registered twice and with different types: ' + id);
- }
- // Check for a default value override, either from compiler flags or from a
- // call to overrideDefaultValue().
- var defaultValueOverride = (id in this.compilerDefaultValueOverrides_) ?
- this.compilerDefaultValueOverrides_[id] :
- this.defaultValueOverrides_[id];
- if (goog.isDef(defaultValueOverride)) {
- goog.asserts.assertInstanceof(
- entry, goog.tweak.BasePrimitiveSetting,
- 'Cannot set the default value of non-primitive setting %s',
- entry.label);
- entry.setDefaultValue(defaultValueOverride);
- }
- // Set its value from the query params.
- if (entry instanceof goog.tweak.BaseSetting) {
- if (entry.getParamName()) {
- entry.setInitialQueryParamValue(
- this.parsedQueryParams_[entry.getParamName()]);
- }
- }
- this.entryMap_[id] = entry;
- // Call all listeners.
- for (var i = 0, callback; callback = this.onRegisterListeners_[i]; ++i) {
- callback(entry);
- }
- };
- /**
- * Adds a callback to be called whenever a new tweak is added.
- * @param {!Function} func The callback.
- */
- goog.tweak.Registry.prototype.addOnRegisterListener = function(func) {
- this.onRegisterListeners_.push(func);
- };
- /**
- * @param {string} id The unique string that identifies this entry.
- * @return {boolean} Whether a tweak with the given ID is registered.
- */
- goog.tweak.Registry.prototype.hasEntry = function(id) {
- return id in this.entryMap_;
- };
- /**
- * Returns the BaseEntry with the given ID. Asserts if it does not exists.
- * @param {string} id The unique string that identifies this entry.
- * @return {!goog.tweak.BaseEntry} The entry.
- */
- goog.tweak.Registry.prototype.getEntry = function(id) {
- var ret = this.entryMap_[id];
- goog.asserts.assert(ret, 'Tweak not registered: %s', id);
- return ret;
- };
- /**
- * Returns the boolean setting with the given ID. Asserts if the ID does not
- * refer to a registered entry or if it refers to one of the wrong type.
- * @param {string} id The unique string that identifies this entry.
- * @return {!goog.tweak.BooleanSetting} The entry.
- */
- goog.tweak.Registry.prototype.getBooleanSetting = function(id) {
- var entry = this.getEntry(id);
- goog.asserts.assertInstanceof(
- entry, goog.tweak.BooleanSetting,
- 'getBooleanSetting called on wrong type of BaseSetting');
- return /** @type {!goog.tweak.BooleanSetting} */ (entry);
- };
- /**
- * Returns the string setting with the given ID. Asserts if the ID does not
- * refer to a registered entry or if it refers to one of the wrong type.
- * @param {string} id The unique string that identifies this entry.
- * @return {!goog.tweak.StringSetting} The entry.
- */
- goog.tweak.Registry.prototype.getStringSetting = function(id) {
- var entry = this.getEntry(id);
- goog.asserts.assertInstanceof(
- entry, goog.tweak.StringSetting,
- 'getStringSetting called on wrong type of BaseSetting');
- return /** @type {!goog.tweak.StringSetting} */ (entry);
- };
- /**
- * Returns the numeric setting with the given ID. Asserts if the ID does not
- * refer to a registered entry or if it refers to one of the wrong type.
- * @param {string} id The unique string that identifies this entry.
- * @return {!goog.tweak.NumericSetting} The entry.
- */
- goog.tweak.Registry.prototype.getNumericSetting = function(id) {
- var entry = this.getEntry(id);
- goog.asserts.assertInstanceof(
- entry, goog.tweak.NumericSetting,
- 'getNumericSetting called on wrong type of BaseSetting');
- return /** @type {!goog.tweak.NumericSetting} */ (entry);
- };
- /**
- * Creates and returns an array of all BaseSetting objects with an associted
- * query parameter.
- * @param {boolean} excludeChildEntries Exclude BooleanInGroupSettings.
- * @param {boolean} excludeNonSettings Exclude entries that are not subclasses
- * of BaseSetting.
- * @return {!Array<!goog.tweak.BaseSetting>} The settings.
- */
- goog.tweak.Registry.prototype.extractEntries = function(
- excludeChildEntries, excludeNonSettings) {
- var entries = [];
- for (var id in this.entryMap_) {
- var entry = this.entryMap_[id];
- if (entry instanceof goog.tweak.BaseSetting) {
- if (excludeChildEntries && !entry.getParamName()) {
- continue;
- }
- } else if (excludeNonSettings) {
- continue;
- }
- entries.push(entry);
- }
- return entries;
- };
- /**
- * Returns the query part of the URL that will apply all set tweaks.
- * @param {string=} opt_existingSearchStr The part of the url between the ? and
- * the #. Uses window.location.search if not given.
- * @return {string} The query string.
- */
- goog.tweak.Registry.prototype.makeUrlQuery = function(opt_existingSearchStr) {
- var existingParams = opt_existingSearchStr == undefined ?
- window.location.search :
- opt_existingSearchStr;
- var sortedEntries = this.extractEntries(
- true /* excludeChildEntries */, true /* excludeNonSettings */);
- // Sort the params so that the urlQuery has stable ordering.
- sortedEntries.sort(function(a, b) {
- return goog.array.defaultCompare(a.getParamName(), b.getParamName());
- });
- // Add all values that are not set to their defaults.
- var keysAndValues = [];
- for (var i = 0, entry; entry = sortedEntries[i]; ++i) {
- var encodedValue = entry.getNewValueEncoded();
- if (encodedValue != null) {
- keysAndValues.push(entry.getParamName(), encodedValue);
- }
- // Strip all tweak query params from the existing query string. This will
- // make the final query string contain only the tweak settings that are set
- // to their non-default values and also maintain non-tweak related query
- // parameters.
- existingParams = goog.uri.utils.removeParam(
- existingParams,
- encodeURIComponent(/** @type {string} */ (entry.getParamName())));
- }
- var tweakParams = goog.uri.utils.buildQueryData(keysAndValues);
- // Decode spaces and commas in order to make the URL more readable.
- tweakParams = tweakParams.replace(/%2C/g, ',').replace(/%20/g, '+');
- return !tweakParams ? existingParams : existingParams ?
- existingParams + '&' + tweakParams :
- '?' + tweakParams;
- };
- /**
- * Sets a default value to use for the given tweak instead of the one passed
- * to the register* function. This function must be called before the tweak is
- * registered.
- * @param {string} id The unique string that identifies the entry.
- * @param {string|number|boolean} value The replacement value to be used as the
- * default value for the setting.
- */
- goog.tweak.Registry.prototype.overrideDefaultValue = function(id, value) {
- goog.asserts.assert(
- !this.hasEntry(id),
- 'goog.tweak.overrideDefaultValue must be called before the tweak is ' +
- 'registered. Tweak: %s',
- id);
- this.defaultValueOverrides_[id] = value;
- };
|