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

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: Address comments 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 = /** @type {!Set.<!WebInspector.ModuleManager.Extens ion>} */ (extensionsBySectionId.get(explicitSectionOrder[i]));
260 if (!extensions)
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], /** @ty pe {!Set.<!WebInspector.ModuleManager.Extension>} */ (extensionsBySectionId.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 = /** @type {!Set.<!WebInspector.ModuleManager .Extension>|undefined} */ (childSettingExtensionsByParentName.get(settingName));
309 if (childSettings) {
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 {function(!Element, string, ?string)} itemRenderer
691 */
692 WebInspector.SettingsList = function(columns, itemRenderer)
693 {
694 this.element = document.createElement("div");
695 this.element.classList.add("settings-list");
696 this.element.tabIndex = -1;
697 this._itemRenderer = itemRenderer;
698 this._listItems = {};
699 this._ids = [];
700 this._columns = columns;
701 }
702
703 WebInspector.SettingsList.Events = {
704 Selected: "Selected",
705 Removed: "Removed",
706 DoubleClicked: "DoubleClicked",
707 }
708
709 WebInspector.SettingsList.prototype = {
710 /**
711 * @param {?string} itemId
712 * @param {?string=} beforeId
713 * @return {!Element}
714 */
715 addItem: function(itemId, beforeId)
716 {
717 var listItem = document.createElement("div");
718 listItem._id = itemId;
719 listItem.classList.add("settings-list-item");
720 if (typeof beforeId !== undefined)
721 this.element.insertBefore(listItem, this._listItems[beforeId]);
722 else
723 this.element.appendChild(listItem);
724
725 var listItemContents = listItem.createChild("div", "settings-list-item-c ontents");
726 var listItemColumnsElement = listItemContents.createChild("div", "settin gs-list-item-columns");
727
728 listItem.columnElements = {};
729 for (var i = 0; i < this._columns.length; ++i) {
730 var columnElement = listItemColumnsElement.createChild("div", "list- column");
731 var columnId = this._columns[i];
732 listItem.columnElements[columnId] = columnElement;
733 this._itemRenderer(columnElement, columnId, itemId);
734 }
735 var removeItemButton = this._createRemoveButton(removeItemClicked.bind(t his));
736 listItemContents.addEventListener("click", this.selectItem.bind(this, it emId), false);
737 listItemContents.addEventListener("dblclick", this._onDoubleClick.bind(t his, itemId), false);
738 listItemContents.appendChild(removeItemButton);
739
740 this._listItems[itemId] = listItem;
741 if (typeof beforeId !== undefined)
742 this._ids.splice(this._ids.indexOf(beforeId), 0, itemId);
743 else
744 this._ids.push(itemId);
745
746 /**
747 * @param {?Event} event
748 * @this {WebInspector.SettingsList}
749 */
750 function removeItemClicked(event)
751 {
752 removeItemButton.disabled = true;
753 this.removeItem(itemId);
754 this.dispatchEventToListeners(WebInspector.SettingsList.Events.Remov ed, itemId);
755 event.consume();
756 }
757
758 return listItem;
759 },
760
761 /**
762 * @param {?string} id
763 */
764 removeItem: function(id)
765 {
766 this._listItems[id].remove();
767 delete this._listItems[id];
768 this._ids.remove(id);
769 if (id === this._selectedId) {
770 delete this._selectedId;
771 if (this._ids.length)
772 this.selectItem(this._ids[0]);
773 }
774 },
775
776 /**
777 * @return {!Array.<?string>}
778 */
779 itemIds: function()
780 {
781 return this._ids.slice();
782 },
783
784 /**
785 * @return {!Array.<string>}
786 */
787 columns: function()
788 {
789 return this._columns.slice();
790 },
791
792 /**
793 * @return {?string}
794 */
795 selectedId: function()
796 {
797 return this._selectedId;
798 },
799
800 /**
801 * @return {!Element}
802 */
803 selectedItem: function()
804 {
805 return this._selectedId ? this._listItems[this._selectedId] : null;
806 },
807
808 /**
809 * @param {string} itemId
810 * @return {!Element}
811 */
812 itemForId: function(itemId)
813 {
814 return this._listItems[itemId];
815 },
816
817 /**
818 * @param {?string} id
819 * @param {!Event=} event
820 */
821 _onDoubleClick: function(id, event)
822 {
823 this.dispatchEventToListeners(WebInspector.SettingsList.Events.DoubleCli cked, id);
824 },
825
826 /**
827 * @param {?string} id
828 * @param {!Event=} event
829 */
830 selectItem: function(id, event)
831 {
832 if (typeof this._selectedId !== "undefined") {
833 this._listItems[this._selectedId].classList.remove("selected");
834 }
835
836 this._selectedId = id;
837 if (typeof this._selectedId !== "undefined") {
838 this._listItems[this._selectedId].classList.add("selected");
839 }
840 this.dispatchEventToListeners(WebInspector.SettingsList.Events.Selected, id);
841 if (event)
842 event.consume();
843 },
844
845 /**
846 * @param {function(?Event)} handler
847 * @return {!Element}
848 */
849 _createRemoveButton: function(handler)
850 {
851 var removeButton = document.createElement("button");
852 removeButton.classList.add("remove-item-button");
853 removeButton.value = WebInspector.UIString("Remove");
854 removeButton.addEventListener("click", handler, false);
855 return removeButton;
856 },
857
858 __proto__: WebInspector.Object.prototype
859 }
860
861 /**
862 * @constructor
863 * @extends {WebInspector.SettingsList}
864 * @param {function(?string, !Object)} validateHandler
865 * @param {function(?string, !Object)} editHandler
866 */
867 WebInspector.EditableSettingsList = function(columns, valuesProvider, validateHa ndler, editHandler)
868 {
869 WebInspector.SettingsList.call(this, columns, this._renderColumn.bind(this)) ;
870 this._validateHandler = validateHandler;
871 this._editHandler = editHandler;
872 this._valuesProvider = valuesProvider;
873 /** @type {!Object.<string, !HTMLInputElement>} */
874 this._addInputElements = {};
875 /** @type {!Object.<string, !Object.<string, !HTMLInputElement>>} */
876 this._editInputElements = {};
877 /** @type {!Object.<string, !Object.<string, !HTMLSpanElement>>} */
878 this._textElements = {};
879
880 this._addMappingItem = this.addItem(null);
881 this._addMappingItem.classList.add("item-editing");
882 this._addMappingItem.classList.add("add-list-item");
883 }
884
885 WebInspector.EditableSettingsList.prototype = {
886 /**
887 * @param {?string} itemId
888 * @param {?string=} beforeId
889 * @return {!Element}
890 */
891 addItem: function(itemId, beforeId)
892 {
893 var listItem = WebInspector.SettingsList.prototype.addItem.call(this, it emId, beforeId);
894 listItem.classList.add("editable");
895 return listItem;
896 },
897
898 /**
899 * @param {!Element} columnElement
900 * @param {string} columnId
901 * @param {?string} itemId
902 */
903 _renderColumn: function(columnElement, columnId, itemId)
904 {
905 columnElement.classList.add("settings-list-column-" + columnId);
906 var placeholder = (columnId === "url") ? WebInspector.UIString("URL pref ix") : WebInspector.UIString("Folder path");
907 if (itemId === null) {
908 var inputElement = columnElement.createChild("input", "list-column-e ditor");
909 inputElement.placeholder = placeholder;
910 inputElement.addEventListener("blur", this._onAddMappingInputBlur.bi nd(this));
911 inputElement.addEventListener("input", this._validateEdit.bind(this, itemId));
912 this._addInputElements[columnId] = inputElement;
913 return;
914 }
915 var validItemId = itemId;
916
917 if (!this._editInputElements[itemId])
918 this._editInputElements[itemId] = {};
919 if (!this._textElements[itemId])
920 this._textElements[itemId] = {};
921
922 var value = this._valuesProvider(itemId, columnId);
923
924 var textElement = columnElement.createChild("span", "list-column-text");
925 textElement.textContent = value;
926 textElement.title = value;
927 columnElement.addEventListener("click", rowClicked.bind(this), false);
928 this._textElements[itemId][columnId] = textElement;
929
930 var inputElement = columnElement.createChild("input", "list-column-edito r");
931 inputElement.value = value;
932 inputElement.addEventListener("blur", this._editMappingBlur.bind(this, i temId));
933 inputElement.addEventListener("input", this._validateEdit.bind(this, ite mId));
934 columnElement.inputElement = inputElement;
935 this._editInputElements[itemId][columnId] = inputElement;
936
937 /**
938 * @param {?Event} event
939 * @this {WebInspector.EditableSettingsList}
940 */
941 function rowClicked(event)
942 {
943 if (itemId === this._editingId)
944 return;
945 event.consume();
946 console.assert(!this._editingId);
947 this._editingId = validItemId;
948 var listItem = this.itemForId(validItemId);
949 listItem.classList.add("item-editing");
950 var inputElement = event.target.inputElement || this._editInputEleme nts[validItemId][this.columns()[0]];
951 inputElement.focus();
952 inputElement.select();
953 }
954 },
955
956 /**
957 * @param {?string} itemId
958 * @return {!Object}
959 */
960 _data: function(itemId)
961 {
962 var inputElements = this._inputElements(itemId);
963 var data = {};
964 var columns = this.columns();
965 for (var i = 0; i < columns.length; ++i)
966 data[columns[i]] = inputElements[columns[i]].value;
967 return data;
968 },
969
970 /**
971 * @param {?string} itemId
972 * @return {?Object.<string, !HTMLInputElement>}
973 */
974 _inputElements: function(itemId)
975 {
976 if (!itemId)
977 return this._addInputElements;
978 return this._editInputElements[itemId] || null;
979 },
980
981 /**
982 * @param {?string} itemId
983 * @return {boolean}
984 */
985 _validateEdit: function(itemId)
986 {
987 var errorColumns = this._validateHandler(itemId, this._data(itemId));
988 var hasChanges = this._hasChanges(itemId);
989 var columns = this.columns();
990 for (var i = 0; i < columns.length; ++i) {
991 var columnId = columns[i];
992 var inputElement = this._inputElements(itemId)[columnId];
993 if (hasChanges && errorColumns.indexOf(columnId) !== -1)
994 inputElement.classList.add("editable-item-error");
995 else
996 inputElement.classList.remove("editable-item-error");
997 }
998 return !errorColumns.length;
999 },
1000
1001 /**
1002 * @param {?string} itemId
1003 * @return {boolean}
1004 */
1005 _hasChanges: function(itemId)
1006 {
1007 var hasChanges = false;
1008 var columns = this.columns();
1009 for (var i = 0; i < columns.length; ++i) {
1010 var columnId = columns[i];
1011 var oldValue = itemId ? this._textElements[itemId][columnId].textCon tent : "";
1012 var newValue = this._inputElements(itemId)[columnId].value;
1013 if (oldValue !== newValue) {
1014 hasChanges = true;
1015 break;
1016 }
1017 }
1018 return hasChanges;
1019 },
1020
1021 /**
1022 * @param {string} itemId
1023 */
1024 _editMappingBlur: function(itemId, event)
1025 {
1026 var inputElements = Object.values(this._editInputElements[itemId]);
1027 if (inputElements.indexOf(event.relatedTarget) !== -1)
1028 return;
1029
1030 var listItem = this.itemForId(itemId);
1031 listItem.classList.remove("item-editing");
1032 delete this._editingId;
1033
1034 if (!this._hasChanges(itemId))
1035 return;
1036
1037 if (!this._validateEdit(itemId)) {
1038 var columns = this.columns();
1039 for (var i = 0; i < columns.length; ++i) {
1040 var columnId = columns[i];
1041 var inputElement = this._editInputElements[itemId][columnId];
1042 inputElement.value = this._textElements[itemId][columnId].textCo ntent;
1043 inputElement.classList.remove("editable-item-error");
1044 }
1045 return;
1046 }
1047 this._editHandler(itemId, this._data(itemId));
1048 },
1049
1050 _onAddMappingInputBlur: function(event)
1051 {
1052 var inputElements = Object.values(this._addInputElements);
1053 if (inputElements.indexOf(event.relatedTarget) !== -1)
1054 return;
1055
1056 if (!this._hasChanges(null))
1057 return;
1058
1059 if (!this._validateEdit(null))
1060 return;
1061
1062 this._editHandler(null, this._data(null));
1063 var columns = this.columns();
1064 for (var i = 0; i < columns.length; ++i) {
1065 var columnId = columns[i];
1066 var inputElement = this._addInputElements[columnId];
1067 inputElement.value = "";
1068 }
1069 },
1070
1071 __proto__: WebInspector.SettingsList.prototype
1072 }
1073
1074 /** @type {!WebInspector.SettingsController} */
1075 WebInspector.settingsController;
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698