| // 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 A UI for editing tweak settings / clicking tweak actions. |
| * |
| * @author agrieve@google.com (Andrew Grieve) |
| */ |
| |
| goog.provide('goog.tweak.EntriesPanel'); |
| goog.provide('goog.tweak.TweakUi'); |
| |
| goog.require('goog.array'); |
| goog.require('goog.asserts'); |
| goog.require('goog.dom'); |
| goog.require('goog.dom.safe'); |
| goog.require('goog.html.SafeHtml'); |
| goog.require('goog.object'); |
| goog.require('goog.string.Const'); |
| goog.require('goog.style'); |
| goog.require('goog.tweak'); |
| goog.require('goog.tweak.BaseEntry'); |
| goog.require('goog.tweak.BooleanGroup'); |
| goog.require('goog.tweak.BooleanInGroupSetting'); |
| goog.require('goog.tweak.BooleanSetting'); |
| goog.require('goog.tweak.ButtonAction'); |
| goog.require('goog.tweak.NumericSetting'); |
| goog.require('goog.tweak.StringSetting'); |
| goog.require('goog.ui.Zippy'); |
| goog.require('goog.userAgent'); |
| |
| |
| |
| /** |
| * A UI for editing tweak settings / clicking tweak actions. |
| * @param {!goog.tweak.Registry} registry The registry to render. |
| * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with. |
| * @constructor |
| * @final |
| */ |
| goog.tweak.TweakUi = function(registry, opt_domHelper) { |
| /** |
| * The registry to create a UI from. |
| * @type {!goog.tweak.Registry} |
| * @private |
| */ |
| this.registry_ = registry; |
| |
| /** |
| * The element to display when the UI is visible. |
| * @type {goog.tweak.EntriesPanel|undefined} |
| * @private |
| */ |
| this.entriesPanel_; |
| |
| /** |
| * The DomHelper to render with. |
| * @type {!goog.dom.DomHelper} |
| * @private |
| */ |
| this.domHelper_ = opt_domHelper || goog.dom.getDomHelper(); |
| |
| // Listen for newly registered entries (happens with lazy-loaded modules). |
| registry.addOnRegisterListener(goog.bind(this.onNewRegisteredEntry_, this)); |
| }; |
| |
| |
| /** |
| * The CSS class name unique to the root tweak panel div. |
| * @type {string} |
| * @private |
| */ |
| goog.tweak.TweakUi.ROOT_PANEL_CLASS_ = goog.getCssName('goog-tweak-root'); |
| |
| |
| /** |
| * The CSS class name unique to the tweak entry div. |
| * @type {string} |
| * @private |
| */ |
| goog.tweak.TweakUi.ENTRY_CSS_CLASS_ = goog.getCssName('goog-tweak-entry'); |
| |
| |
| /** |
| * The CSS classes for each tweak entry div. |
| * @type {string} |
| * @private |
| */ |
| goog.tweak.TweakUi.ENTRY_CSS_CLASSES_ = goog.tweak.TweakUi.ENTRY_CSS_CLASS_ + |
| ' ' + goog.getCssName('goog-inline-block'); |
| |
| |
| /** |
| * The CSS classes for each namespace tweak entry div. |
| * @type {string} |
| * @private |
| */ |
| goog.tweak.TweakUi.ENTRY_GROUP_CSS_CLASSES_ = |
| goog.tweak.TweakUi.ENTRY_CSS_CLASS_; |
| |
| |
| /** |
| * Marker that the style sheet has already been installed. |
| * @type {string} |
| * @private |
| */ |
| goog.tweak.TweakUi.STYLE_SHEET_INSTALLED_MARKER_ = |
| '__closure_tweak_installed_'; |
| |
| |
| /** |
| * CSS used by TweakUI. |
| * @type {string} |
| * @private |
| */ |
| goog.tweak.TweakUi.CSS_STYLES_ = (function() { |
| var MOBILE = goog.userAgent.MOBILE; |
| var IE = goog.userAgent.IE; |
| var ENTRY_CLASS = '.' + goog.tweak.TweakUi.ENTRY_CSS_CLASS_; |
| var ROOT_PANEL_CLASS = '.' + goog.tweak.TweakUi.ROOT_PANEL_CLASS_; |
| var GOOG_INLINE_BLOCK_CLASS = '.' + goog.getCssName('goog-inline-block'); |
| var ret = ROOT_PANEL_CLASS + '{background:#ffc; padding:0 4px}'; |
| // Make this work even if the user hasn't included common.css. |
| if (!IE) { |
| ret += GOOG_INLINE_BLOCK_CLASS + '{display:inline-block}'; |
| } |
| // Space things out vertically for touch UIs. |
| if (MOBILE) { |
| ret += ROOT_PANEL_CLASS + ',' + ROOT_PANEL_CLASS + ' fieldset{' + |
| 'line-height:2em;' + '}'; |
| } |
| return ret; |
| })(); |
| |
| |
| /** |
| * Creates a TweakUi if tweaks are enabled. |
| * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with. |
| * @return {!Element|undefined} The root UI element or undefined if tweaks are |
| * not enabled. |
| */ |
| goog.tweak.TweakUi.create = function(opt_domHelper) { |
| var registry = goog.tweak.getRegistry(); |
| if (registry) { |
| var ui = new goog.tweak.TweakUi(registry, opt_domHelper); |
| ui.render(); |
| return ui.getRootElement(); |
| } |
| }; |
| |
| |
| /** |
| * Creates a TweakUi inside of a show/hide link. |
| * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with. |
| * @return {!Element|undefined} The root UI element or undefined if tweaks are |
| * not enabled. |
| */ |
| goog.tweak.TweakUi.createCollapsible = function(opt_domHelper) { |
| var registry = goog.tweak.getRegistry(); |
| if (registry) { |
| var dh = opt_domHelper || goog.dom.getDomHelper(); |
| |
| // The following strings are for internal debugging only. No translation |
| // necessary. Do NOT wrap goog.getMsg() around these strings. |
| var showLink = dh.createDom('a', {href: 'javascript:;'}, 'Show Tweaks'); |
| var hideLink = dh.createDom('a', {href: 'javascript:;'}, 'Hide Tweaks'); |
| var ret = dh.createDom('div', null, showLink); |
| |
| var lazyCreate = function() { |
| // Lazily render the UI. |
| var ui = new goog.tweak.TweakUi( |
| /** @type {!goog.tweak.Registry} */ (registry), dh); |
| ui.render(); |
| // Put the hide link on the same line as the "Show Descriptions" link. |
| // Set the style lazily because we can. |
| hideLink.style.marginRight = '10px'; |
| var tweakElem = ui.getRootElement(); |
| tweakElem.insertBefore(hideLink, tweakElem.firstChild); |
| ret.appendChild(tweakElem); |
| return tweakElem; |
| }; |
| new goog.ui.Zippy(showLink, lazyCreate, false /* expanded */, hideLink); |
| return ret; |
| } |
| }; |
| |
| |
| /** |
| * Compares the given entries. Orders alphabetically and groups buttons and |
| * expandable groups. |
| * @param {goog.tweak.BaseEntry} a The first entry to compare. |
| * @param {goog.tweak.BaseEntry} b The second entry to compare. |
| * @return {number} Refer to goog.array.defaultCompare. |
| * @private |
| */ |
| goog.tweak.TweakUi.entryCompare_ = function(a, b) { |
| return ( |
| goog.array.defaultCompare(a instanceof goog.tweak.NamespaceEntry_, |
| b instanceof goog.tweak.NamespaceEntry_) || |
| goog.array.defaultCompare(a instanceof goog.tweak.BooleanGroup, |
| b instanceof goog.tweak.BooleanGroup) || |
| goog.array.defaultCompare(a instanceof goog.tweak.ButtonAction, |
| b instanceof goog.tweak.ButtonAction) || |
| goog.array.defaultCompare(a.label, b.label) || |
| goog.array.defaultCompare(a.getId(), b.getId())); |
| }; |
| |
| |
| /** |
| * @param {!goog.tweak.BaseEntry} entry The entry. |
| * @return {boolean} Returns whether the given entry contains sub-entries. |
| * @private |
| */ |
| goog.tweak.TweakUi.isGroupEntry_ = function(entry) { |
| return entry instanceof goog.tweak.NamespaceEntry_ || |
| entry instanceof goog.tweak.BooleanGroup; |
| }; |
| |
| |
| /** |
| * Returns the list of entries from the given boolean group. |
| * @param {!goog.tweak.BooleanGroup} group The group to get the entries from. |
| * @return {!Array<!goog.tweak.BaseEntry>} The sorted entries. |
| * @private |
| */ |
| goog.tweak.TweakUi.extractBooleanGroupEntries_ = function(group) { |
| var ret = goog.object.getValues(group.getChildEntries()); |
| ret.sort(goog.tweak.TweakUi.entryCompare_); |
| return ret; |
| }; |
| |
| |
| /** |
| * @param {!goog.tweak.BaseEntry} entry The entry. |
| * @return {string} Returns the namespace for the entry, or '' if it is not |
| * namespaced. |
| * @private |
| */ |
| goog.tweak.TweakUi.extractNamespace_ = function(entry) { |
| var namespaceMatch = /.+(?=\.)/.exec(entry.getId()); |
| return namespaceMatch ? namespaceMatch[0] : ''; |
| }; |
| |
| |
| /** |
| * @param {!goog.tweak.BaseEntry} entry The entry. |
| * @return {string} Returns the part of the label after the last period, unless |
| * the label has been explicly set (it is different from the ID). |
| * @private |
| */ |
| goog.tweak.TweakUi.getNamespacedLabel_ = function(entry) { |
| var label = entry.label; |
| if (label == entry.getId()) { |
| label = label.substr(label.lastIndexOf('.') + 1); |
| } |
| return label; |
| }; |
| |
| |
| /** |
| * @return {!Element} The root element. Must not be called before render(). |
| */ |
| goog.tweak.TweakUi.prototype.getRootElement = function() { |
| goog.asserts.assert(this.entriesPanel_, |
| 'TweakUi.getRootElement called before render().'); |
| return this.entriesPanel_.getRootElement(); |
| }; |
| |
| |
| /** |
| * Reloads the page with query parameters set by the UI. |
| * @private |
| */ |
| goog.tweak.TweakUi.prototype.restartWithAppliedTweaks_ = function() { |
| var queryString = this.registry_.makeUrlQuery(); |
| var wnd = this.domHelper_.getWindow(); |
| if (queryString != wnd.location.search) { |
| wnd.location.search = queryString; |
| } else { |
| wnd.location.reload(); |
| } |
| }; |
| |
| |
| /** |
| * Installs the required CSS styles. |
| * @private |
| */ |
| goog.tweak.TweakUi.prototype.installStyles_ = function() { |
| // Use an marker to install the styles only once per document. |
| // Styles are injected via JS instead of in a separate style sheet so that |
| // they are automatically excluded when tweaks are stripped out. |
| var doc = this.domHelper_.getDocument(); |
| if (!(goog.tweak.TweakUi.STYLE_SHEET_INSTALLED_MARKER_ in doc)) { |
| goog.style.installStyles( |
| goog.tweak.TweakUi.CSS_STYLES_, doc); |
| doc[goog.tweak.TweakUi.STYLE_SHEET_INSTALLED_MARKER_] = true; |
| } |
| }; |
| |
| |
| /** |
| * Creates the element to display when the UI is visible. |
| * @return {!Element} The root element. |
| */ |
| goog.tweak.TweakUi.prototype.render = function() { |
| this.installStyles_(); |
| var dh = this.domHelper_; |
| // The submit button |
| var submitButton = dh.createDom('button', {style: 'font-weight:bold'}, |
| 'Apply Tweaks'); |
| submitButton.onclick = goog.bind(this.restartWithAppliedTweaks_, this); |
| |
| var rootPanel = new goog.tweak.EntriesPanel([], dh); |
| var rootPanelDiv = rootPanel.render(submitButton); |
| rootPanelDiv.className += ' ' + goog.tweak.TweakUi.ROOT_PANEL_CLASS_; |
| this.entriesPanel_ = rootPanel; |
| |
| var entries = this.registry_.extractEntries(true /* excludeChildEntries */, |
| false /* excludeNonSettings */); |
| for (var i = 0, entry; entry = entries[i]; i++) { |
| this.insertEntry_(entry); |
| } |
| |
| return rootPanelDiv; |
| }; |
| |
| |
| /** |
| * Updates the UI with the given entry. |
| * @param {!goog.tweak.BaseEntry} entry The newly registered entry. |
| * @private |
| */ |
| goog.tweak.TweakUi.prototype.onNewRegisteredEntry_ = function(entry) { |
| if (this.entriesPanel_) { |
| this.insertEntry_(entry); |
| } |
| }; |
| |
| |
| /** |
| * Updates the UI with the given entry. |
| * @param {!goog.tweak.BaseEntry} entry The newly registered entry. |
| * @private |
| */ |
| goog.tweak.TweakUi.prototype.insertEntry_ = function(entry) { |
| var panel = this.entriesPanel_; |
| var namespace = goog.tweak.TweakUi.extractNamespace_(entry); |
| |
| if (namespace) { |
| // Find the NamespaceEntry that the entry belongs to. |
| var namespaceEntryId = goog.tweak.NamespaceEntry_.ID_PREFIX + namespace; |
| var nsPanel = panel.childPanels[namespaceEntryId]; |
| if (nsPanel) { |
| panel = nsPanel; |
| } else { |
| entry = new goog.tweak.NamespaceEntry_(namespace, [entry]); |
| } |
| } |
| if (entry instanceof goog.tweak.BooleanInGroupSetting) { |
| var group = entry.getGroup(); |
| // BooleanGroup entries are always registered before their |
| // BooleanInGroupSettings. |
| panel = panel.childPanels[group.getId()]; |
| } |
| goog.asserts.assert(panel, 'Missing panel for entry %s', entry.getId()); |
| panel.insertEntry(entry); |
| }; |
| |
| |
| |
| /** |
| * The body of the tweaks UI and also used for BooleanGroup. |
| * @param {!Array<!goog.tweak.BaseEntry>} entries The entries to show in the |
| * panel. |
| * @param {goog.dom.DomHelper=} opt_domHelper The DomHelper to render with. |
| * @constructor |
| * @final |
| */ |
| goog.tweak.EntriesPanel = function(entries, opt_domHelper) { |
| /** |
| * The entries to show in the panel. |
| * @type {!Array<!goog.tweak.BaseEntry>} entries |
| * @private |
| */ |
| this.entries_ = entries; |
| |
| var self = this; |
| /** |
| * The bound onclick handler for the help question marks. |
| * @this {Element} |
| * @private |
| */ |
| this.boundHelpOnClickHandler_ = function() { |
| self.onHelpClick_(this.parentNode); |
| }; |
| |
| /** |
| * The element that contains the UI. |
| * @type {Element} |
| * @private |
| */ |
| this.rootElem_; |
| |
| /** |
| * The element that contains all of the settings and the endElement. |
| * @type {Element} |
| * @private |
| */ |
| this.mainPanel_; |
| |
| /** |
| * Flips between true/false each time the "Toggle Descriptions" link is |
| * clicked. |
| * @type {boolean} |
| * @private |
| */ |
| this.showAllDescriptionsState_; |
| |
| /** |
| * The DomHelper to render with. |
| * @type {!goog.dom.DomHelper} |
| * @private |
| */ |
| this.domHelper_ = opt_domHelper || goog.dom.getDomHelper(); |
| |
| /** |
| * Map of tweak ID -> EntriesPanel for child panels (BooleanGroups). |
| * @type {!Object<!goog.tweak.EntriesPanel>} |
| */ |
| this.childPanels = {}; |
| }; |
| |
| |
| /** |
| * @return {!Element} Returns the expanded element. Must not be called before |
| * render(). |
| */ |
| goog.tweak.EntriesPanel.prototype.getRootElement = function() { |
| goog.asserts.assert(this.rootElem_, |
| 'EntriesPanel.getRootElement called before render().'); |
| return /** @type {!Element} */ (this.rootElem_); |
| }; |
| |
| |
| /** |
| * Creates and returns the expanded element. |
| * The markup looks like: |
| * <div> |
| * <a>Show Descriptions</a> |
| * <div> |
| * ... |
| * {endElement} |
| * </div> |
| * </div> |
| * @param {Element|DocumentFragment=} opt_endElement Element to insert after all |
| * tweak entries. |
| * @return {!Element} The root element for the panel. |
| */ |
| goog.tweak.EntriesPanel.prototype.render = function(opt_endElement) { |
| var dh = this.domHelper_; |
| var entries = this.entries_; |
| var ret = dh.createDom('div'); |
| |
| var showAllDescriptionsLink = dh.createDom('a', { |
| href: 'javascript:;', |
| onclick: goog.bind(this.toggleAllDescriptions, this) |
| }, 'Toggle all Descriptions'); |
| ret.appendChild(showAllDescriptionsLink); |
| |
| // Add all of the entries. |
| var mainPanel = dh.createElement('div'); |
| this.mainPanel_ = mainPanel; |
| for (var i = 0, entry; entry = entries[i]; i++) { |
| mainPanel.appendChild(this.createEntryElem_(entry)); |
| } |
| |
| if (opt_endElement) { |
| mainPanel.appendChild(opt_endElement); |
| } |
| ret.appendChild(mainPanel); |
| this.rootElem_ = ret; |
| return /** @type {!Element} */ (ret); |
| }; |
| |
| |
| /** |
| * Inserts the given entry into the panel. |
| * @param {!goog.tweak.BaseEntry} entry The entry to insert. |
| */ |
| goog.tweak.EntriesPanel.prototype.insertEntry = function(entry) { |
| var insertIndex = -goog.array.binarySearch(this.entries_, entry, |
| goog.tweak.TweakUi.entryCompare_) - 1; |
| goog.asserts.assert(insertIndex >= 0, 'insertEntry failed for %s', |
| entry.getId()); |
| goog.array.insertAt(this.entries_, entry, insertIndex); |
| this.mainPanel_.insertBefore( |
| this.createEntryElem_(entry), |
| // IE doesn't like 'undefined' here. |
| this.mainPanel_.childNodes[insertIndex] || null); |
| }; |
| |
| |
| /** |
| * Creates and returns a form element for the given entry. |
| * @param {!goog.tweak.BaseEntry} entry The entry. |
| * @return {!Element} The root DOM element for the entry. |
| * @private |
| */ |
| goog.tweak.EntriesPanel.prototype.createEntryElem_ = function(entry) { |
| var dh = this.domHelper_; |
| var isGroupEntry = goog.tweak.TweakUi.isGroupEntry_(entry); |
| var classes = isGroupEntry ? goog.tweak.TweakUi.ENTRY_GROUP_CSS_CLASSES_ : |
| goog.tweak.TweakUi.ENTRY_CSS_CLASSES_; |
| // Containers should not use label tags or else all descendent inputs will be |
| // connected on desktop browsers. |
| var containerNodeName = isGroupEntry ? 'span' : 'label'; |
| var ret = dh.createDom('div', classes, |
| dh.createDom(containerNodeName, { |
| // Make the hover text the description. |
| title: entry.description, |
| style: 'color:' + (entry.isRestartRequired() ? '' : 'blue') |
| }, this.createTweakEntryDom_(entry)), |
| // Add the expandable help question mark. |
| this.createHelpElem_(entry)); |
| return ret; |
| }; |
| |
| |
| /** |
| * Click handler for the help link. |
| * @param {Node} entryDiv The div that contains the tweak. |
| * @private |
| */ |
| goog.tweak.EntriesPanel.prototype.onHelpClick_ = function(entryDiv) { |
| this.showDescription_(entryDiv, !entryDiv.style.display); |
| }; |
| |
| |
| /** |
| * Twiddle the DOM so that the entry within the given span is shown/hidden. |
| * @param {Node} entryDiv The div that contains the tweak. |
| * @param {boolean} show True to show, false to hide. |
| * @private |
| */ |
| goog.tweak.EntriesPanel.prototype.showDescription_ = |
| function(entryDiv, show) { |
| var descriptionElem = entryDiv.lastChild.lastChild; |
| goog.style.setElementShown(/** @type {Element} */ (descriptionElem), show); |
| entryDiv.style.display = show ? 'block' : ''; |
| }; |
| |
| |
| /** |
| * Creates and returns a help element for the given entry. |
| * @param {goog.tweak.BaseEntry} entry The entry. |
| * @return {!Element} The root element of the created DOM. |
| * @private |
| */ |
| goog.tweak.EntriesPanel.prototype.createHelpElem_ = function(entry) { |
| // The markup looks like: |
| // <span onclick=...><b>?</b><span>{description}</span></span> |
| var ret = this.domHelper_.createElement('span'); |
| goog.dom.safe.setInnerHtml(ret, goog.html.SafeHtml.concat( |
| goog.html.SafeHtml.create('b', |
| {'style': goog.string.Const.from('padding:0 1em 0 .5em')}, '?'), |
| goog.html.SafeHtml.create('span', |
| {'style': goog.string.Const.from('display:none;color:#666')}))); |
| ret.onclick = this.boundHelpOnClickHandler_; |
| // IE<9 doesn't support lastElementChild. |
| var descriptionElem = /** @type {!Element} */ (ret.lastChild); |
| if (entry.isRestartRequired()) { |
| goog.dom.setTextContent(descriptionElem, entry.description); |
| } else { |
| goog.dom.safe.setInnerHtml(descriptionElem, goog.html.SafeHtml.concat( |
| goog.html.SafeHtml.htmlEscape(entry.description), |
| goog.html.SafeHtml.create('span', |
| {'style': goog.string.Const.from('color: blue')}, |
| '(no restart required)'))); |
| } |
| return ret; |
| }; |
| |
| |
| /** |
| * Show all entry descriptions (has the same effect as clicking on all ?'s). |
| */ |
| goog.tweak.EntriesPanel.prototype.toggleAllDescriptions = function() { |
| var show = !this.showAllDescriptionsState_; |
| this.showAllDescriptionsState_ = show; |
| var entryDivs = this.domHelper_.getElementsByTagNameAndClass('div', |
| goog.tweak.TweakUi.ENTRY_CSS_CLASS_, this.rootElem_); |
| for (var i = 0, div; div = entryDivs[i]; i++) { |
| this.showDescription_(div, show); |
| } |
| }; |
| |
| |
| /** |
| * Creates the DOM element to control the given enum setting. |
| * @param {!goog.tweak.StringSetting|!goog.tweak.NumericSetting} tweak The |
| * setting. |
| * @param {string} label The label for the entry. |
| * @param {!Function} onchangeFunc onchange event handler. |
| * @return {!DocumentFragment} The DOM element. |
| * @private |
| */ |
| goog.tweak.EntriesPanel.prototype.createComboBoxDom_ = |
| function(tweak, label, onchangeFunc) { |
| // The markup looks like: |
| // Label: <select><option></option></select> |
| var dh = this.domHelper_; |
| var ret = dh.getDocument().createDocumentFragment(); |
| ret.appendChild(dh.createTextNode(label + ': ')); |
| var selectElem = dh.createElement('select'); |
| var values = tweak.getValidValues(); |
| for (var i = 0, il = values.length; i < il; ++i) { |
| var optionElem = dh.createElement('option'); |
| optionElem.text = String(values[i]); |
| // Setting the option tag's value is required for selectElem.value to work |
| // properly. |
| optionElem.value = String(values[i]); |
| selectElem.appendChild(optionElem); |
| } |
| ret.appendChild(selectElem); |
| |
| // Set the value and add a callback. |
| selectElem.value = tweak.getNewValue(); |
| selectElem.onchange = onchangeFunc; |
| tweak.addCallback(function() { |
| selectElem.value = tweak.getNewValue(); |
| }); |
| return ret; |
| }; |
| |
| |
| /** |
| * Creates the DOM element to control the given boolean setting. |
| * @param {!goog.tweak.BooleanSetting} tweak The setting. |
| * @param {string} label The label for the entry. |
| * @return {!DocumentFragment} The DOM elements. |
| * @private |
| */ |
| goog.tweak.EntriesPanel.prototype.createBooleanSettingDom_ = |
| function(tweak, label) { |
| var dh = this.domHelper_; |
| var ret = dh.getDocument().createDocumentFragment(); |
| var checkbox = dh.createDom('input', {type: 'checkbox'}); |
| ret.appendChild(checkbox); |
| ret.appendChild(dh.createTextNode(label)); |
| |
| // Needed on IE6 to ensure the textbox doesn't get cleared |
| // when added to the DOM. |
| checkbox.defaultChecked = tweak.getNewValue(); |
| |
| checkbox.checked = tweak.getNewValue(); |
| checkbox.onchange = function() { |
| tweak.setValue(checkbox.checked); |
| }; |
| tweak.addCallback(function() { |
| checkbox.checked = tweak.getNewValue(); |
| }); |
| return ret; |
| }; |
| |
| |
| /** |
| * Creates the DOM for a BooleanGroup or NamespaceEntry. |
| * @param {!goog.tweak.BooleanGroup|!goog.tweak.NamespaceEntry_} entry The |
| * entry. |
| * @param {string} label The label for the entry. |
| * @param {!Array<goog.tweak.BaseEntry>} childEntries The child entries. |
| * @return {!DocumentFragment} The DOM element. |
| * @private |
| */ |
| goog.tweak.EntriesPanel.prototype.createSubPanelDom_ = function(entry, label, |
| childEntries) { |
| var dh = this.domHelper_; |
| var toggleLink = dh.createDom('a', {href: 'javascript:;'}, |
| label + ' \xBB'); |
| var toggleLink2 = dh.createDom('a', {href: 'javascript:;'}, |
| '\xAB ' + label); |
| toggleLink2.style.marginRight = '10px'; |
| |
| var innerUi = new goog.tweak.EntriesPanel(childEntries, dh); |
| this.childPanels[entry.getId()] = innerUi; |
| |
| var elem = innerUi.render(); |
| // Move the toggle descriptions link into the legend. |
| var descriptionsLink = elem.firstChild; |
| var childrenElem = dh.createDom('fieldset', |
| goog.getCssName('goog-inline-block'), |
| dh.createDom('legend', null, toggleLink2, descriptionsLink), |
| elem); |
| |
| new goog.ui.Zippy(toggleLink, childrenElem, false /* expanded */, |
| toggleLink2); |
| |
| var ret = dh.getDocument().createDocumentFragment(); |
| ret.appendChild(toggleLink); |
| ret.appendChild(childrenElem); |
| return ret; |
| }; |
| |
| |
| /** |
| * Creates the DOM element to control the given string setting. |
| * @param {!goog.tweak.StringSetting|!goog.tweak.NumericSetting} tweak The |
| * setting. |
| * @param {string} label The label for the entry. |
| * @param {!Function} onchangeFunc onchange event handler. |
| * @return {!DocumentFragment} The DOM element. |
| * @private |
| */ |
| goog.tweak.EntriesPanel.prototype.createTextBoxDom_ = |
| function(tweak, label, onchangeFunc) { |
| var dh = this.domHelper_; |
| var ret = dh.getDocument().createDocumentFragment(); |
| ret.appendChild(dh.createTextNode(label + ': ')); |
| var textBox = dh.createDom('input', { |
| value: tweak.getNewValue(), |
| // TODO(agrieve): Make size configurable or autogrow. |
| size: 5, |
| onblur: onchangeFunc |
| }); |
| ret.appendChild(textBox); |
| tweak.addCallback(function() { |
| textBox.value = tweak.getNewValue(); |
| }); |
| return ret; |
| }; |
| |
| |
| /** |
| * Creates the DOM element to control the given button action. |
| * @param {!goog.tweak.ButtonAction} tweak The action. |
| * @param {string} label The label for the entry. |
| * @return {!Element} The DOM element. |
| * @private |
| */ |
| goog.tweak.EntriesPanel.prototype.createButtonActionDom_ = |
| function(tweak, label) { |
| return this.domHelper_.createDom('button', { |
| onclick: goog.bind(tweak.fireCallbacks, tweak) |
| }, label); |
| }; |
| |
| |
| /** |
| * Creates the DOM element to control the given entry. |
| * @param {!goog.tweak.BaseEntry} entry The entry. |
| * @return {!Element|!DocumentFragment} The DOM element. |
| * @private |
| */ |
| goog.tweak.EntriesPanel.prototype.createTweakEntryDom_ = function(entry) { |
| var label = goog.tweak.TweakUi.getNamespacedLabel_(entry); |
| if (entry instanceof goog.tweak.BooleanSetting) { |
| return this.createBooleanSettingDom_(entry, label); |
| } else if (entry instanceof goog.tweak.BooleanGroup) { |
| var childEntries = goog.tweak.TweakUi.extractBooleanGroupEntries_(entry); |
| return this.createSubPanelDom_(entry, label, childEntries); |
| } else if (entry instanceof goog.tweak.StringSetting) { |
| /** @this {Element} */ |
| var setValueFunc = function() { |
| entry.setValue(this.value); |
| }; |
| return entry.getValidValues() ? |
| this.createComboBoxDom_(entry, label, setValueFunc) : |
| this.createTextBoxDom_(entry, label, setValueFunc); |
| } else if (entry instanceof goog.tweak.NumericSetting) { |
| setValueFunc = function() { |
| // Reset the value if it's not a number. |
| if (isNaN(this.value)) { |
| this.value = entry.getNewValue(); |
| } else { |
| entry.setValue(+this.value); |
| } |
| }; |
| return entry.getValidValues() ? |
| this.createComboBoxDom_(entry, label, setValueFunc) : |
| this.createTextBoxDom_(entry, label, setValueFunc); |
| } else if (entry instanceof goog.tweak.NamespaceEntry_) { |
| return this.createSubPanelDom_(entry, entry.label, entry.entries); |
| } |
| goog.asserts.assertInstanceof(entry, goog.tweak.ButtonAction, |
| 'invalid entry: %s', entry); |
| return this.createButtonActionDom_( |
| /** @type {!goog.tweak.ButtonAction} */ (entry), label); |
| }; |
| |
| |
| |
| /** |
| * Entries used to represent the collapsible namespace links. These entries are |
| * never registered with the TweakRegistry, but are contained within the |
| * collection of entries within TweakPanels. |
| * @param {string} namespace The namespace for the entry. |
| * @param {!Array<!goog.tweak.BaseEntry>} entries Entries within the namespace. |
| * @constructor |
| * @extends {goog.tweak.BaseEntry} |
| * @private |
| */ |
| goog.tweak.NamespaceEntry_ = function(namespace, entries) { |
| goog.tweak.BaseEntry.call(this, |
| goog.tweak.NamespaceEntry_.ID_PREFIX + namespace, |
| 'Tweaks within the ' + namespace + ' namespace.'); |
| |
| /** |
| * Entries within this namespace. |
| * @type {!Array<!goog.tweak.BaseEntry>} |
| */ |
| this.entries = entries; |
| |
| this.label = namespace; |
| }; |
| goog.inherits(goog.tweak.NamespaceEntry_, goog.tweak.BaseEntry); |
| |
| |
| /** |
| * Prefix for the IDs of namespace entries used to ensure that they do not |
| * conflict with regular entries. |
| * @type {string} |
| */ |
| goog.tweak.NamespaceEntry_.ID_PREFIX = '!'; |
| |