Home | History | Annotate | Download | only in base
      1 <!DOCTYPE html>
      2 <!--
      3 Copyright (c) 2014 The Chromium Authors. All rights reserved.
      4 Use of this source code is governed by a BSD-style license that can be
      5 found 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 
     21 tr.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