Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(544)

Side by Side Diff: Source/devtools/front_end/SettingsScreen.js

Issue 283063003: DevTools: Implement extension-based status bar buttons (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Get rid of components/module.json Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
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
13 * distribution.
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.
17 *
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.
29 */
30
31 /**
32 * @constructor
33 * @param {!function()} onHide
34 * @extends {WebInspector.HelpScreen}
35 */
36 WebInspector.SettingsScreen = function(onHide)
37 {
38 WebInspector.HelpScreen.call(this);
39 this.element.id = "settings-screen";
40
41 /** @type {function()} */
42 this._onHide = onHide;
43
44 this._tabbedPane = new WebInspector.TabbedPane();
45 this._tabbedPane.element.classList.add("help-window-main");
46 var settingsLabelElement = document.createElement("div");
47 settingsLabelElement.className = "help-window-label";
48 settingsLabelElement.createTextChild(WebInspector.UIString("Settings"));
49 this._tabbedPane.element.insertBefore(settingsLabelElement, this._tabbedPane .element.firstChild);
50 this._tabbedPane.element.appendChild(this._createCloseButton());
51 this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.General, WebInsp ector.UIString("General"), new WebInspector.GenericSettingsTab());
52 this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Workspace, WebIn spector.UIString("Workspace"), new WebInspector.WorkspaceSettingsTab());
53 if (WebInspector.experimentsSettings.experimentsEnabled)
54 this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Experiments, WebInspector.UIString("Experiments"), new WebInspector.ExperimentsSettingsTab() );
55 this._tabbedPane.appendTab(WebInspector.SettingsScreen.Tabs.Shortcuts, WebIn spector.UIString("Shortcuts"), WebInspector.shortcutsScreen.createShortcutsTabVi ew());
56 this._tabbedPane.shrinkableTabs = false;
57 this._tabbedPane.verticalTabLayout = true;
58
59 this._lastSelectedTabSetting = WebInspector.settings.createSetting("lastSele ctedSettingsTab", WebInspector.SettingsScreen.Tabs.General);
60 this.selectTab(this._lastSelectedTabSetting.get());
61 this._tabbedPane.addEventListener(WebInspector.TabbedPane.EventTypes.TabSele cted, this._tabSelected, this);
62 this.element.addEventListener("keydown", this._keyDown.bind(this), false);
63 this._developerModeCounter = 0;
64 }
65
66 /**
67 * @param {number} min
68 * @param {number} max
69 * @param {string} text
70 * @return {?string}
71 */
72 WebInspector.SettingsScreen.integerValidator = function(min, max, text)
73 {
74 var value = Number(text);
75 if (isNaN(value))
76 return WebInspector.UIString("Invalid number format");
77 if (value < min || value > max)
78 return WebInspector.UIString("Value is out of range [%d, %d]", min, max) ;
79 return null;
80 }
81
82 WebInspector.SettingsScreen.Tabs = {
83 General: "general",
84 Overrides: "overrides",
85 Workspace: "workspace",
86 Experiments: "experiments",
87 Shortcuts: "shortcuts"
88 }
89
90 WebInspector.SettingsScreen.prototype = {
91 /**
92 * @param {string} tabId
93 */
94 selectTab: function(tabId)
95 {
96 this._tabbedPane.selectTab(tabId);
97 },
98
99 /**
100 * @param {!WebInspector.Event} event
101 */
102 _tabSelected: function(event)
103 {
104 this._lastSelectedTabSetting.set(this._tabbedPane.selectedTabId);
105 },
106
107 /**
108 * @override
109 */
110 wasShown: function()
111 {
112 this._tabbedPane.show(this.element);
113 WebInspector.HelpScreen.prototype.wasShown.call(this);
114 },
115
116 /**
117 * @override
118 * @return {boolean}
119 */
120 isClosingKey: function(keyCode)
121 {
122 return [
123 WebInspector.KeyboardShortcut.Keys.Enter.code,
124 WebInspector.KeyboardShortcut.Keys.Esc.code,
125 ].indexOf(keyCode) >= 0;
126 },
127
128 /**
129 * @override
130 */
131 willHide: function()
132 {
133 this._onHide();
134 WebInspector.HelpScreen.prototype.willHide.call(this);
135 },
136
137 /**
138 * @param {?Event} event
139 */
140 _keyDown: function(event)
141 {
142 var shiftKeyCode = 16;
143 if (event.keyCode === shiftKeyCode && ++this._developerModeCounter > 5)
144 this.element.classList.add("settings-developer-mode");
145 },
146
147 __proto__: WebInspector.HelpScreen.prototype
148 }
149
150 /**
151 * @constructor
152 * @extends {WebInspector.VBox}
153 * @param {string} name
154 * @param {string=} id
155 */
156 WebInspector.SettingsTab = function(name, id)
157 {
158 WebInspector.VBox.call(this);
159 this.element.classList.add("settings-tab-container");
160 if (id)
161 this.element.id = id;
162 var header = this.element.createChild("header");
163 header.createChild("h3").appendChild(document.createTextNode(name));
164 this.containerElement = this.element.createChild("div", "help-container-wrap per").createChild("div", "settings-tab help-content help-container");
165 }
166
167 WebInspector.SettingsTab.prototype = {
168 /**
169 * @param {string=} name
170 * @return {!Element}
171 */
172 _appendSection: function(name)
173 {
174 var block = this.containerElement.createChild("div", "help-block");
175 if (name)
176 block.createChild("div", "help-section-title").textContent = name;
177 return block;
178 },
179
180 _createSelectSetting: function(name, options, setting)
181 {
182 var p = document.createElement("p");
183 var labelElement = p.createChild("label");
184 labelElement.textContent = name;
185
186 var select = p.createChild("select");
187 var settingValue = setting.get();
188
189 for (var i = 0; i < options.length; ++i) {
190 var option = options[i];
191 select.add(new Option(option[0], option[1]));
192 if (settingValue === option[1])
193 select.selectedIndex = i;
194 }
195
196 function changeListener(e)
197 {
198 // Don't use e.target.value to avoid conversion of the value to stri ng.
199 setting.set(options[select.selectedIndex][1]);
200 }
201
202 select.addEventListener("change", changeListener, false);
203 return p;
204 },
205
206 __proto__: WebInspector.VBox.prototype
207 }
208
209 /**
210 * @constructor
211 * @extends {WebInspector.SettingsTab}
212 */
213 WebInspector.GenericSettingsTab = function()
214 {
215 WebInspector.SettingsTab.call(this, WebInspector.UIString("General"), "gener al-tab-content");
216
217 this._populateSectionsFromExtensions();
218
219 var restoreDefaults = this._appendSection().createChild("input", "settings-t ab-text-button");
220 restoreDefaults.type = "button";
221 restoreDefaults.value = WebInspector.UIString("Restore defaults and reload") ;
222 restoreDefaults.addEventListener("click", restoreAndReload);
223
224 function restoreAndReload()
225 {
226 if (window.localStorage)
227 window.localStorage.clear();
228 WebInspector.reload();
229 }
230 }
231
232 WebInspector.GenericSettingsTab.prototype = {
233 _populateSectionsFromExtensions: function()
234 {
235 /** @const */
236 var explicitSectionOrder = ["", "Appearance", "Elements", "Sources", "Pr ofiler", "Console", "Extensions"];
237
238 var allExtensions = WebInspector.moduleManager.extensions("ui-setting");
239
240 /** @type {!StringMultimap.<!WebInspector.ModuleManager.Extension>} */
241 var extensionsBySectionId = new StringMultimap();
242 /** @type {!StringMultimap.<!WebInspector.ModuleManager.Extension>} */
243 var childSettingExtensionsByParentName = new StringMultimap();
244
245 allExtensions.forEach(function(extension) {
246 var descriptor = extension.descriptor();
247 var sectionName = descriptor["section"] || "";
248 if (!sectionName && descriptor["parentSettingName"]) {
249 childSettingExtensionsByParentName.put(descriptor["parentSetting Name"], extension);
250 return;
251 }
252 extensionsBySectionId.put(sectionName, extension);
253 });
254
255 var sectionIds = extensionsBySectionId.keys();
256 var explicitlyOrderedSections = {};
257 for (var i = 0; i < explicitSectionOrder.length; ++i) {
258 explicitlyOrderedSections[explicitSectionOrder[i]] = true;
259 var extensions = extensionsBySectionId.get(explicitSectionOrder[i]);
260 if (!extensions.size())
261 continue;
262 this._addSectionWithExtensionProvidedSettings(explicitSectionOrder[i ], extensions.values(), childSettingExtensionsByParentName);
263 }
264 for (var i = 0; i < sectionIds.length; ++i) {
265 if (explicitlyOrderedSections[sectionIds[i]])
266 continue;
267 this._addSectionWithExtensionProvidedSettings(sectionIds[i], extensi onsBySectionId.get(sectionIds[i]).values(), childSettingExtensionsByParentName);
268 }
269 },
270
271 /**
272 * @param {string} sectionName
273 * @param {!Array.<!WebInspector.ModuleManager.Extension>} extensions
274 * @param {!StringMultimap.<!WebInspector.ModuleManager.Extension>} childSet tingExtensionsByParentName
275 */
276 _addSectionWithExtensionProvidedSettings: function(sectionName, extensions, childSettingExtensionsByParentName)
277 {
278 var uiSectionName = sectionName && WebInspector.UIString(sectionName);
279 var sectionElement = this._appendSection(uiSectionName);
280 extensions.forEach(processSetting.bind(this, null));
281
282 /**
283 * @param {?Element} parentFieldset
284 * @param {!WebInspector.ModuleManager.Extension} extension
285 * @this {WebInspector.GenericSettingsTab}
286 */
287 function processSetting(parentFieldset, extension)
288 {
289 var descriptor = extension.descriptor();
290 var experimentName = descriptor["experiment"];
291 if (experimentName && (!WebInspector.experimentsSettings[experimentN ame] || !WebInspector.experimentsSettings[experimentName].isEnabled()))
292 return;
293
294 var settingName = descriptor["settingName"];
295 var setting = WebInspector.settings[settingName];
296 var instance = extension.instance();
297 var settingControl;
298 if (instance && descriptor["settingType"] === "custom") {
299 settingControl = instance.settingElement();
300 if (!settingControl)
301 return;
302 }
303 if (!settingControl) {
304 var uiTitle = WebInspector.UIString(descriptor["title"]);
305 settingControl = createSettingControl.call(this, uiTitle, settin g, descriptor, instance);
306 }
307 if (settingName) {
308 var childSettings = childSettingExtensionsByParentName.get(setti ngName);
309 if (childSettings.size()) {
310 var fieldSet = WebInspector.SettingsUI.createSettingFieldset (setting);
311 settingControl.appendChild(fieldSet);
312 childSettings.values().forEach(function(item) { processSetti ng.call(this, fieldSet, item); }, this);
313 }
314 }
315 var containerElement = parentFieldset || sectionElement;
316 containerElement.appendChild(settingControl);
317 }
318
319 /**
320 * @param {string} uiTitle
321 * @param {!WebInspector.Setting} setting
322 * @param {!Object} descriptor
323 * @param {?Object} instance
324 * @return {!Element}
325 * @this {WebInspector.GenericSettingsTab}
326 */
327 function createSettingControl(uiTitle, setting, descriptor, instance)
328 {
329 switch (descriptor["settingType"]) {
330 case "checkbox":
331 return WebInspector.SettingsUI.createSettingCheckbox(uiTitle, se tting);
332 case "select":
333 var descriptorOptions = descriptor["options"]
334 var options = new Array(descriptorOptions.length);
335 for (var i = 0; i < options.length; ++i) {
336 // The third array item flags that the option name is "raw" (non-i18n-izable).
337 var optionName = descriptorOptions[i][2] ? descriptorOptions [i][0] : WebInspector.UIString(descriptorOptions[i][0]);
338 options[i] = [WebInspector.UIString(descriptorOptions[i][0]) , descriptorOptions[i][1]];
339 }
340 return this._createSelectSetting(uiTitle, options, setting);
341 default:
342 throw "Invalid setting type: " + descriptor["settingType"];
343 }
344 }
345 },
346
347 /**
348 * @param {?Element} p
349 */
350 _appendDrawerNote: function(p)
351 {
352 var noteElement = p.createChild("div", "help-field-note");
353 noteElement.createTextChild("Hit ");
354 noteElement.createChild("span", "help-key").textContent = "Esc";
355 noteElement.createTextChild(WebInspector.UIString(" or click the"));
356 noteElement.appendChild(new WebInspector.StatusBarButton(WebInspector.UI String("Drawer"), "console-status-bar-item").element);
357 noteElement.createTextChild(WebInspector.UIString("toolbar item"));
358 },
359
360 __proto__: WebInspector.SettingsTab.prototype
361 }
362
363 /**
364 * @constructor
365 * @extends {WebInspector.SettingsTab}
366 */
367 WebInspector.WorkspaceSettingsTab = function()
368 {
369 WebInspector.SettingsTab.call(this, WebInspector.UIString("Workspace"), "wor kspace-tab-content");
370 WebInspector.isolatedFileSystemManager.addEventListener(WebInspector.Isolate dFileSystemManager.Events.FileSystemAdded, this._fileSystemAdded, this);
371 WebInspector.isolatedFileSystemManager.addEventListener(WebInspector.Isolate dFileSystemManager.Events.FileSystemRemoved, this._fileSystemRemoved, this);
372
373 this._commonSection = this._appendSection(WebInspector.UIString("Common"));
374 var folderExcludePatternInput = WebInspector.SettingsUI.createSettingInputFi eld(WebInspector.UIString("Folder exclude pattern"), WebInspector.settings.works paceFolderExcludePattern, false, 0, "270px", WebInspector.SettingsUI.regexValida tor);
375 this._commonSection.appendChild(folderExcludePatternInput);
376
377 this._fileSystemsSection = this._appendSection(WebInspector.UIString("Folder s"));
378 this._fileSystemsListContainer = this._fileSystemsSection.createChild("p", " settings-list-container");
379
380 this._addFileSystemRowElement = this._fileSystemsSection.createChild("div");
381 var addFileSystemButton = this._addFileSystemRowElement.createChild("input", "settings-tab-text-button");
382 addFileSystemButton.type = "button";
383 addFileSystemButton.value = WebInspector.UIString("Add folder\u2026");
384 addFileSystemButton.addEventListener("click", this._addFileSystemClicked.bin d(this));
385
386 this._editFileSystemButton = this._addFileSystemRowElement.createChild("inpu t", "settings-tab-text-button");
387 this._editFileSystemButton.type = "button";
388 this._editFileSystemButton.value = WebInspector.UIString("Edit\u2026");
389 this._editFileSystemButton.addEventListener("click", this._editFileSystemCli cked.bind(this));
390 this._updateEditFileSystemButtonState();
391
392 this._reset();
393 }
394
395 WebInspector.WorkspaceSettingsTab.prototype = {
396 wasShown: function()
397 {
398 WebInspector.SettingsTab.prototype.wasShown.call(this);
399 this._reset();
400 },
401
402 _reset: function()
403 {
404 this._resetFileSystems();
405 },
406
407 _resetFileSystems: function()
408 {
409 this._fileSystemsListContainer.removeChildren();
410 var fileSystemPaths = WebInspector.isolatedFileSystemManager.mapping().f ileSystemPaths();
411 delete this._fileSystemsList;
412
413 if (!fileSystemPaths.length) {
414 var noFileSystemsMessageElement = this._fileSystemsListContainer.cre ateChild("div", "no-file-systems-message");
415 noFileSystemsMessageElement.textContent = WebInspector.UIString("You have no file systems added.");
416 return;
417 }
418
419 this._fileSystemsList = new WebInspector.SettingsList(["path"], this._re nderFileSystem.bind(this));
420 this._fileSystemsList.element.classList.add("file-systems-list");
421 this._fileSystemsList.addEventListener(WebInspector.SettingsList.Events. Selected, this._fileSystemSelected.bind(this));
422 this._fileSystemsList.addEventListener(WebInspector.SettingsList.Events. Removed, this._fileSystemRemovedfromList.bind(this));
423 this._fileSystemsList.addEventListener(WebInspector.SettingsList.Events. DoubleClicked, this._fileSystemDoubleClicked.bind(this));
424 this._fileSystemsListContainer.appendChild(this._fileSystemsList.element );
425 for (var i = 0; i < fileSystemPaths.length; ++i)
426 this._fileSystemsList.addItem(fileSystemPaths[i]);
427 this._updateEditFileSystemButtonState();
428 },
429
430 _updateEditFileSystemButtonState: function()
431 {
432 this._editFileSystemButton.disabled = !this._selectedFileSystemPath();
433 },
434
435 /**
436 * @param {!WebInspector.Event} event
437 */
438 _fileSystemSelected: function(event)
439 {
440 this._updateEditFileSystemButtonState();
441 },
442
443 /**
444 * @param {!WebInspector.Event} event
445 */
446 _fileSystemDoubleClicked: function(event)
447 {
448 var id = /** @type{?string} */ (event.data);
449 this._editFileSystem(id);
450 },
451
452 /**
453 * @param {!WebInspector.Event=} event
454 */
455 _editFileSystemClicked: function(event)
456 {
457 this._editFileSystem(this._selectedFileSystemPath());
458 },
459
460 /**
461 * @param {?string} id
462 */
463 _editFileSystem: function(id)
464 {
465 WebInspector.EditFileSystemDialog.show(WebInspector.inspectorView.elemen t, id);
466 },
467
468 /**
469 * @param {function(?Event)} handler
470 * @return {!Element}
471 */
472 _createRemoveButton: function(handler)
473 {
474 var removeButton = document.createElement("button");
475 removeButton.classList.add("button");
476 removeButton.classList.add("remove-item-button");
477 removeButton.value = WebInspector.UIString("Remove");
478 if (handler)
479 removeButton.addEventListener("click", handler, false);
480 else
481 removeButton.disabled = true;
482 return removeButton;
483 },
484
485 /**
486 * @param {!Element} columnElement
487 * @param {string} column
488 * @param {?string} id
489 */
490 _renderFileSystem: function(columnElement, column, id)
491 {
492 if (!id)
493 return "";
494 var fileSystemPath = id;
495 var textElement = columnElement.createChild("span", "list-column-text");
496 var pathElement = textElement.createChild("span", "file-system-path");
497 pathElement.title = fileSystemPath;
498
499 const maxTotalPathLength = 55;
500 const maxFolderNameLength = 30;
501
502 var lastIndexOfSlash = fileSystemPath.lastIndexOf(WebInspector.isWin() ? "\\" : "/");
503 var folderName = fileSystemPath.substr(lastIndexOfSlash + 1);
504 var folderPath = fileSystemPath.substr(0, lastIndexOfSlash + 1);
505 folderPath = folderPath.trimMiddle(maxTotalPathLength - Math.min(maxFold erNameLength, folderName.length));
506 folderName = folderName.trimMiddle(maxFolderNameLength);
507
508 var folderPathElement = pathElement.createChild("span");
509 folderPathElement.textContent = folderPath;
510
511 var nameElement = pathElement.createChild("span", "file-system-path-name ");
512 nameElement.textContent = folderName;
513 },
514
515 /**
516 * @param {!WebInspector.Event} event
517 */
518 _fileSystemRemovedfromList: function(event)
519 {
520 var id = /** @type{?string} */ (event.data);
521 if (!id)
522 return;
523 WebInspector.isolatedFileSystemManager.removeFileSystem(id);
524 },
525
526 _addFileSystemClicked: function()
527 {
528 WebInspector.isolatedFileSystemManager.addFileSystem();
529 },
530
531 _fileSystemAdded: function(event)
532 {
533 var fileSystem = /** @type {!WebInspector.IsolatedFileSystem} */ (event. data);
534 if (!this._fileSystemsList)
535 this._reset();
536 else
537 this._fileSystemsList.addItem(fileSystem.path());
538 },
539
540 _fileSystemRemoved: function(event)
541 {
542 var fileSystem = /** @type {!WebInspector.IsolatedFileSystem} */ (event. data);
543 var selectedFileSystemPath = this._selectedFileSystemPath();
544 if (this._fileSystemsList.itemForId(fileSystem.path()))
545 this._fileSystemsList.removeItem(fileSystem.path());
546 if (!this._fileSystemsList.itemIds().length)
547 this._reset();
548 this._updateEditFileSystemButtonState();
549 },
550
551 _selectedFileSystemPath: function()
552 {
553 return this._fileSystemsList ? this._fileSystemsList.selectedId() : null ;
554 },
555
556 __proto__: WebInspector.SettingsTab.prototype
557 }
558
559
560 /**
561 * @constructor
562 * @extends {WebInspector.SettingsTab}
563 */
564 WebInspector.ExperimentsSettingsTab = function()
565 {
566 WebInspector.SettingsTab.call(this, WebInspector.UIString("Experiments"), "e xperiments-tab-content");
567
568 var experiments = WebInspector.experimentsSettings.experiments;
569 if (experiments.length) {
570 var experimentsSection = this._appendSection();
571 experimentsSection.appendChild(this._createExperimentsWarningSubsection( ));
572 for (var i = 0; i < experiments.length; ++i)
573 experimentsSection.appendChild(this._createExperimentCheckbox(experi ments[i]));
574 }
575 }
576
577 WebInspector.ExperimentsSettingsTab.prototype = {
578 /**
579 * @return {!Element} element
580 */
581 _createExperimentsWarningSubsection: function()
582 {
583 var subsection = document.createElement("div");
584 var warning = subsection.createChild("span", "settings-experiments-warni ng-subsection-warning");
585 warning.textContent = WebInspector.UIString("WARNING:");
586 subsection.appendChild(document.createTextNode(" "));
587 var message = subsection.createChild("span", "settings-experiments-warni ng-subsection-message");
588 message.textContent = WebInspector.UIString("These experiments could be dangerous and may require restart.");
589 return subsection;
590 },
591
592 _createExperimentCheckbox: function(experiment)
593 {
594 var input = document.createElement("input");
595 input.type = "checkbox";
596 input.name = experiment.name;
597 input.checked = experiment.isEnabled();
598 function listener()
599 {
600 experiment.setEnabled(input.checked);
601 }
602 input.addEventListener("click", listener, false);
603
604 var p = document.createElement("p");
605 p.className = experiment.hidden && !experiment.isEnabled() ? "settings-e xperiment-hidden" : "";
606 var label = p.createChild("label");
607 label.appendChild(input);
608 label.appendChild(document.createTextNode(WebInspector.UIString(experime nt.title)));
609 p.appendChild(label);
610 return p;
611 },
612
613 __proto__: WebInspector.SettingsTab.prototype
614 }
615
616 /**
617 * @constructor
618 */
619 WebInspector.SettingsController = function()
620 {
621 this._statusBarButton = new WebInspector.StatusBarButton(WebInspector.UIStri ng("Settings"), "settings-status-bar-item");
622 this._statusBarButton.element.addEventListener("mouseup", this._mouseUp.bind (this), false);
623
624 /** @type {?WebInspector.SettingsScreen} */
625 this._settingsScreen;
626 }
627
628 WebInspector.SettingsController.prototype =
629 {
630 /**
631 * @return {!Element}
632 */
633 get statusBarItem()
634 {
635 return this._statusBarButton.element;
636 },
637
638 _mouseUp: function()
639 {
640 this.showSettingsScreen();
641 },
642
643 _onHideSettingsScreen: function()
644 {
645 delete this._settingsScreenVisible;
646 },
647
648 /**
649 * @param {string=} tabId
650 */
651 showSettingsScreen: function(tabId)
652 {
653 if (!this._settingsScreen)
654 this._settingsScreen = new WebInspector.SettingsScreen(this._onHideS ettingsScreen.bind(this));
655
656 if (tabId)
657 this._settingsScreen.selectTab(tabId);
658
659 this._settingsScreen.showModal();
660 this._settingsScreenVisible = true;
661 },
662
663 resize: function()
664 {
665 if (this._settingsScreen && this._settingsScreen.isShowing())
666 this._settingsScreen.doResize();
667 }
668 }
669
670 /**
671 * @constructor
672 * @implements {WebInspector.ActionDelegate}
673 */
674 WebInspector.SettingsController.SettingsScreenActionDelegate = function() { }
675
676 WebInspector.SettingsController.SettingsScreenActionDelegate.prototype = {
677 /**
678 * @return {boolean}
679 */
680 handleAction: function()
681 {
682 WebInspector.settingsController.showSettingsScreen(WebInspector.Settings Screen.Tabs.General);
683 return true;
684 }
685 }
686
687 /**
688 * @constructor
689 * @extends {WebInspector.Object}
690 * @param {!Array.<string>} columns
691 * @param {function(!Element, string, ?string)} itemRenderer
692 */
693 WebInspector.SettingsList = function(columns, itemRenderer)
694 {
695 this.element = document.createElement("div");
696 this.element.classList.add("settings-list");
697 this.element.tabIndex = -1;
698 this._itemRenderer = itemRenderer;
699 this._listItems = {};
700 this._ids = [];
701 this._columns = columns;
702 }
703
704 WebInspector.SettingsList.Events = {
705 Selected: "Selected",
706 Removed: "Removed",
707 DoubleClicked: "DoubleClicked",
708 }
709
710 WebInspector.SettingsList.prototype = {
711 /**
712 * @param {?string} itemId
713 * @param {?string=} beforeId
714 * @return {!Element}
715 */
716 addItem: function(itemId, beforeId)
717 {
718 var listItem = document.createElement("div");
719 listItem._id = itemId;
720 listItem.classList.add("settings-list-item");
721 if (typeof beforeId !== undefined)
722 this.element.insertBefore(listItem, this._listItems[beforeId]);
723 else
724 this.element.appendChild(listItem);
725
726 var listItemContents = listItem.createChild("div", "settings-list-item-c ontents");
727 var listItemColumnsElement = listItemContents.createChild("div", "settin gs-list-item-columns");
728
729 listItem.columnElements = {};
730 for (var i = 0; i < this._columns.length; ++i) {
731 var columnElement = listItemColumnsElement.createChild("div", "list- column");
732 var columnId = this._columns[i];
733 listItem.columnElements[columnId] = columnElement;
734 this._itemRenderer(columnElement, columnId, itemId);
735 }
736 var removeItemButton = this._createRemoveButton(removeItemClicked.bind(t his));
737 listItemContents.addEventListener("click", this.selectItem.bind(this, it emId), false);
738 listItemContents.addEventListener("dblclick", this._onDoubleClick.bind(t his, itemId), false);
739 listItemContents.appendChild(removeItemButton);
740
741 this._listItems[itemId] = listItem;
742 if (typeof beforeId !== undefined)
743 this._ids.splice(this._ids.indexOf(beforeId), 0, itemId);
744 else
745 this._ids.push(itemId);
746
747 /**
748 * @param {?Event} event
749 * @this {WebInspector.SettingsList}
750 */
751 function removeItemClicked(event)
752 {
753 removeItemButton.disabled = true;
754 this.removeItem(itemId);
755 this.dispatchEventToListeners(WebInspector.SettingsList.Events.Remov ed, itemId);
756 event.consume();
757 }
758
759 return listItem;
760 },
761
762 /**
763 * @param {?string} id
764 */
765 removeItem: function(id)
766 {
767 this._listItems[id].remove();
768 delete this._listItems[id];
769 this._ids.remove(id);
770 if (id === this._selectedId) {
771 delete this._selectedId;
772 if (this._ids.length)
773 this.selectItem(this._ids[0]);
774 }
775 },
776
777 /**
778 * @return {!Array.<?string>}
779 */
780 itemIds: function()
781 {
782 return this._ids.slice();
783 },
784
785 /**
786 * @return {!Array.<string>}
787 */
788 columns: function()
789 {
790 return this._columns.slice();
791 },
792
793 /**
794 * @return {?string}
795 */
796 selectedId: function()
797 {
798 return this._selectedId;
799 },
800
801 /**
802 * @return {!Element}
803 */
804 selectedItem: function()
805 {
806 return this._selectedId ? this._listItems[this._selectedId] : null;
807 },
808
809 /**
810 * @param {string} itemId
811 * @return {!Element}
812 */
813 itemForId: function(itemId)
814 {
815 return this._listItems[itemId];
816 },
817
818 /**
819 * @param {?string} id
820 * @param {!Event=} event
821 */
822 _onDoubleClick: function(id, event)
823 {
824 this.dispatchEventToListeners(WebInspector.SettingsList.Events.DoubleCli cked, id);
825 },
826
827 /**
828 * @param {?string} id
829 * @param {!Event=} event
830 */
831 selectItem: function(id, event)
832 {
833 if (typeof this._selectedId !== "undefined") {
834 this._listItems[this._selectedId].classList.remove("selected");
835 }
836
837 this._selectedId = id;
838 if (typeof this._selectedId !== "undefined") {
839 this._listItems[this._selectedId].classList.add("selected");
840 }
841 this.dispatchEventToListeners(WebInspector.SettingsList.Events.Selected, id);
842 if (event)
843 event.consume();
844 },
845
846 /**
847 * @param {function(?Event)} handler
848 * @return {!Element}
849 */
850 _createRemoveButton: function(handler)
851 {
852 var removeButton = document.createElement("button");
853 removeButton.classList.add("remove-item-button");
854 removeButton.value = WebInspector.UIString("Remove");
855 removeButton.addEventListener("click", handler, false);
856 return removeButton;
857 },
858
859 __proto__: WebInspector.Object.prototype
860 }
861
862 /**
863 * @constructor
864 * @extends {WebInspector.SettingsList}
865 * @param {!Array.<string>} columns
866 * @param {function(string, string):string} valuesProvider
867 * @param {function(?string, !Object)} validateHandler
868 * @param {function(?string, !Object)} editHandler
869 */
870 WebInspector.EditableSettingsList = function(columns, valuesProvider, validateHa ndler, editHandler)
871 {
872 WebInspector.SettingsList.call(this, columns, this._renderColumn.bind(this)) ;
873 this._validateHandler = validateHandler;
874 this._editHandler = editHandler;
875 this._valuesProvider = valuesProvider;
876 /** @type {!Object.<string, !HTMLInputElement>} */
877 this._addInputElements = {};
878 /** @type {!Object.<string, !Object.<string, !HTMLInputElement>>} */
879 this._editInputElements = {};
880 /** @type {!Object.<string, !Object.<string, !HTMLSpanElement>>} */
881 this._textElements = {};
882
883 this._addMappingItem = this.addItem(null);
884 this._addMappingItem.classList.add("item-editing");
885 this._addMappingItem.classList.add("add-list-item");
886 }
887
888 WebInspector.EditableSettingsList.prototype = {
889 /**
890 * @param {?string} itemId
891 * @param {?string=} beforeId
892 * @return {!Element}
893 */
894 addItem: function(itemId, beforeId)
895 {
896 var listItem = WebInspector.SettingsList.prototype.addItem.call(this, it emId, beforeId);
897 listItem.classList.add("editable");
898 return listItem;
899 },
900
901 /**
902 * @param {!Element} columnElement
903 * @param {string} columnId
904 * @param {?string} itemId
905 */
906 _renderColumn: function(columnElement, columnId, itemId)
907 {
908 columnElement.classList.add("settings-list-column-" + columnId);
909 var placeholder = (columnId === "url") ? WebInspector.UIString("URL pref ix") : WebInspector.UIString("Folder path");
910 if (itemId === null) {
911 var inputElement = columnElement.createChild("input", "list-column-e ditor");
912 inputElement.placeholder = placeholder;
913 inputElement.addEventListener("blur", this._onAddMappingInputBlur.bi nd(this));
914 inputElement.addEventListener("input", this._validateEdit.bind(this, itemId));
915 this._addInputElements[columnId] = inputElement;
916 return;
917 }
918 var validItemId = itemId;
919
920 if (!this._editInputElements[itemId])
921 this._editInputElements[itemId] = {};
922 if (!this._textElements[itemId])
923 this._textElements[itemId] = {};
924
925 var value = this._valuesProvider(itemId, columnId);
926
927 var textElement = columnElement.createChild("span", "list-column-text");
928 textElement.textContent = value;
929 textElement.title = value;
930 columnElement.addEventListener("click", rowClicked.bind(this), false);
931 this._textElements[itemId][columnId] = textElement;
932
933 var inputElement = columnElement.createChild("input", "list-column-edito r");
934 inputElement.value = value;
935 inputElement.addEventListener("blur", this._editMappingBlur.bind(this, i temId));
936 inputElement.addEventListener("input", this._validateEdit.bind(this, ite mId));
937 columnElement.inputElement = inputElement;
938 this._editInputElements[itemId][columnId] = inputElement;
939
940 /**
941 * @param {?Event} event
942 * @this {WebInspector.EditableSettingsList}
943 */
944 function rowClicked(event)
945 {
946 if (itemId === this._editingId)
947 return;
948 event.consume();
949 console.assert(!this._editingId);
950 this._editingId = validItemId;
951 var listItem = this.itemForId(validItemId);
952 listItem.classList.add("item-editing");
953 var inputElement = event.target.inputElement || this._editInputEleme nts[validItemId][this.columns()[0]];
954 inputElement.focus();
955 inputElement.select();
956 }
957 },
958
959 /**
960 * @param {?string} itemId
961 * @return {!Object}
962 */
963 _data: function(itemId)
964 {
965 var inputElements = this._inputElements(itemId);
966 var data = {};
967 var columns = this.columns();
968 for (var i = 0; i < columns.length; ++i)
969 data[columns[i]] = inputElements[columns[i]].value;
970 return data;
971 },
972
973 /**
974 * @param {?string} itemId
975 * @return {?Object.<string, !HTMLInputElement>}
976 */
977 _inputElements: function(itemId)
978 {
979 if (!itemId)
980 return this._addInputElements;
981 return this._editInputElements[itemId] || null;
982 },
983
984 /**
985 * @param {?string} itemId
986 * @return {boolean}
987 */
988 _validateEdit: function(itemId)
989 {
990 var errorColumns = this._validateHandler(itemId, this._data(itemId));
991 var hasChanges = this._hasChanges(itemId);
992 var columns = this.columns();
993 for (var i = 0; i < columns.length; ++i) {
994 var columnId = columns[i];
995 var inputElement = this._inputElements(itemId)[columnId];
996 if (hasChanges && errorColumns.indexOf(columnId) !== -1)
997 inputElement.classList.add("editable-item-error");
998 else
999 inputElement.classList.remove("editable-item-error");
1000 }
1001 return !errorColumns.length;
1002 },
1003
1004 /**
1005 * @param {?string} itemId
1006 * @return {boolean}
1007 */
1008 _hasChanges: function(itemId)
1009 {
1010 var hasChanges = false;
1011 var columns = this.columns();
1012 for (var i = 0; i < columns.length; ++i) {
1013 var columnId = columns[i];
1014 var oldValue = itemId ? this._textElements[itemId][columnId].textCon tent : "";
1015 var newValue = this._inputElements(itemId)[columnId].value;
1016 if (oldValue !== newValue) {
1017 hasChanges = true;
1018 break;
1019 }
1020 }
1021 return hasChanges;
1022 },
1023
1024 /**
1025 * @param {string} itemId
1026 * @param {!Event} event
1027 */
1028 _editMappingBlur: function(itemId, event)
1029 {
1030 var inputElements = Object.values(this._editInputElements[itemId]);
1031 if (inputElements.indexOf(event.relatedTarget) !== -1)
1032 return;
1033
1034 var listItem = this.itemForId(itemId);
1035 listItem.classList.remove("item-editing");
1036 delete this._editingId;
1037
1038 if (!this._hasChanges(itemId))
1039 return;
1040
1041 if (!this._validateEdit(itemId)) {
1042 var columns = this.columns();
1043 for (var i = 0; i < columns.length; ++i) {
1044 var columnId = columns[i];
1045 var inputElement = this._editInputElements[itemId][columnId];
1046 inputElement.value = this._textElements[itemId][columnId].textCo ntent;
1047 inputElement.classList.remove("editable-item-error");
1048 }
1049 return;
1050 }
1051 this._editHandler(itemId, this._data(itemId));
1052 },
1053
1054 _onAddMappingInputBlur: function(event)
1055 {
1056 var inputElements = Object.values(this._addInputElements);
1057 if (inputElements.indexOf(event.relatedTarget) !== -1)
1058 return;
1059
1060 if (!this._hasChanges(null))
1061 return;
1062
1063 if (!this._validateEdit(null))
1064 return;
1065
1066 this._editHandler(null, this._data(null));
1067 var columns = this.columns();
1068 for (var i = 0; i < columns.length; ++i) {
1069 var columnId = columns[i];
1070 var inputElement = this._addInputElements[columnId];
1071 inputElement.value = "";
1072 }
1073 },
1074
1075 __proto__: WebInspector.SettingsList.prototype
1076 }
1077
1078 /** @type {!WebInspector.SettingsController} */
1079 WebInspector.settingsController;
OLDNEW
« no previous file with comments | « Source/devtools/front_end/EditFileSystemDialog.js ('k') | Source/devtools/front_end/components/DockController.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698