1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5'use strict';
6
7/**
8 * @fileoverview RecordSelectionDialog presents the available categories
9 * to be enabled/disabled during tracing.
10 */
11base.requireStylesheet('tracing.record_selection_dialog');
12base.requireTemplate('tracing.record_selection_dialog');
13
14base.require('base.utils');
15base.require('tracing.filter');
16base.require('ui.overlay');
17
18base.exportTo('tracing', function() {
19  var RecordSelectionDialog = ui.define('div');
20
21  RecordSelectionDialog.prototype = {
22    __proto__: ui.Overlay.prototype,
23
24    decorate: function() {
25      ui.Overlay.prototype.decorate.call(this);
26
27      this.className = 'record-dialog-overlay';
28      this.obeyCloseEvents = true;
29
30      var node = base.instantiateTemplate('#record-selection-dialog-template');
31      this.appendChild(node);
32
33      this.formEl_ = this.querySelector('form');
34
35      this.recordButtonEl_ = this.querySelector('.record-categories');
36      this.recordButtonEl_.onclick = this.onRecord_.bind(this);
37
38      this.continuousTracingBn_ =
39          this.querySelector('.continuous-tracing-button');
40      this.continuousTracingBn_.onchange = this.updateDlgSetting_.bind(this);
41
42      this.systemTracingBn_ = this.querySelector('.system-tracing-button');
43      this.systemTracingBn_.onchange = this.updateDlgSetting_.bind(this);
44
45      this.samplingBn_ = this.querySelector('.sampling-button');
46      this.samplingBn_.onchange = this.updateDlgSetting_.bind(this);
47
48      this.systemTracingLabelEl_ = this.querySelector('.system-tracing-label');
49      this.systemTracingLabelEl_.style.display = 'none';
50
51      this.enabledCategoriesContainerEl_ =
52          this.querySelector('.default-enabled-categories .categories');
53
54      this.disabledCategoriesContainerEl_ =
55          this.querySelector('.default-disabled-categories .categories');
56
57      this.createGroupSelectButtons_(
58          this.querySelector('.default-enabled-categories'));
59      this.createGroupSelectButtons_(
60          this.querySelector('.default-disabled-categories'));
61
62      this.addEventListener('visibleChange', this.onVisibleChange_.bind(this));
63    },
64
65    updateDlgSetting_: function(e) {
66      var checkbox = e.target;
67      this.settings_.set(checkbox.value, checkbox.checked, 'record_dlg');
68    },
69
70    set categories(c) {
71      this.categories_ = c;
72    },
73
74    set settings_key(k) {
75      this.settings_key_ = k;
76    },
77
78    set settings(s) {
79      this.settings_ = s;
80
81      this.continuousTracingBn_.checked =
82          this.settings_.get('continuousTracing', true, 'record_dlg');
83      this.systemTracingBn_.checked =
84          this.settings_.get('systemTracing', false, 'record_dlg');
85      this.samplingBn_.checked =
86          this.settings_.get('enableSampling', false, 'record_dlg');
87    },
88
89    set recordCallback(cb) {
90      this.recordCallback_ = cb;
91    },
92
93    set showSystemTracing(isEnabled) {
94      if ((this.settings_ === undefined) ||
95          (this.settings_.get('systemTracing',
96                              undefined,
97                              'record_dlg') === undefined)) {
98        this.systemTracingBn_.checked = isEnabled;
99      }
100
101      this.systemTracingLabelEl_.style.display =
102          isEnabled ? 'inline-block' : 'none';
103    },
104
105    isContinuousTracingEnabled: function() {
106      return this.continuousTracingBn_.checked;
107    },
108
109    isSystemTracingEnabled: function() {
110      return this.systemTracingBn_.checked;
111    },
112
113    isSamplingEnabled: function() {
114      return this.samplingBn_.checked;
115    },
116
117    categoryFilter: function() {
118      var categories = this.unselectedCategories_();
119      var categories_length = categories.length;
120      var negated_categories = [];
121      for (var i = 0; i < categories_length; ++i) {
122        // Skip any category with a , as it will cause issues when we negate.
123        // Both sides should have been added as separate categories, these can
124        // only come from settings.
125        if (categories[i].match(/,/))
126          continue;
127        negated_categories.push('-' + categories[i]);
128      }
129      categories = negated_categories.join(',');
130
131      var disabledCategories = this.enabledDisabledByDefaultCategories_();
132      disabledCategories = disabledCategories.join(',');
133
134      var results = [];
135      if (categories !== '')
136        results.push(categories);
137      if (disabledCategories !== '')
138        results.push(disabledCategories);
139
140      return results.join(',');
141    },
142
143    onRecord_: function() {
144      this.visible = false;
145      this.recordCallback_();
146      return false;
147    },
148
149    collectInputs_: function(inputs, isChecked) {
150      var inputs_length = inputs.length;
151      var categories = [];
152      for (var i = 0; i < inputs_length; ++i) {
153        var input = inputs[i];
154        if (input.checked === isChecked)
155          categories.push(input.value);
156      }
157      return categories;
158    },
159
160    unselectedCategories_: function() {
161      var inputs =
162          this.enabledCategoriesContainerEl_.querySelectorAll('input');
163      return this.collectInputs_(inputs, false);
164    },
165
166    enabledDisabledByDefaultCategories_: function() {
167      var inputs =
168          this.disabledCategoriesContainerEl_.querySelectorAll('input');
169      return this.collectInputs_(inputs, true);
170    },
171
172    onVisibleChange_: function() {
173      if (this.visible) {
174        this.updateForm_();
175      }
176    },
177
178    buildInputs_: function(inputs, checkedDefault, parent) {
179      var inputs_length = inputs.length;
180      for (var i = 0; i < inputs_length; i++) {
181        var category = inputs[i];
182
183        var inputEl = document.createElement('input');
184        inputEl.type = 'checkbox';
185        inputEl.id = category;
186        inputEl.value = category;
187
188        inputEl.checked = this.settings_.get(
189            category, checkedDefault, this.settings_key_);
190        inputEl.onclick = this.updateSetting_.bind(this);
191
192        var labelEl = document.createElement('label');
193        labelEl.textContent = category.replace('disabled-by-default-', '');
194        labelEl.setAttribute('for', category);
195
196        var divEl = document.createElement('div');
197        divEl.appendChild(inputEl);
198        divEl.appendChild(labelEl);
199
200        parent.appendChild(divEl);
201      }
202    },
203
204    updateForm_: function() {
205      this.enabledCategoriesContainerEl_.innerHTML = ''; // Clear old categories
206      this.disabledCategoriesContainerEl_.innerHTML = '';
207
208      this.recordButtonEl_.focus();
209
210      // Dedup the categories. We may have things in settings that are also
211      // returned when we query the category list.
212      var set = {};
213      var allCategories =
214          this.categories_.concat(this.settings_.keys(this.settings_key_));
215      var allCategoriesLength = allCategories.length;
216      for (var i = 0; i < allCategoriesLength; ++i) {
217        set[allCategories[i]] = true;
218      }
219
220      var categories = [];
221      var disabledCategories = [];
222      for (var category in set) {
223        if (category.indexOf('disabled-by-default-') == 0)
224          disabledCategories.push(category);
225        else
226          categories.push(category);
227      }
228      disabledCategories = disabledCategories.sort();
229      categories = categories.sort();
230
231      this.buildInputs_(categories, true, this.enabledCategoriesContainerEl_);
232
233      if (disabledCategories.length > 0) {
234        this.disabledCategoriesContainerEl_.hidden = false;
235        this.buildInputs_(disabledCategories, false,
236            this.disabledCategoriesContainerEl_);
237      }
238    },
239
240    updateSetting_: function(e) {
241      var checkbox = e.target;
242      this.settings_.set(checkbox.value, checkbox.checked, this.settings_key_);
243    },
244
245    createGroupSelectButtons_: function(parent) {
246      var flipInputs = function(dir) {
247        var inputs = parent.querySelectorAll('input');
248        for (var i = 0; i < inputs.length; i++) {
249          if (inputs[i].checked === dir)
250            continue;
251          // click() is used so the settings will be correclty stored. Setting
252          // checked does not trigger the onclick (or onchange) callback.
253          inputs[i].click();
254        }
255      };
256
257      var allBtn = parent.querySelector('.all-btn');
258      allBtn.onclick = function(evt) {
259        flipInputs(true);
260        evt.preventDefault();
261      };
262
263      var noneBtn = parent.querySelector('.none-btn');
264      noneBtn.onclick = function(evt) {
265        flipInputs(false);
266        evt.preventDefault();
267      };
268    }
269  };
270
271  return {
272    RecordSelectionDialog: RecordSelectionDialog
273  };
274});
275