| Index: appengine/swarming/elements/build/elements.html
|
| diff --git a/appengine/swarming/elements/build/elements.html b/appengine/swarming/elements/build/elements.html
|
| index 553de914fc68e5c3da1c13716d8efdeca67b3554..c741b96f7fd0b716d6c41169e9cb96940fe95f3e 100644
|
| --- a/appengine/swarming/elements/build/elements.html
|
| +++ b/appengine/swarming/elements/build/elements.html
|
| @@ -14632,7 +14632,30 @@ You can bind to `isAuthorized` property to monitor authorization state.
|
| color: #1F78B4;
|
| }
|
| </style>
|
| -</dom-module><dom-module id="swarming-index" assetpath="/res/imp/index/">
|
| +</dom-module>
|
| +
|
| +<script>
|
| + window.SwarmingBehaviors = window.SwarmingBehaviors || {};
|
| + (function(){
|
| + // This behavior wraps up all the shared swarming functionality.
|
| + SwarmingBehaviors.SwarmingBehavior = {
|
| +
|
| + _not: function(a) {
|
| + return !a;
|
| + },
|
| +
|
| + _or: function() {
|
| + var result = false;
|
| + // can't use .foreach, as arguments isn't really a function.
|
| + for (var i = 0; i < arguments.length; i++) {
|
| + result = result || arguments[i];
|
| + }
|
| + return result;
|
| + },
|
| + };
|
| + })();
|
| +</script>
|
| +<dom-module id="swarming-index" assetpath="/res/imp/index/">
|
| <template>
|
| <style include="swarming-app-style">
|
|
|
| @@ -14675,9 +14698,167 @@ You can bind to `isAuthorized` property to monitor authorization state.
|
|
|
| });
|
| </script>
|
| +</dom-module><dom-module id="dynamic-table-style" assetpath="/res/imp/common/">
|
| + <template>
|
| + <style>
|
| + table {
|
| + border-collapse: collapse;
|
| + margin-left: 5px;
|
| + }
|
| + td, th {
|
| + border: 1px solid #DDD;
|
| + padding: 5px;
|
| + }
|
| + th {
|
| + position: relative;
|
| + }
|
| + sort-toggle {
|
| + position: absolute;
|
| + right: 0;
|
| + top: 0.4em;
|
| + }
|
| + </style>
|
| +
|
| + </template>
|
| </dom-module>
|
|
|
| <script>
|
| + window.SwarmingBehaviors = window.SwarmingBehaviors || {};
|
| + (function(){
|
| + // This behavior wraps up all the shared swarming functionality.
|
| + SwarmingBehaviors.DynamicTableBehavior = {
|
| +
|
| + properties: {
|
| +
|
| + _columns: {
|
| + type: Array,
|
| + },
|
| +
|
| + _filter: {
|
| + type: Function,
|
| + },
|
| +
|
| + _filteredSortedItems: {
|
| + type: Array,
|
| + computed: "_filterAndSort(_items,_filter.*,_sort.*)"
|
| + },
|
| +
|
| + _items: {
|
| + type: Array,
|
| + },
|
| +
|
| + _plainColumns: {
|
| + type: Array,
|
| + computed: "_stripSpecial(_columns.*)",
|
| + },
|
| +
|
| + // _sort is an Object {name:String, direction:String}.
|
| + _sort: {
|
| + type: Object,
|
| + computed: "_makeSortObject(_sortstr)",
|
| + },
|
| +
|
| + _sortstr: {
|
| + type: String,
|
| + },
|
| +
|
| + _verbose: {
|
| + type: Boolean,
|
| + }
|
| + },
|
| +
|
| + _column: function(col, key) {
|
| + var f = this._columnMap[col];
|
| + if (!f) {
|
| + f = function(key) {
|
| + var c = this._attribute(key, col, "none");
|
| + if (this._verbose) {
|
| + return c.join(" | ");
|
| + }
|
| + return c[0];
|
| + }
|
| + }
|
| + return f.bind(this)(key);
|
| + },
|
| +
|
| + _compare: function(a, b) {
|
| + if (!this._sort) {
|
| + return 0;
|
| + }
|
| + var dir = 1;
|
| + if (this._sort.direction === "desc") {
|
| + dir = -1;
|
| + }
|
| + var sort = this._specialSort[this._sort.name];
|
| + if (sort) {
|
| + return sort.bind(this)(dir, a, b);
|
| + }
|
| + // Default to a natural compare of the columns.
|
| + var aCol = this._column(this._sort.name, a);
|
| + var bCol = this._column(this._sort.name, b);
|
| +
|
| + return dir * swarming.naturalCompare(aCol, bCol);
|
| + },
|
| +
|
| + _filterAndSort: function() {
|
| + // We intentionally sort this._items (and not a copy) to allow users to
|
| + // "chain" sorts, that is, sort by one thing and then another, and
|
| + // have both orderings properly impact the list.
|
| + swarming.stableSort(this._items, this._compare.bind(this));
|
| + var items = this._items;
|
| + if (this._filter) {
|
| + items = items.filter(this._filter.bind(this));
|
| + }
|
| +
|
| + return items;
|
| + },
|
| +
|
| + _header: function(col){
|
| + return this._headerMap[col] || col;
|
| + },
|
| +
|
| + _hide: function(col) {
|
| + return this._columns.indexOf(col) === -1;
|
| + },
|
| +
|
| + _makeSortObject: function(sortstr){
|
| + if (!sortstr) {
|
| + return undefined;
|
| + }
|
| + var pieces = sortstr.split(":");
|
| + if (pieces.length != 2) {
|
| + // fail safe
|
| + return {name: "id", direction: "asc"};
|
| + }
|
| + return {
|
| + name: pieces[0],
|
| + direction: pieces[1],
|
| + }
|
| + },
|
| +
|
| + _sortChange: function(e) {
|
| + // The event we get from sort-toggle tells us the name of what needs
|
| + // to be sorting and how to sort it.
|
| + if (!(e && e.detail && e.detail.name)) {
|
| + return;
|
| + }
|
| + // should trigger the computation of _sort and __filterAndSort
|
| + this.set("_sortstr", e.detail.name +":"+e.detail.direction);
|
| + },
|
| + // _stripSpecial removes the special columns and sorts the remaining
|
| + // columns so they always appear in the same order, regardless of
|
| + // the order they are added.
|
| + _stripSpecial: function(){
|
| + return this._columns.filter(function(c){
|
| + return this._specialColumns.indexOf(c) === -1;
|
| + }.bind(this)).sort();
|
| + },
|
| +
|
| + };
|
| + })();
|
| +</script>
|
| +
|
| +<script>
|
|
|
| (function() {
|
|
|
| @@ -22685,8 +22866,6 @@ is separate from validation, and `allowed-pattern` does not affect how the input
|
| });
|
| </script>
|
| <script>
|
| -
|
| - window.SwarmingBehaviors = window.SwarmingBehaviors || {};
|
| (function(){
|
| var ANDROID_ALIASES = {
|
| "bullhead": "Nexus 5X",
|
| @@ -22742,8 +22921,9 @@ is separate from validation, and `allowed-pattern` does not affect how the input
|
| // This regex matches a string like "ALIAS (ORIG)", with ORIG as group 1.
|
| var ALIAS_REGEXP = /.+ \((.*)\)/;
|
|
|
| - // This behavior wraps up all the shared bot-list functionality.
|
| - SwarmingBehaviors.BotListBehavior = {
|
| + // This behavior wraps up all the shared bot-list functionality by
|
| + // extending SwarmingBehaviors.SwarmingBehavior
|
| + SwarmingBehaviors.BotListBehavior = [SwarmingBehaviors.SwarmingBehavior, {
|
|
|
| properties: {
|
| DIMENSIONS_WITH_ALIASES: {
|
| @@ -22818,19 +22998,6 @@ is separate from validation, and `allowed-pattern` does not affect how the input
|
| return GPU_ALIASES[gpu] || UNKNOWN;
|
| },
|
|
|
| - _not: function(a) {
|
| - return !a;
|
| - },
|
| -
|
| - _or: function() {
|
| - var result = false;
|
| - // can't use .foreach, as arguments isn't really a function.
|
| - for (var i = 0; i < arguments.length; i++) {
|
| - result = result || arguments[i];
|
| - }
|
| - return result;
|
| - },
|
| -
|
| // _state returns the requested attribute from a bot's state.
|
| // For consistency with _dimension, if the attribute is not an array,
|
| // it is put as the only element in an array.
|
| @@ -22861,7 +23028,7 @@ is separate from validation, and `allowed-pattern` does not affect how the input
|
| }
|
| return str;
|
| },
|
| - }
|
| + }];
|
| })()
|
| </script>
|
| <dom-module id="bot-filters" assetpath="/res/imp/botlist/">
|
| @@ -23806,41 +23973,17 @@ the fleet.">
|
| </script>
|
| </dom-module><dom-module id="bot-list" assetpath="/res/imp/botlist/">
|
| <template>
|
| - <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-style">
|
| + <style include="iron-flex iron-flex-alignment iron-positioning swarming-app-style dynamic-table-style">
|
| bot-filters, bot-list-summary {
|
| margin-bottom: 8px;
|
| margin-right: 10px;
|
| }
|
| - .bot {
|
| - margin:5px;
|
| - max-width:400px;
|
| - min-height:100px;
|
| - min-width:300px;
|
| - }
|
| - table {
|
| - border-collapse: collapse;
|
| - margin-left: 5px;
|
| - }
|
| - td, th {
|
| - border: 1px solid #DDD;
|
| - padding: 5px;
|
| - }
|
| -
|
| .quarantined, .bad-device {
|
| background-color: #ffdddd;
|
| }
|
| .dead {
|
| background-color: #cccccc;
|
| }
|
| -
|
| - th {
|
| - position: relative;
|
| - }
|
| - sort-toggle {
|
| - position: absolute;
|
| - right: 0;
|
| - top: 0.4em;
|
| - }
|
| .bot-list th > span {
|
| /* Leave space for sort-toggle*/
|
| padding-right: 30px;
|
| @@ -23861,12 +24004,12 @@ the fleet.">
|
| <bot-filters dimensions="[[_dimensions]]" primary_map="[[_primary_map]]" primary_arr="[[_primary_arr]]" columns="{{_columns}}" query_params="{{_query_params}}" filter="{{_filter}}" verbose="{{_verbose}}">
|
| </bot-filters>
|
|
|
| - <bot-list-summary columns="[[_columns]]" fleet="[[_fleet]]" filtered_bots="[[_filteredSortedBots]]" sort="[[_sortstr]]" verbose="[[_verbose]]">
|
| + <bot-list-summary columns="[[_columns]]" fleet="[[_fleet]]" filtered_bots="[[_filteredSortedItems]]" sort="[[_sortstr]]" verbose="[[_verbose]]">
|
| </bot-list-summary>
|
|
|
| </div>
|
|
|
| - <bot-list-data auth_headers="[[_auth_headers]]" query_params="[[_query_params]]" bots="{{_bots}}" busy="{{_busy}}" dimensions="{{_dimensions}}" fleet="{{_fleet}}" primary_map="{{_primary_map}}" primary_arr="{{_primary_arr}}">
|
| + <bot-list-data auth_headers="[[_auth_headers]]" query_params="[[_query_params]]" bots="{{_items}}" busy="{{_busy}}" dimensions="{{_dimensions}}" fleet="{{_fleet}}" primary_map="{{_primary_map}}" primary_arr="{{_primary_arr}}">
|
| </bot-list-data>
|
|
|
| <table class="bot-list">
|
| @@ -23885,7 +24028,7 @@ the fleet.">
|
| </sort-toggle>
|
| </th>
|
|
|
| - <template is="dom-repeat" items="[[_plain_columns]]" as="c">
|
| + <template is="dom-repeat" items="[[_plainColumns]]" as="c">
|
| <th hidden$="[[_hide(c)]]">
|
| <span>[[_header(c)]]</span>
|
| <sort-toggle name="[[c]]" current="[[_sort]]">
|
| @@ -23895,7 +24038,7 @@ the fleet.">
|
| </tr>
|
| </thead>
|
| <tbody>
|
| - <template id="bot_table" is="dom-repeat" items="[[_filteredSortedBots]]" as="bot" initial-count="50">
|
| + <template id="bot_table" is="dom-repeat" items="[[_filteredSortedItems]]" as="bot" initial-count="50">
|
|
|
| <tr class$="[[_botClass(bot)]]">
|
| <td>
|
| @@ -23907,7 +24050,7 @@ the fleet.">
|
| <a href$="[[_taskLink(bot)]]">[[_taskId(bot)]]</a>
|
| </td>
|
|
|
| - <template is="dom-repeat" items="[[_plain_columns]]" as="c">
|
| + <template is="dom-repeat" items="[[_plainColumns]]" as="c">
|
| <td hidden$="[[_hide(c)]]">
|
| [[_column(c, bot, _verbose)]]
|
| </td>
|
| @@ -23918,7 +24061,7 @@ the fleet.">
|
| <tr hidden$="[[_hide('android_devices', _columns.*)]]" class$="[[_deviceClass(device)]]">
|
| <td></td>
|
| <td hidden$="[[_hide('task', _columns.*)]]"></td>
|
| - <template is="dom-repeat" items="[[_plain_columns]]" as="c">
|
| + <template is="dom-repeat" items="[[_plainColumns]]" as="c">
|
| <td hidden$="[[_hide(c)]]">
|
| [[_deviceColumn(c, device, _verbose)]]
|
| </td>
|
| @@ -23935,11 +24078,13 @@ the fleet.">
|
| </template>
|
| <script>
|
| (function(){
|
| - var special_columns = ["id", "task"];
|
| + // see dynamic-table for more information on specialColumns, headerMap,
|
| + // columnMap, and specialSort
|
| + var specialColumns = ["id", "task"];
|
|
|
| var headerMap = {
|
| - // "id" and "task" are special, so they don't go here and have their
|
| - // headers hard-coded below.
|
| + // "id" and "task" are special, so they don't go here. They have their
|
| + // headers hard-coded above.
|
| "android_devices": "Android Devices",
|
| "cores": "Cores",
|
| "cpu": "CPU",
|
| @@ -23954,10 +24099,6 @@ the fleet.">
|
| "xcode_version": "XCode Version",
|
| };
|
|
|
| - // This maps column name to a function that will return the content for a
|
| - // given bot. These functions are bound to this element, and have access
|
| - // to all functions defined here and in bot-list-shared. If a column
|
| - // is not listed here, a sane default will be used (see _column()).
|
| var columnMap = {
|
| android_devices: function(bot) {
|
| var devs = this._attribute(bot, "android_devices", "0");
|
| @@ -24057,8 +24198,6 @@ the fleet.">
|
| }
|
| }
|
|
|
| - // specialSort defines any custom sorting rules. By default, a
|
| - // naturalCompare of the column content is done.
|
| var specialSort = {
|
| android_devices: function(dir, botA, botB) {
|
| // We sort on the number of attached devices. Note that this
|
| @@ -24079,7 +24218,8 @@ the fleet.">
|
|
|
| Polymer({
|
| is: 'bot-list',
|
| - behaviors: [SwarmingBehaviors.BotListBehavior],
|
| + behaviors: [SwarmingBehaviors.BotListBehavior,
|
| + SwarmingBehaviors.DynamicTableBehavior],
|
|
|
| properties: {
|
|
|
| @@ -24087,40 +24227,34 @@ the fleet.">
|
| type: String,
|
| },
|
|
|
| - _bots: {
|
| - type: Array,
|
| - },
|
| -
|
| - _columns: {
|
| - type: Array,
|
| + // for dynamic table
|
| + _columnMap: {
|
| + type: Object,
|
| + value: function() {
|
| + return columnMap;
|
| + }
|
| },
|
| -
|
| - _filter: {
|
| - type: Function,
|
| + _headerMap: {
|
| + type: Object,
|
| value: function() {
|
| - return true;
|
| + return headerMap;
|
| },
|
| },
|
| -
|
| - _filteredSortedBots: {
|
| + // special columns contain html. non-special (i.e. normal colunns) just
|
| + // contain text.
|
| + _specialColumns: {
|
| type: Array,
|
| - computed: "_filterAndSort(_bots,_filter.*,_sort.*)"
|
| + value: function() {
|
| + return specialColumns;
|
| + }
|
| },
|
| -
|
| - _plain_columns: {
|
| - type: Array,
|
| - computed: "_stripSpecial(_columns.*)",
|
| - },
|
| -
|
| - // _sort is an Object {name:String, direction:String}.
|
| - _sort: {
|
| + _specialSort: {
|
| type: Object,
|
| - computed: "_makeObject(_sortstr)",
|
| + value: function() {
|
| + return specialSort;
|
| + }
|
| },
|
|
|
| - _verbose: {
|
| - type: Boolean,
|
| - }
|
| },
|
|
|
| _botClass: function(bot) {
|
| @@ -24139,20 +24273,6 @@ the fleet.">
|
| },
|
|
|
|
|
| - _column: function(col, bot) {
|
| - var f = columnMap[col];
|
| - if (!f) {
|
| - f = function(bot) {
|
| - var c = this._attribute(bot, col, "none");
|
| - if (this._verbose) {
|
| - return c.join(" | ");
|
| - }
|
| - return c[0];
|
| - }
|
| - }
|
| - return f.bind(this)(bot);
|
| - },
|
| -
|
| _androidAliasDevice: function(device) {
|
| if (device.notReady) {
|
| return UNAUTHENTICATED.toUpperCase();
|
| @@ -24175,84 +24295,6 @@ the fleet.">
|
| return "";
|
| },
|
|
|
| - _filterAndSort: function(a,b,c) {
|
| - // We intentionally sort this._bots (and not a copy) to allow users to
|
| - // "chain" sorts, that is, sort by one thing and then another, and
|
| - // have both orderings properly impact the list.
|
| - swarming.stableSort(this._bots, this._sortBotTable.bind(this));
|
| - var bots = this._bots;
|
| - if (this._filter) {
|
| - bots = bots.filter(this._filter.bind(this));
|
| - }
|
| -
|
| - return bots;
|
| - },
|
| -
|
| - _header: function(col){
|
| - return headerMap[col] || col;
|
| - },
|
| -
|
| - _hide: function(col) {
|
| - return this._columns.indexOf(col) === -1;
|
| - },
|
| -
|
| - _makeObject: function(sortstr){
|
| - if (!sortstr) {
|
| - return undefined;
|
| - }
|
| - var pieces = sortstr.split(":");
|
| - if (pieces.length != 2) {
|
| - // fail safe
|
| - return {name: "id", direction:"desc"};
|
| - }
|
| - return {
|
| - name: pieces[0],
|
| - direction: pieces[1],
|
| - }
|
| - },
|
| -
|
| - _reRender: function(filter, sort) {
|
| - this.$.bot_table.render();
|
| - },
|
| -
|
| - _sortBotTable: function(botA, botB) {
|
| - if (!this._sort) {
|
| - return 0;
|
| - }
|
| - var dir = 1;
|
| - if (this._sort.direction === "desc") {
|
| - dir = -1;
|
| - }
|
| - var sort = specialSort[this._sort.name];
|
| - if (sort) {
|
| - return sort.bind(this)(dir, botA, botB);
|
| - }
|
| - // Default to a natural compare of the columns.
|
| - var botACol = this._column(this._sort.name, botA);
|
| - var botBCol = this._column(this._sort.name, botB);
|
| -
|
| - return dir * swarming.naturalCompare(botACol, botBCol);
|
| - },
|
| -
|
| - _sortChange: function(e) {
|
| - // The event we get from sort-toggle tells us the name of what needs
|
| - // to be sorting and how to sort it.
|
| - if (!(e && e.detail && e.detail.name)) {
|
| - return;
|
| - }
|
| - // should trigger the computation of _sort and __filterAndSort
|
| - this.set("_sortstr", e.detail.name +":"+e.detail.direction);
|
| - },
|
| -
|
| - // _stripSpecial removes the special columns and sorts the remaining
|
| - // columns so they always appear in the same order, regardless of
|
| - // the order they are added.
|
| - _stripSpecial: function(){
|
| - return this._columns.filter(function(c){
|
| - return special_columns.indexOf(c) === -1;
|
| - }).sort();
|
| - },
|
| -
|
| _taskLink: function(data) {
|
| if (data && data.task_id) {
|
| return "/user/task/" + data.task_id;
|
|
|