blob: c43eae0c1ea74c1a38e60d5438f41ab090bdb542 [file] [log] [blame]
<!--
The common.js file must be included before this file.
This in an HTML Import-able file that contains the definition
of the following elements:
<query-sk>
To use this file import it:
<link href="/res/imp/query.html" rel="import" />
Usage:
<query-sk></query-sk>
Properties:
currentquery - The current URL query formatted selections.
whitelist - A list of keys for params that should always be shown.
blacklist - A list of keys for params that should never be shown.
matches - A URL for reporting the number of items that match
the currentquery. If not the empty string then a request
will be made to:
matches + '?' + currentquery
The response must have the form of:
{
"matches": NNNN,
}
noclear - Don't display the clear selections button.
feedback - If set shows the current selections as string values.
Methods:
setParamSet(set) - Set the params to be displayed.
clearSelections() - Clear all selections.
setSelections(sel) - Set the current selections. Must be called after
setParamSet.
Events:
'query-change'
The 'sk-query' element will produce 'query-change' events when the query
parameters chosen have changed. The event contains the current
selections formatted as a URL query, found in e.detail.
-->
<link rel="import" href="/res/imp/bower_components/paper-input/paper-input.html">
<link rel="import" href="/res/common/imp/toggle.html">
<link rel="stylesheet" href="/res/common/css/md.css" type="text/css" media="all" />
<link rel="import" href="/res/imp/bower_components/iron-flex-layout/iron-flex-layout-classes.html">
<style type="text/css" media="screen">
#moreContainer.hide {
display: none;
}
</style>
<dom-module id="query-sk">
<style include="iron-flex iron-flex-alignment">
#moreContainer.hide {
display: none;
}
select {
border: solid 1px lightgray;
padding: 0.2em;
}
option {
padding: 0.2em;
}
.node {
margin: 0.5em;
}
.topControls {
min-width: 40em;
}
.currSelection {
padding-left: 2em;
min-width: 40em;
}
</style>
<template>
<div class="horizontal layout">
<div class="vertical layout topControls">
<div>
<button id="clear">Clear selections</button>
<span id="count"></span>
</div>
<div>
<paper-input id="fast" label="Filter"></paper-input>
</div>
</div>
<div hidden$="{{!feedback}}" class="currSelection">
<pre>{{_splitAmp(currentquery)}}</pre>
</div>
</div>
<div id=inputsContainer class="horizontal layout wrap">
<template is="dom-repeat" items="[[_primary]]" as="sel">
<div class="node">
<h4>[[sel.name]]</h4>
<select size=9 data-name$="[[sel.name]]" multiple=true initial-count=1000>
<template is="dom-repeat" items="[[sel.values]]" as="o">
<option label$="[[o]]">[[o]]</option>
</template>
</select>
</div>
</template>
</div>
<toggle-display-sk id=toggle classname=hide>More...</toggle-display-sk>
<div id=moreContainer class="horizontal layout wrap hide">
<template is="dom-repeat" items="[[_secondary]]" as="sel">
<div class="node">
<h4>[[sel.name]]</h4>
<select size=9 data-name$="[[sel.name]]" multiple=true>
<template is="dom-repeat" items="[[sel.values]]" as="o" initial-count=1000>
<option label$="[[o]]">[[o]]</option>
</template>
</select>
</div>
</template>
</div>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'query-sk',
properties: {
currentquery: {
type: String,
value: "",
},
matches: {
type: String,
value: '/query/0/-1/',
reflectToAttribute: true
},
noclear: {
type: Boolean,
value: false,
reflectToAttribute: true
},
whitelist: {
type: Array,
value: function() {
return [
'name',
'bench_type',
'os',
'source_type',
'scale',
'extra_config',
'config',
'arch',
'sub_result'
];
},
reflectToAttribute: true
},
blacklist: {
type: Array,
value: function() { return []; }
},
feedback: {
type: Boolean,
value: false,
notify: true
}
},
ready: function() {
// Both _primary and _secondary are arrays of objects that look like:
//
// {
// name: "config",
// values: ["565", "8888", "gpu"]
// }
//
// Where _primary contains selections that are in the whitelist, and
// _secondary contains all the other selections.
this._primary = [];
this._secondary = [];
this.$.inputsContainer.addEventListener('change', this._fireChange.bind(this));
this.$.moreContainer.addEventListener('change', this._fireChange.bind(this));
this.$.clear.addEventListener('click', this.clearSelections.bind(this));
if (this.noclear) {
this.$.clear.style.display = 'none';
}
this.$.fast.addEventListener('input', function (e) {
this.$.toggle.open();
this._fastFilter();
}.bind(this));
},
_splitAmp: sk.query.splitAmp,
_fastFilter: function() {
var filters = this.$.fast.value.trim().toLowerCase().split(/\s+/);
// Create a closure that returns true if the given label
// matches the filter.
var matches = function(s) {
s = s.toLowerCase();
for (var i = 0; i < filters.length; i++) {
if (s.indexOf(filters[i]) >= 0) {
return true;
}
}
return false;
};
$$('option', this).forEach(function (ele) {
ele.hidden = !matches(ele.getAttribute('label'));
});
},
_fireChange: function() {
var q = this._selectionsAsQuery();
// Only fire if we see a real change in the state.
if (q != this.currentquery) {
this.currentquery = q;
this.dispatchEvent(new CustomEvent('query-change', { detail: q, bubbles: true }));
var countEle = this.$.count;
if (this.matches) {
sk.get(this.matches + "?" + q).then(JSON.parse).then(function (json) {
countEle.innerHTML = json['matches'] + ' lines selected<br />';
});
}
}
},
// _selectionsAsQuery bundles up the current set of selections as a URL query
// suitable for passing to the /query/ endpoint.
//
_selectionsAsQuery: function () {
var sel = [];
$$('option', this).forEach(function (option) {
if (!option.selected) {
return;
}
var key = option.parentElement.dataset.name;
sel.push(encodeURIComponent(key) + '=' + encodeURIComponent(option.value));
});
return sel.join('&');
},
clearSelections: function() {
$$('option', this).forEach(function (elem) {
elem.selected = false;
});
this.$.fast.value = '';
this._fastFilter();
this._fireChange();
this.$.count.textContent = '';
},
// Selects all the values in paramset.
//
// The argument is a URL query encoded paramset.
setSelections: function (paramset) {
paramset = sk.query.toParamSet(paramset);
this.clearSelections();
$$('option', this).forEach(function (sel) {
var params = paramset[sel.parentElement.dataset.name];
if (params) {
sel.selected = params.indexOf(sel.value) !== -1;
}
});
this._fireChange();
},
// When paramset is changed we rebuild _primary and _secondary.
//
// The paramset is an object that maps selection names
// to a list of selection values, not necessarily in alphabetical
// order.
setParamSet: function (paramset) {
var keylist = Object.keys(paramset).sort();
this.splice('_primary', 0, this._primary.length);
this.splice('_secondary', 0, this._secondary.length);
for (var i = 0; i < keylist.length; i++) {
var key = keylist[i];
if ((this.blacklist.length > 0) && (this.blacklist.indexOf(key) != -1)) {
continue;
}
var sel = {
name: key,
values: paramset[key].sort()
};
if (this.whitelist.length == 0 || this.whitelist.indexOf(key) != -1) {
this.push('_primary', sel);
} else {
this.push('_secondary', sel);
}
}
this.$.toggle.hidden = (this._secondary.length == 0);
}
});
})();
</script>