1<!DOCTYPE html> 2<!-- 3Copyright (c) 2014 The Chromium Authors. All rights reserved. 4Use of this source code is governed by a BSD-style license that can be 5found in the LICENSE file. 6--> 7 8<link rel="import" href="/tracing/base/settings.html"> 9<link rel="import" href="/tracing/ui/base/ui.html"> 10 11<style> 12* /deep/ .labeled-checkbox { 13 display: flex; 14 white-space: nowrap; 15} 16</style> 17 18<script> 19'use strict'; 20 21tr.exportTo('tr.ui.b', function() { 22 23 function createSpan(opt_dictionary) { 24 var ownerDocument = document; 25 if (opt_dictionary && opt_dictionary.ownerDocument) 26 ownerDocument = opt_dictionary.ownerDocument; 27 var spanEl = ownerDocument.createElement('span'); 28 if (opt_dictionary) { 29 if (opt_dictionary.className) 30 spanEl.className = opt_dictionary.className; 31 if (opt_dictionary.textContent) 32 spanEl.textContent = opt_dictionary.textContent; 33 if (opt_dictionary.tooltip) 34 spanEl.title = opt_dictionary.tooltip; 35 if (opt_dictionary.parent) 36 opt_dictionary.parent.appendChild(spanEl); 37 if (opt_dictionary.bold) 38 spanEl.style.fontWeight = 'bold'; 39 if (opt_dictionary.italic) 40 spanEl.style.fontStyle = 'italic'; 41 if (opt_dictionary.marginLeft) 42 spanEl.style.marginLeft = opt_dictionary.marginLeft; 43 if (opt_dictionary.marginRight) 44 spanEl.style.marginRight = opt_dictionary.marginRight; 45 if (opt_dictionary.backgroundColor) 46 spanEl.style.backgroundColor = opt_dictionary.backgroundColor; 47 if (opt_dictionary.color) 48 spanEl.style.color = opt_dictionary.color; 49 } 50 return spanEl; 51 }; 52 53 function createDiv(opt_dictionary) { 54 var divEl = document.createElement('div'); 55 if (opt_dictionary) { 56 if (opt_dictionary.className) 57 divEl.className = opt_dictionary.className; 58 if (opt_dictionary.parent) 59 opt_dictionary.parent.appendChild(divEl); 60 if (opt_dictionary.textContent) 61 divEl.textContent = opt_dictionary.textContent; 62 if (opt_dictionary.maxWidth) 63 divEl.style.maxWidth = opt_dictionary.maxWidth; 64 } 65 return divEl; 66 }; 67 68 function createScopedStyle(styleContent) { 69 var styleEl = document.createElement('style'); 70 styleEl.scoped = true; 71 styleEl.innerHTML = styleContent; 72 return styleEl; 73 } 74 75 function valuesEqual(a, b) { 76 if (a instanceof Array && b instanceof Array) 77 return a.length === b.length && JSON.stringify(a) === JSON.stringify(b); 78 return a === b; 79 } 80 81 function createSelector( 82 targetEl, targetElProperty, 83 settingsKey, defaultValue, 84 items, opt_namespace) { 85 var defaultValueIndex; 86 for (var i = 0; i < items.length; i++) { 87 var item = items[i]; 88 if (valuesEqual(item.value, defaultValue)) { 89 defaultValueIndex = i; 90 break; 91 } 92 } 93 if (defaultValueIndex === undefined) 94 throw new Error('defaultValue must be in the items list'); 95 96 var selectorEl = document.createElement('select'); 97 selectorEl.addEventListener('change', onChange); 98 for (var i = 0; i < items.length; i++) { 99 var item = items[i]; 100 var optionEl = document.createElement('option'); 101 optionEl.textContent = item.label; 102 optionEl.targetPropertyValue = item.value; 103 optionEl.item = item; 104 selectorEl.appendChild(optionEl); 105 } 106 function onChange(e) { 107 var value = selectorEl.selectedOptions[0].targetPropertyValue; 108 tr.b.Settings.set(settingsKey, value, opt_namespace); 109 targetEl[targetElProperty] = value; 110 } 111 var oldSetter = targetEl.__lookupSetter__('selectedIndex'); 112 selectorEl.__defineGetter__('selectedValue', function(v) { 113 return selectorEl.children[selectorEl.selectedIndex].targetPropertyValue; 114 }); 115 selectorEl.__defineGetter__('selectedItem', function(v) { 116 return selectorEl.children[selectorEl.selectedIndex].item; 117 }); 118 selectorEl.__defineSetter__('selectedValue', function(v) { 119 for (var i = 0; i < selectorEl.children.length; i++) { 120 var value = selectorEl.children[i].targetPropertyValue; 121 if (valuesEqual(value, v)) { 122 var changed = selectorEl.selectedIndex != i; 123 if (changed) { 124 selectorEl.selectedIndex = i; 125 onChange(); 126 } 127 return; 128 } 129 } 130 throw new Error('Not a valid value'); 131 }); 132 133 var initialValue = tr.b.Settings.get( 134 settingsKey, defaultValue, opt_namespace); 135 var didSet = false; 136 for (var i = 0; i < selectorEl.children.length; i++) { 137 if (valuesEqual(selectorEl.children[i].targetPropertyValue, 138 initialValue)) { 139 didSet = true; 140 targetEl[targetElProperty] = initialValue; 141 selectorEl.selectedIndex = i; 142 break; 143 } 144 } 145 if (!didSet) { 146 selectorEl.selectedIndex = defaultValueIndex; 147 targetEl[targetElProperty] = defaultValue; 148 } 149 150 return selectorEl; 151 } 152 153 function createEditCategorySpan(optionGroupEl, targetEl) { 154 var spanEl = createSpan({className: 'edit-categories'}); 155 spanEl.textContent = 'Edit categories'; 156 spanEl.classList.add('labeled-option'); 157 158 spanEl.addEventListener('click', function() { 159 targetEl.onClickEditCategories(); 160 }); 161 return spanEl; 162 } 163 164 function createOptionGroup(targetEl, targetElProperty, 165 settingsKey, defaultValue, 166 items) { 167 function onChange() { 168 var value = []; 169 if (this.value.length) 170 value = this.value.split(','); 171 tr.b.Settings.set(settingsKey, value); 172 targetEl[targetElProperty] = value; 173 } 174 175 var optionGroupEl = createSpan({className: 'labeled-option-group'}); 176 var initialValue = tr.b.Settings.get(settingsKey, defaultValue); 177 for (var i = 0; i < items.length; ++i) { 178 var item = items[i]; 179 var id = 'category-preset-' + item.label.replace(/ /g, '-'); 180 181 var radioEl = document.createElement('input'); 182 radioEl.type = 'radio'; 183 radioEl.setAttribute('id', id); 184 radioEl.setAttribute('name', 'category-presets-group'); 185 radioEl.setAttribute('value', item.value); 186 radioEl.addEventListener('change', onChange.bind(radioEl, targetEl, 187 targetElProperty, 188 settingsKey)); 189 if (valuesEqual(initialValue, item.value)) 190 radioEl.checked = true; 191 192 var labelEl = document.createElement('label'); 193 labelEl.textContent = item.label; 194 labelEl.setAttribute('for', id); 195 196 var spanEl = createSpan({className: 'labeled-option'}); 197 spanEl.appendChild(radioEl); 198 spanEl.appendChild(labelEl); 199 200 spanEl.__defineSetter__('checked', function(opt_bool) { 201 var changed = radioEl.checked !== (!!opt_bool); 202 if (!changed) 203 return; 204 205 radioEl.checked = !!opt_bool; 206 onChange(); 207 }); 208 spanEl.__defineGetter__('checked', function() { 209 return radioEl.checked; 210 }); 211 212 optionGroupEl.appendChild(spanEl); 213 } 214 optionGroupEl.appendChild(createEditCategorySpan(optionGroupEl, targetEl)); 215 // Since this option group element is not yet added to the tree, 216 // querySelector will fail during updateEditCategoriesStatus_ call. 217 // Hence, creating the element with the 'expanded' classlist category 218 // added, if last selected value was 'Manual' selection. 219 if (!initialValue.length) 220 optionGroupEl.classList.add('categories-expanded'); 221 targetEl[targetElProperty] = initialValue; 222 223 return optionGroupEl; 224 } 225 226 var nextCheckboxId = 1; 227 function createCheckBox(targetEl, targetElProperty, 228 settingsKey, defaultValue, 229 label, opt_changeCb) { 230 var buttonEl = document.createElement('input'); 231 buttonEl.type = 'checkbox'; 232 233 var initialValue = tr.b.Settings.get(settingsKey, defaultValue); 234 buttonEl.checked = !!initialValue; 235 if (targetEl) 236 targetEl[targetElProperty] = initialValue; 237 238 function onChange() { 239 tr.b.Settings.set(settingsKey, buttonEl.checked); 240 if (targetEl) 241 targetEl[targetElProperty] = buttonEl.checked; 242 if (opt_changeCb) 243 opt_changeCb.call(); 244 } 245 246 buttonEl.addEventListener('change', onChange); 247 248 var id = '#checkbox-' + nextCheckboxId++; 249 250 var spanEl = createSpan({className: 'labeled-checkbox'}); 251 buttonEl.setAttribute('id', id); 252 253 var labelEl = document.createElement('label'); 254 labelEl.textContent = label; 255 labelEl.setAttribute('for', id); 256 spanEl.appendChild(buttonEl); 257 spanEl.appendChild(labelEl); 258 259 spanEl.__defineSetter__('checked', function(opt_bool) { 260 var changed = buttonEl.checked !== (!!opt_bool); 261 if (!changed) 262 return; 263 264 buttonEl.checked = !!opt_bool; 265 onChange(); 266 }); 267 spanEl.__defineGetter__('checked', function() { 268 return buttonEl.checked; 269 }); 270 271 return spanEl; 272 } 273 274 function createButton(targetEl, targetElProperty, label, opt_changeCb) { 275 var buttonEl = document.createElement('input'); 276 buttonEl.type = 'button'; 277 278 function onClick() { 279 if (opt_changeCb) 280 opt_changeCb.call(); 281 } 282 283 buttonEl.addEventListener('click', onClick); 284 buttonEl.value = label; 285 286 return buttonEl; 287 } 288 289 function createTextInput( 290 targetEl, targetElProperty, settingsKey, defaultValue) { 291 var initialValue = tr.b.Settings.get(settingsKey, defaultValue); 292 var el = document.createElement('input'); 293 el.type = 'text'; 294 function onChange(e) { 295 tr.b.Settings.set(settingsKey, el.value); 296 targetEl[targetElProperty] = el.value; 297 } 298 el.addEventListener('input', onChange); 299 el.value = initialValue; 300 targetEl[targetElProperty] = initialValue; 301 302 return el; 303 } 304 305 function isElementAttachedToDocument(el) { 306 var cur = el; 307 while (cur.parentNode) 308 cur = cur.parentNode; 309 return (cur === el.ownerDocument || cur.nodeName === '#document-fragment'); 310 } 311 312 function asHTMLOrTextNode(value, opt_ownerDocument) { 313 if (value instanceof Node) 314 return value; 315 var ownerDocument = opt_ownerDocument || document; 316 return ownerDocument.createTextNode(value); 317 } 318 319 return { 320 createSpan: createSpan, 321 createDiv: createDiv, 322 createScopedStyle: createScopedStyle, 323 createSelector: createSelector, 324 createOptionGroup: createOptionGroup, 325 createCheckBox: createCheckBox, 326 createButton: createButton, 327 createTextInput: createTextInput, 328 isElementAttachedToDocument: isElementAttachedToDocument, 329 asHTMLOrTextNode: asHTMLOrTextNode 330 }; 331}); 332</script> 333