2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @param {function()} onHide
34 * @extends {WebInspector.HelpScreen}
36 WebInspector
.SettingsScreen = function(onHide
)
38 WebInspector
.HelpScreen
.call(this);
39 this.element
.id
= "settings-screen";
41 /** @type {function()} */
42 this._onHide
= onHide
;
44 this._contentElement
= this.element
.createChild("div", "help-window-main");
45 var settingsLabelElement
= createElementWithClass("div", "help-window-label");
46 settingsLabelElement
.createTextChild(WebInspector
.UIString("Settings"));
47 this._contentElement
.appendChild(this.createCloseButton());
49 this._tabbedPane
= new WebInspector
.TabbedPane();
50 this._tabbedPane
.insertBeforeTabStrip(settingsLabelElement
);
51 this._tabbedPane
.setShrinkableTabs(false);
52 this._tabbedPane
.setVerticalTabLayout(true);
53 this._tabbedPane
.appendTab("general", WebInspector
.UIString("General"), new WebInspector
.GenericSettingsTab());
54 this._tabbedPane
.appendTab("workspace", WebInspector
.UIString("Workspace"), new WebInspector
.WorkspaceSettingsTab());
55 if (Runtime
.experiments
.supportEnabled())
56 this._tabbedPane
.appendTab("experiments", WebInspector
.UIString("Experiments"), new WebInspector
.ExperimentsSettingsTab());
57 this._tabbedPaneController
= new WebInspector
.ExtensibleTabbedPaneController(this._tabbedPane
, "settings-view");
58 this._tabbedPane
.appendTab("shortcuts", WebInspector
.UIString("Shortcuts"), WebInspector
.shortcutsScreen
.createShortcutsTabView());
60 this.element
.addEventListener("keydown", this._keyDown
.bind(this), false);
61 this._developerModeCounter
= 0;
64 WebInspector
.SettingsScreen
.prototype = {
70 this._tabbedPane
.selectTab("general");
71 this._tabbedPane
.show(this._contentElement
);
72 WebInspector
.HelpScreen
.prototype.wasShown
.call(this);
76 * @param {string} name
78 selectTab: function(name
)
80 this._tabbedPane
.selectTab(name
);
87 isClosingKey: function(keyCode
)
90 WebInspector
.KeyboardShortcut
.Keys
.Enter
.code
,
91 WebInspector
.KeyboardShortcut
.Keys
.Esc
.code
,
92 ].indexOf(keyCode
) >= 0;
101 WebInspector
.HelpScreen
.prototype.willHide
.call(this);
105 * @param {!Event} event
107 _keyDown: function(event
)
109 var shiftKeyCode
= 16;
110 if (event
.keyCode
=== shiftKeyCode
&& ++this._developerModeCounter
> 5)
111 this.element
.classList
.add("settings-developer-mode");
114 __proto__
: WebInspector
.HelpScreen
.prototype
119 * @extends {WebInspector.VBox}
120 * @param {string} name
121 * @param {string=} id
123 WebInspector
.SettingsTab = function(name
, id
)
125 WebInspector
.VBox
.call(this);
126 this.element
.classList
.add("settings-tab-container");
128 this.element
.id
= id
;
129 var header
= this.element
.createChild("header");
130 header
.createChild("h3").createTextChild(name
);
131 this.containerElement
= this.element
.createChild("div", "help-container-wrapper").createChild("div", "settings-tab help-content help-container");
134 WebInspector
.SettingsTab
.prototype = {
136 * @param {string=} name
139 _appendSection: function(name
)
141 var block
= this.containerElement
.createChild("div", "help-block");
143 block
.createChild("div", "help-section-title").textContent
= name
;
147 _createSelectSetting: function(name
, options
, setting
)
149 var p
= createElement("p");
150 p
.createChild("label").textContent
= name
;
152 var select
= p
.createChild("select", "chrome-select");
153 var settingValue
= setting
.get();
155 for (var i
= 0; i
< options
.length
; ++i
) {
156 var option
= options
[i
];
157 select
.add(new Option(option
[0], option
[1]));
158 if (settingValue
=== option
[1])
159 select
.selectedIndex
= i
;
162 function changeListener(e
)
164 // Don't use e.target.value to avoid conversion of the value to string.
165 setting
.set(options
[select
.selectedIndex
][1]);
168 select
.addEventListener("change", changeListener
, false);
172 __proto__
: WebInspector
.VBox
.prototype
177 * @extends {WebInspector.SettingsTab}
179 WebInspector
.GenericSettingsTab = function()
181 WebInspector
.SettingsTab
.call(this, WebInspector
.UIString("General"), "general-tab-content");
184 var explicitSectionOrder
= ["", "Appearance", "Elements", "Sources", "Network", "Profiler", "Console", "Extensions"];
185 /** @type {!Map<string, !Element>} */
186 this._nameToSection
= new Map();
187 /** @type {!Map<string, !Element>} */
188 this._nameToSettingElement
= new Map();
189 for (var sectionName
of explicitSectionOrder
)
190 this._sectionElement(sectionName
);
191 self
.runtime
.extensions("setting").forEach(this._addSetting
.bind(this));
192 self
.runtime
.extensions(WebInspector
.SettingUI
).forEach(this._addSettingUI
.bind(this));
194 this._appendSection().appendChild(createTextButton(WebInspector
.UIString("Restore defaults and reload"), restoreAndReload
));
196 function restoreAndReload()
198 WebInspector
.settings
.clearAll();
199 WebInspector
.reload();
204 * @param {!Runtime.Extension} extension
207 WebInspector
.GenericSettingsTab
.isSettingVisible = function(extension
)
209 var descriptor
= extension
.descriptor();
210 if (!("title" in descriptor
))
212 if (!(("category" in descriptor
) || ("parentSettingName" in descriptor
)))
217 WebInspector
.GenericSettingsTab
.prototype = {
219 * @param {!Runtime.Extension} extension
221 _addSetting: function(extension
)
223 if (!WebInspector
.GenericSettingsTab
.isSettingVisible(extension
))
225 var descriptor
= extension
.descriptor();
226 var sectionName
= descriptor
["category"];
227 var settingName
= descriptor
["settingName"];
228 var setting
= WebInspector
.moduleSetting(settingName
);
229 var uiTitle
= WebInspector
.UIString(extension
.title(WebInspector
.platform()));
231 var sectionElement
= this._sectionElement(sectionName
);
232 var parentSettingName
= descriptor
["parentSettingName"];
233 var parentSettingElement
= parentSettingName
? this._nameToSettingElement
.get(descriptor
["parentSettingName"]) : null;
234 var parentFieldset
= null;
235 if (parentSettingElement
) {
236 parentFieldset
= parentSettingElement
.__fieldset
;
237 if (!parentFieldset
) {
238 parentFieldset
= WebInspector
.SettingsUI
.createSettingFieldset(WebInspector
.moduleSetting(parentSettingName
));
239 parentSettingElement
.appendChild(parentFieldset
);
240 parentSettingElement
.__fieldset
= parentFieldset
;
246 switch (descriptor
["settingType"]) {
248 settingControl
= WebInspector
.SettingsUI
.createSettingCheckbox(uiTitle
, setting
);
251 var descriptorOptions
= descriptor
["options"];
252 var options
= new Array(descriptorOptions
.length
);
253 for (var i
= 0; i
< options
.length
; ++i
) {
254 // The third array item flags that the option name is "raw" (non-i18n-izable).
255 var optionName
= descriptorOptions
[i
][2] ? descriptorOptions
[i
][0] : WebInspector
.UIString(descriptorOptions
[i
][0]);
256 options
[i
] = [optionName
, descriptorOptions
[i
][1]];
258 settingControl
= this._createSelectSetting(uiTitle
, options
, setting
);
261 console
.error("Invalid setting type: " + descriptor
["settingType"]);
264 this._nameToSettingElement
.set(settingName
, settingControl
);
265 (parentFieldset
|| sectionElement
).appendChild(/** @type {!Element} */ (settingControl
));
269 * @param {!Runtime.Extension} extension
271 _addSettingUI: function(extension
)
273 var descriptor
= extension
.descriptor();
274 var sectionName
= descriptor
["category"] || "";
275 extension
.instancePromise().then(appendCustomSetting
.bind(this));
278 * @param {!Object} object
279 * @this {WebInspector.GenericSettingsTab}
281 function appendCustomSetting(object
)
283 var settingUI
= /** @type {!WebInspector.SettingUI} */ (object
);
284 var element
= settingUI
.settingElement();
286 this._sectionElement(sectionName
).appendChild(element
);
291 * @param {string} sectionName
294 _sectionElement: function(sectionName
)
296 var sectionElement
= this._nameToSection
.get(sectionName
);
297 if (!sectionElement
) {
298 var uiSectionName
= sectionName
&& WebInspector
.UIString(sectionName
);
299 sectionElement
= this._appendSection(uiSectionName
);
300 this._nameToSection
.set(sectionName
, sectionElement
);
302 return sectionElement
;
305 __proto__
: WebInspector
.SettingsTab
.prototype
310 * @implements {WebInspector.SettingUI}
312 WebInspector
.SettingsScreen
.SkipStackFramePatternSettingUI = function()
316 WebInspector
.SettingsScreen
.SkipStackFramePatternSettingUI
.prototype = {
321 settingElement: function()
323 return createTextButton(WebInspector
.manageBlackboxingButtonLabel(), this._onManageButtonClick
.bind(this), "", WebInspector
.UIString("Skip stepping through sources with particular names"));
326 _onManageButtonClick: function()
328 WebInspector
.FrameworkBlackboxDialog
.show(WebInspector
.inspectorView
.element
);
334 * @extends {WebInspector.SettingsTab}
336 WebInspector
.WorkspaceSettingsTab = function()
338 WebInspector
.SettingsTab
.call(this, WebInspector
.UIString("Workspace"), "workspace-tab-content");
339 WebInspector
.isolatedFileSystemManager
.addEventListener(WebInspector
.IsolatedFileSystemManager
.Events
.FileSystemAdded
, this._fileSystemAdded
, this);
340 WebInspector
.isolatedFileSystemManager
.addEventListener(WebInspector
.IsolatedFileSystemManager
.Events
.FileSystemRemoved
, this._fileSystemRemoved
, this);
342 this._commonSection
= this._appendSection(WebInspector
.UIString("Common"));
343 var folderExcludeSetting
= WebInspector
.isolatedFileSystemManager
.excludedFolderManager().workspaceFolderExcludePatternSetting();
344 var folderExcludePatternInput
= WebInspector
.SettingsUI
.createSettingInputField(WebInspector
.UIString("Folder exclude pattern"), folderExcludeSetting
, false, 0, "270px", WebInspector
.SettingsUI
.regexValidator
);
345 this._commonSection
.appendChild(folderExcludePatternInput
);
347 this._fileSystemsSection
= this._appendSection(WebInspector
.UIString("Folders"));
348 this._fileSystemsListContainer
= this._fileSystemsSection
.createChild("p", "settings-list-container");
350 this._addFileSystemRowElement
= this._fileSystemsSection
.createChild("div");
351 this._addFileSystemRowElement
.appendChild(createTextButton(WebInspector
.UIString("Add folder\u2026"), this._addFileSystemClicked
.bind(this)));
353 this._editFileSystemButton
= createTextButton(WebInspector
.UIString("Folder options\u2026"), this._editFileSystemClicked
.bind(this));
354 this._addFileSystemRowElement
.appendChild(this._editFileSystemButton
);
355 this._updateEditFileSystemButtonState();
360 WebInspector
.WorkspaceSettingsTab
.prototype = {
363 WebInspector
.SettingsTab
.prototype.wasShown
.call(this);
369 this._resetFileSystems();
372 _resetFileSystems: function()
374 this._fileSystemsListContainer
.removeChildren();
375 var fileSystemPaths
= WebInspector
.isolatedFileSystemManager
.mapping().fileSystemPaths();
376 delete this._fileSystemsList
;
378 if (!fileSystemPaths
.length
) {
379 var noFileSystemsMessageElement
= this._fileSystemsListContainer
.createChild("div", "no-file-systems-message");
380 noFileSystemsMessageElement
.textContent
= WebInspector
.UIString("You have no file systems added.");
384 this._fileSystemsList
= new WebInspector
.SettingsList([{ id
: "path" }], this._renderFileSystem
.bind(this));
385 this._fileSystemsList
.element
.classList
.add("file-systems-list");
386 this._fileSystemsList
.addEventListener(WebInspector
.SettingsList
.Events
.Selected
, this._fileSystemSelected
.bind(this));
387 this._fileSystemsList
.addEventListener(WebInspector
.SettingsList
.Events
.Removed
, this._fileSystemRemovedfromList
.bind(this));
388 this._fileSystemsList
.addEventListener(WebInspector
.SettingsList
.Events
.DoubleClicked
, this._fileSystemDoubleClicked
.bind(this));
389 this._fileSystemsListContainer
.appendChild(this._fileSystemsList
.element
);
390 for (var i
= 0; i
< fileSystemPaths
.length
; ++i
)
391 this._fileSystemsList
.addItem(fileSystemPaths
[i
]);
392 this._updateEditFileSystemButtonState();
395 _updateEditFileSystemButtonState: function()
397 this._editFileSystemButton
.disabled
= !this._selectedFileSystemPath();
401 * @param {!WebInspector.Event} event
403 _fileSystemSelected: function(event
)
405 this._updateEditFileSystemButtonState();
409 * @param {!WebInspector.Event} event
411 _fileSystemDoubleClicked: function(event
)
413 var id
= /** @type{?string} */ (event
.data
);
414 this._editFileSystem(id
);
417 _editFileSystemClicked: function()
419 this._editFileSystem(this._selectedFileSystemPath());
423 * @param {?string} id
425 _editFileSystem: function(id
)
427 WebInspector
.EditFileSystemDialog
.show(WebInspector
.inspectorView
.element
, id
);
431 * @param {!Element} columnElement
432 * @param {{id: string, placeholder: (string|undefined), options: (!Array.<string>|undefined)}} column
433 * @param {?string} id
435 _renderFileSystem: function(columnElement
, column
, id
)
439 var fileSystemPath
= id
;
440 var textElement
= columnElement
.createChild("span", "list-column-text");
441 var pathElement
= textElement
.createChild("span", "file-system-path");
442 pathElement
.title
= fileSystemPath
;
444 const maxTotalPathLength
= 55;
445 const maxFolderNameLength
= 30;
447 var lastIndexOfSlash
= fileSystemPath
.lastIndexOf(WebInspector
.isWin() ? "\\" : "/");
448 var folderName
= fileSystemPath
.substr(lastIndexOfSlash
+ 1);
449 var folderPath
= fileSystemPath
.substr(0, lastIndexOfSlash
+ 1);
450 folderPath
= folderPath
.trimMiddle(maxTotalPathLength
- Math
.min(maxFolderNameLength
, folderName
.length
));
451 folderName
= folderName
.trimMiddle(maxFolderNameLength
);
453 var folderPathElement
= pathElement
.createChild("span");
454 folderPathElement
.textContent
= folderPath
;
456 var nameElement
= pathElement
.createChild("span", "file-system-path-name");
457 nameElement
.textContent
= folderName
;
461 * @param {!WebInspector.Event} event
463 _fileSystemRemovedfromList: function(event
)
465 var id
= /** @type{?string} */ (event
.data
);
468 WebInspector
.isolatedFileSystemManager
.removeFileSystem(id
);
471 _addFileSystemClicked: function()
473 WebInspector
.isolatedFileSystemManager
.addFileSystem();
476 _fileSystemAdded: function(event
)
478 var fileSystem
= /** @type {!WebInspector.IsolatedFileSystem} */ (event
.data
);
479 if (!this._fileSystemsList
)
482 this._fileSystemsList
.addItem(fileSystem
.path());
485 _fileSystemRemoved: function(event
)
487 var fileSystem
= /** @type {!WebInspector.IsolatedFileSystem} */ (event
.data
);
488 if (this._fileSystemsList
.itemForId(fileSystem
.path()))
489 this._fileSystemsList
.removeItem(fileSystem
.path());
490 if (!this._fileSystemsList
.itemIds().length
)
492 this._updateEditFileSystemButtonState();
495 _selectedFileSystemPath: function()
497 return this._fileSystemsList
? this._fileSystemsList
.selectedId() : null;
500 __proto__
: WebInspector
.SettingsTab
.prototype
506 * @extends {WebInspector.SettingsTab}
508 WebInspector
.ExperimentsSettingsTab = function()
510 WebInspector
.SettingsTab
.call(this, WebInspector
.UIString("Experiments"), "experiments-tab-content");
512 var experiments
= Runtime
.experiments
.allConfigurableExperiments();
513 if (experiments
.length
) {
514 var experimentsSection
= this._appendSection();
515 experimentsSection
.appendChild(this._createExperimentsWarningSubsection());
516 for (var i
= 0; i
< experiments
.length
; ++i
)
517 experimentsSection
.appendChild(this._createExperimentCheckbox(experiments
[i
]));
521 WebInspector
.ExperimentsSettingsTab
.prototype = {
523 * @return {!Element} element
525 _createExperimentsWarningSubsection: function()
527 var subsection
= createElement("div");
528 var warning
= subsection
.createChild("span", "settings-experiments-warning-subsection-warning");
529 warning
.textContent
= WebInspector
.UIString("WARNING:");
530 subsection
.createTextChild(" ");
531 var message
= subsection
.createChild("span", "settings-experiments-warning-subsection-message");
532 message
.textContent
= WebInspector
.UIString("These experiments could be dangerous and may require restart.");
536 _createExperimentCheckbox: function(experiment
)
538 var label
= createCheckboxLabel(WebInspector
.UIString(experiment
.title
), experiment
.isEnabled());
539 var input
= label
.checkboxElement
;
540 input
.name
= experiment
.name
;
543 experiment
.setEnabled(input
.checked
);
545 input
.addEventListener("click", listener
, false);
547 var p
= createElement("p");
548 p
.className
= experiment
.hidden
&& !experiment
.isEnabled() ? "settings-experiment-hidden" : "";
549 p
.appendChild(label
);
553 __proto__
: WebInspector
.SettingsTab
.prototype
559 WebInspector
.SettingsController = function()
561 /** @type {?WebInspector.SettingsScreen} */
562 this._settingsScreen
;
563 this._resizeBound
= this._resize
.bind(this);
566 WebInspector
.SettingsController
.prototype = {
567 _onHideSettingsScreen: function()
569 var window
= this._settingsScreen
.element
.ownerDocument
.defaultView
;
570 window
.removeEventListener("resize", this._resizeBound
, false);
571 delete this._settingsScreenVisible
;
575 * @param {string=} name
577 showSettingsScreen: function(name
)
579 if (!this._settingsScreen
)
580 this._settingsScreen
= new WebInspector
.SettingsScreen(this._onHideSettingsScreen
.bind(this));
581 this._settingsScreen
.showModal();
583 this._settingsScreen
.selectTab(name
);
584 this._settingsScreenVisible
= true;
585 var window
= this._settingsScreen
.element
.ownerDocument
.defaultView
;
586 window
.addEventListener("resize", this._resizeBound
, false);
591 if (this._settingsScreen
&& this._settingsScreen
.isShowing())
592 this._settingsScreen
.doResize();
598 * @implements {WebInspector.ActionDelegate}
600 WebInspector
.SettingsController
.ActionDelegate = function() { }
602 WebInspector
.SettingsController
.ActionDelegate
.prototype = {
605 * @param {!WebInspector.Context} context
606 * @param {string} actionId
608 handleAction: function(context
, actionId
)
610 if (actionId
=== "settings.show")
611 WebInspector
._settingsController
.showSettingsScreen();
612 else if (actionId
=== "settings.help")
613 InspectorFrontendHost
.openInNewTab("https://developers.google.com/web/tools/chrome-devtools/");
614 else if (actionId
=== "settings.shortcuts")
615 WebInspector
._settingsController
.showSettingsScreen("shortcuts");
621 * @implements {WebInspector.Revealer}
623 WebInspector
.SettingsController
.Revealer = function() { }
625 WebInspector
.SettingsController
.Revealer
.prototype = {
628 * @param {!Object} object
629 * @param {number=} lineNumber
632 reveal: function(object
, lineNumber
)
634 console
.assert(object
instanceof WebInspector
.Setting
);
635 var setting
= /** @type {!WebInspector.Setting} */ (object
);
638 self
.runtime
.extensions("setting").forEach(revealModuleSetting
);
639 self
.runtime
.extensions(WebInspector
.SettingUI
).forEach(revealSettingUI
);
640 self
.runtime
.extensions("settings-view").forEach(revealSettingsView
);
642 return success
? Promise
.resolve() : Promise
.reject();
645 * @param {!Runtime.Extension} extension
647 function revealModuleSetting(extension
)
649 if (!WebInspector
.GenericSettingsTab
.isSettingVisible(extension
))
651 if (extension
.descriptor()["settingName"] === setting
.name
) {
652 WebInspector
._settingsController
.showSettingsScreen("general");
658 * @param {!Runtime.Extension} extension
660 function revealSettingUI(extension
)
662 var settings
= extension
.descriptor()["settings"];
663 if (settings
&& settings
.indexOf(setting
.name
) !== -1) {
664 WebInspector
._settingsController
.showSettingsScreen("general");
670 * @param {!Runtime.Extension} extension
672 function revealSettingsView(extension
)
674 var settings
= extension
.descriptor()["settings"];
675 if (settings
&& settings
.indexOf(setting
.name
) !== -1) {
676 WebInspector
._settingsController
.showSettingsScreen(extension
.descriptor()["name"]);
685 * @extends {WebInspector.Object}
686 * @param {!Array.<{id: string, placeholder: (string|undefined), options: (!Array.<string>|undefined)}>} columns
687 * @param {function(!Element, {id: string, placeholder: (string|undefined), options: (!Array.<string>|undefined)}, ?string)} itemRenderer
689 WebInspector
.SettingsList = function(columns
, itemRenderer
)
691 this.element
= createElementWithClass("div", "settings-list");
692 this.element
.tabIndex
= -1;
693 this._itemRenderer
= itemRenderer
;
694 /** @type {!Map.<string, !Element>} */
695 this._listItems
= new Map();
696 /** @type {!Array.<?string>} */
698 this._columns
= columns
;
701 WebInspector
.SettingsList
.Events
= {
702 Selected
: "Selected",
704 DoubleClicked
: "DoubleClicked",
707 WebInspector
.SettingsList
.prototype = {
709 * @param {?string} itemId
710 * @param {?string=} beforeId
713 addItem: function(itemId
, beforeId
)
715 var listItem
= createElementWithClass("div", "settings-list-item");
716 listItem
._id
= itemId
;
717 if (typeof beforeId
!== "undefined")
718 this.element
.insertBefore(listItem
, this.itemForId(beforeId
));
720 this.element
.appendChild(listItem
);
722 var listItemContents
= listItem
.createChild("div", "settings-list-item-contents");
723 var listItemColumnsElement
= listItemContents
.createChild("div", "settings-list-item-columns");
725 listItem
.columnElements
= {};
726 for (var i
= 0; i
< this._columns
.length
; ++i
) {
727 var column
= this._columns
[i
];
728 var columnElement
= listItemColumnsElement
.createChild("div", "list-column settings-list-column-" + column
.id
);
729 listItem
.columnElements
[column
.id
] = columnElement
;
730 this._itemRenderer(columnElement
, column
, itemId
);
732 var removeItemButton
= this._createRemoveButton(removeItemClicked
.bind(this));
733 listItemContents
.addEventListener("click", this.selectItem
.bind(this, itemId
), false);
734 listItemContents
.addEventListener("dblclick", this._onDoubleClick
.bind(this, itemId
), false);
735 listItemContents
.appendChild(removeItemButton
);
737 this._listItems
.set(itemId
|| "", listItem
);
738 if (typeof beforeId
!== "undefined")
739 this._ids
.splice(this._ids
.indexOf(beforeId
), 0, itemId
);
741 this._ids
.push(itemId
);
744 * @param {!Event} event
745 * @this {WebInspector.SettingsList}
747 function removeItemClicked(event
)
749 removeItemButton
.disabled
= true;
750 this.removeItem(itemId
);
751 this.dispatchEventToListeners(WebInspector
.SettingsList
.Events
.Removed
, itemId
);
759 * @param {?string} id
761 removeItem: function(id
)
763 var listItem
= this._listItems
.remove(id
|| "");
766 this._ids
.remove(id
);
767 if (id
=== this._selectedId
) {
768 delete this._selectedId
;
769 if (this._ids
.length
)
770 this.selectItem(this._ids
[0]);
775 * @return {!Array.<?string>}
779 return this._ids
.slice();
783 * @return {!Array.<string>}
787 return this._columns
.select("id");
793 selectedId: function()
795 return this._selectedId
;
801 selectedItem: function()
803 return this._selectedId
? this.itemForId(this._selectedId
) : null;
807 * @param {?string} itemId
810 itemForId: function(itemId
)
812 return this._listItems
.get(itemId
|| "") || null;
816 * @param {?string} id
817 * @param {!Event=} event
819 _onDoubleClick: function(id
, event
)
821 this.dispatchEventToListeners(WebInspector
.SettingsList
.Events
.DoubleClicked
, id
);
825 * @param {?string} id
826 * @param {!Event=} event
828 selectItem: function(id
, event
)
830 if (typeof this._selectedId
!== "undefined")
831 this.itemForId(this._selectedId
).classList
.remove("selected");
833 this._selectedId
= id
;
834 if (typeof this._selectedId
!== "undefined")
835 this.itemForId(this._selectedId
).classList
.add("selected");
837 this.dispatchEventToListeners(WebInspector
.SettingsList
.Events
.Selected
, id
);
843 * @param {function(!Event)} handler
846 _createRemoveButton: function(handler
)
848 var removeButton
= createElementWithClass("div", "remove-item-button");
849 removeButton
.addEventListener("click", handler
, false);
853 __proto__
: WebInspector
.Object
.prototype
858 * @extends {WebInspector.SettingsList}
859 * @param {!Array.<{id: string, placeholder: (string|undefined), options: (!Array.<string>|undefined)}>} columns
860 * @param {function(string, string):string} valuesProvider
861 * @param {function(?string, !Object):!Array.<string>} validateHandler
862 * @param {function(?string, !Object)} editHandler
864 WebInspector
.EditableSettingsList = function(columns
, valuesProvider
, validateHandler
, editHandler
)
866 WebInspector
.SettingsList
.call(this, columns
, this._renderColumn
.bind(this));
867 this._valuesProvider
= valuesProvider
;
868 this._validateHandler
= validateHandler
;
869 this._editHandler
= editHandler
;
870 /** @type {!Map.<string, (!HTMLInputElement|!HTMLSelectElement)>} */
871 this._addInputElements
= new Map();
872 /** @type {!Map.<string, !Map.<string, (!HTMLInputElement|!HTMLSelectElement)>>} */
873 this._editInputElements
= new Map();
874 /** @type {!Map.<string, !Map.<string, !HTMLSpanElement>>} */
875 this._textElements
= new Map();
877 this._addMappingItem
= this.addItem(null);
878 this._addMappingItem
.classList
.add("item-editing", "add-list-item");
881 WebInspector
.EditableSettingsList
.prototype = {
884 * @param {?string} itemId
885 * @param {?string=} beforeId
888 addItem: function(itemId
, beforeId
)
890 var listItem
= WebInspector
.SettingsList
.prototype.addItem
.call(this, itemId
, beforeId
);
891 listItem
.classList
.add("editable");
896 * @param {?string} itemId
898 refreshItem: function(itemId
)
902 var listItem
= this.itemForId(itemId
);
905 for (var i
= 0; i
< this._columns
.length
; ++i
) {
906 var column
= this._columns
[i
];
907 var columnId
= column
.id
;
909 var value
= this._valuesProvider(itemId
, columnId
);
910 this._setTextElementContent(itemId
, columnId
, value
);
912 var editElement
= this._editInputElements
.get(itemId
).get(columnId
);
913 this._setEditElementValue(editElement
, value
|| "");
918 * @param {?string} itemId
919 * @param {string} columnId
921 _textElementContent: function(itemId
, columnId
)
925 return this._textElements
.get(itemId
).get(columnId
).textContent
.replace(/\u200B/g, "");
929 * @param {string} itemId
930 * @param {string} columnId
931 * @param {string} text
933 _setTextElementContent: function(itemId
, columnId
, text
)
935 var textElement
= this._textElements
.get(itemId
).get(columnId
);
936 textElement
.textContent
= text
.replace(/.{4}/g, "$&\u200B");
937 textElement
.title
= text
;
941 * @param {!Element} columnElement
942 * @param {{id: string, placeholder: (string|undefined), options: (!Array.<string>|undefined)}} column
943 * @param {?string} itemId
945 _renderColumn: function(columnElement
, column
, itemId
)
947 var columnId
= column
.id
;
948 if (itemId
=== null) {
949 this._createEditElement(columnElement
, column
, itemId
);
952 var validItemId
= itemId
;
954 if (!this._editInputElements
.has(itemId
))
955 this._editInputElements
.set(itemId
, new Map());
956 if (!this._textElements
.has(itemId
))
957 this._textElements
.set(itemId
, new Map());
959 var value
= this._valuesProvider(itemId
, columnId
);
961 var textElement
= /** @type {!HTMLSpanElement} */ (columnElement
.createChild("span", "list-column-text"));
962 columnElement
.addEventListener("click", rowClicked
.bind(this), false);
963 this._textElements
.get(itemId
).set(columnId
, textElement
);
964 this._setTextElementContent(itemId
, columnId
, value
);
966 this._createEditElement(columnElement
, column
, itemId
, value
);
969 * @param {!Event} event
970 * @this {WebInspector.EditableSettingsList}
972 function rowClicked(event
)
974 if (itemId
=== this._editingId
)
976 console
.assert(!this._editingId
);
977 this._editingId
= validItemId
;
978 var listItem
= this.itemForId(validItemId
);
979 listItem
.classList
.add("item-editing");
980 var editElement
= event
.target
.editElement
|| this._editInputElements
.get(validItemId
).get(this.columns()[0]);
982 if (editElement
.select
)
983 editElement
.select();
988 * @param {!Element} columnElement
989 * @param {{id: string, placeholder: (string|undefined), options: (!Array.<string>|undefined)}} column
990 * @param {?string} itemId
991 * @param {string=} value
994 _createEditElement: function(columnElement
, column
, itemId
, value
)
996 var options
= column
.options
;
998 var editElement
= /** @type {!HTMLSelectElement} */ (columnElement
.createChild("select", "chrome-select list-column-editor"));
999 for (var i
= 0; i
< options
.length
; ++i
) {
1000 var option
= editElement
.createChild("option");
1001 option
.value
= options
[i
];
1002 option
.textContent
= options
[i
];
1004 editElement
.addEventListener("blur", this._editMappingBlur
.bind(this, itemId
), false);
1005 editElement
.addEventListener("change", this._editMappingBlur
.bind(this, itemId
), false);
1007 var editElement
= /** @type {!HTMLInputElement} */ (columnElement
.createChild("input", "list-column-editor"));
1008 editElement
.addEventListener("blur", this._editMappingBlur
.bind(this, itemId
), false);
1009 editElement
.addEventListener("input", this._validateEdit
.bind(this, itemId
), false);
1010 if (itemId
=== null)
1011 editElement
.placeholder
= column
.placeholder
|| "";
1014 if (itemId
=== null)
1015 this._addInputElements
.set(column
.id
, editElement
);
1017 this._editInputElements
.get(itemId
).set(column
.id
, editElement
);
1019 this._setEditElementValue(editElement
, value
|| "");
1020 columnElement
.editElement
= editElement
;
1025 * @param {!HTMLInputElement|!HTMLSelectElement|undefined} editElement
1026 * @param {string} value
1028 _setEditElementValue: function(editElement
, value
)
1032 if (editElement
instanceof HTMLSelectElement
) {
1033 var options
= editElement
.options
;
1034 for (var i
= 0; i
< options
.length
; ++i
)
1035 options
[i
].selected
= (options
[i
].value
=== value
);
1037 editElement
.value
= value
;
1042 * @param {?string} itemId
1045 _data: function(itemId
)
1047 var inputElements
= this._inputElements(itemId
);
1048 var data
= { __proto__
: null };
1049 var columns
= this.columns();
1050 for (var i
= 0; i
< columns
.length
; ++i
)
1051 data
[columns
[i
]] = inputElements
.get(columns
[i
]).value
;
1056 * @param {?string} itemId
1057 * @return {?Map.<string, (!HTMLInputElement|!HTMLSelectElement)>}
1059 _inputElements: function(itemId
)
1062 return this._addInputElements
;
1063 return this._editInputElements
.get(itemId
) || null;
1067 * @param {?string} itemId
1070 _validateEdit: function(itemId
)
1072 var errorColumns
= this._validateHandler(itemId
, this._data(itemId
));
1073 var hasChanges
= this._hasChanges(itemId
);
1074 var columns
= this.columns();
1075 for (var i
= 0; i
< columns
.length
; ++i
) {
1076 var columnId
= columns
[i
];
1077 var inputElement
= this._inputElements(itemId
).get(columnId
);
1078 if (hasChanges
&& errorColumns
.indexOf(columnId
) !== -1)
1079 inputElement
.classList
.add("editable-item-error");
1081 inputElement
.classList
.remove("editable-item-error");
1083 return !errorColumns
.length
;
1087 * @param {?string} itemId
1090 _hasChanges: function(itemId
)
1092 var columns
= this.columns();
1093 for (var i
= 0; i
< columns
.length
; ++i
) {
1094 var columnId
= columns
[i
];
1095 var oldValue
= this._textElementContent(itemId
, columnId
);
1096 var newValue
= this._inputElements(itemId
).get(columnId
).value
;
1097 if (oldValue
!== newValue
)
1104 * @param {?string} itemId
1105 * @param {!Event} event
1107 _editMappingBlur: function(itemId
, event
)
1109 if (itemId
=== null) {
1110 this._onAddMappingInputBlur(event
);
1114 var inputElements
= this._editInputElements
.get(itemId
).valuesArray();
1115 if (inputElements
.indexOf(event
.relatedTarget
) !== -1)
1118 var listItem
= this.itemForId(itemId
);
1119 listItem
.classList
.remove("item-editing");
1120 delete this._editingId
;
1122 if (!this._hasChanges(itemId
))
1125 if (!this._validateEdit(itemId
)) {
1126 var columns
= this.columns();
1127 for (var i
= 0; i
< columns
.length
; ++i
) {
1128 var columnId
= columns
[i
];
1129 var editElement
= this._editInputElements
.get(itemId
).get(columnId
);
1130 this._setEditElementValue(editElement
, this._textElementContent(itemId
, columnId
));
1131 editElement
.classList
.remove("editable-item-error");
1135 this._editHandler(itemId
, this._data(itemId
));
1139 * @param {!Event} event
1141 _onAddMappingInputBlur: function(event
)
1143 var inputElements
= this._addInputElements
.valuesArray();
1144 if (inputElements
.indexOf(event
.relatedTarget
) !== -1)
1147 if (!this._hasChanges(null))
1150 if (!this._validateEdit(null))
1153 this._editHandler(null, this._data(null));
1154 var columns
= this.columns();
1155 for (var i
= 0; i
< columns
.length
; ++i
) {
1156 var columnId
= columns
[i
];
1157 var editElement
= this._addInputElements
.get(columnId
);
1158 this._setEditElementValue(editElement
, "");
1162 __proto__
: WebInspector
.SettingsList
.prototype
1165 WebInspector
._settingsController
= new WebInspector
.SettingsController();