| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Javascript for DeviceTable UI, served from chrome://bluetooth-internals/. | 6 * Javascript for DeviceTable UI, served from chrome://bluetooth-internals/. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 cr.define('device_table', function() { | 9 cr.define('device_table', function() { |
| 10 var COLUMNS = { | 10 var COLUMNS = { |
| 11 NAME: 0, | 11 NAME: 0, |
| 12 ADDRESS: 1, | 12 ADDRESS: 1, |
| 13 RSSI: 2, | 13 RSSI: 2, |
| 14 SERVICES: 3, | 14 SERVICES: 3, |
| 15 CONNECTION_STATE: 4, | 15 CONNECTION_STATE: 4, |
| 16 INSPECT_LINK: 5, | 16 LINKS: 5, |
| 17 }; | 17 }; |
| 18 | 18 |
| 19 /** | 19 /** |
| 20 * A table that lists the devices and responds to changes in the given | 20 * A table that lists the devices and responds to changes in the given |
| 21 * DeviceCollection. Fires events for inspection requests from listed | 21 * DeviceCollection. Fires events for inspection requests from listed |
| 22 * devices. | 22 * devices. |
| 23 * @constructor | 23 * @constructor |
| 24 * @extends {HTMLTableElement} | 24 * @extends {HTMLTableElement} |
| 25 */ | 25 */ |
| 26 var DeviceTable = cr.ui.define(function() { | 26 var DeviceTable = cr.ui.define(function() { |
| 27 /** @private {?Array<device_collection.Device>} */ | 27 /** @private {?Array<device_collection.Device>} */ |
| 28 this.devices_ = null; | 28 this.devices_ = null; |
| 29 | 29 |
| 30 return document.importNode($('table-template').content.children[0], | 30 return document.importNode($('table-template').content.children[0], |
| 31 true /* deep */); | 31 true /* deep */); |
| 32 }); | 32 }); |
| 33 | 33 |
| 34 DeviceTable.prototype = { | 34 DeviceTable.prototype = { |
| 35 __proto__: HTMLTableElement.prototype, | 35 __proto__: HTMLTableElement.prototype, |
| 36 | 36 |
| 37 /** | 37 /** |
| 38 * Decorates an element as a UI element class. Caches references to the | 38 * Decorates an element as a UI element class. Caches references to the |
| 39 * table body and headers. | 39 * table body and headers. |
| 40 */ | 40 */ |
| 41 decorate: function() { | 41 decorate: function() { |
| 42 /** @private */ | 42 /** @private */ |
| 43 this.body_ = this.tBodies[0]; | 43 this.body_ = this.tBodies[0]; |
| 44 /** @private */ | 44 /** @private */ |
| 45 this.headers_ = this.tHead.rows[0].cells; | 45 this.headers_ = this.tHead.rows[0].cells; |
| 46 /** @private {!Map<!interfaces.BluetoothDevice.DeviceInfo, boolean>} */ |
| 47 this.inspectionMap_ = new Map(); |
| 46 }, | 48 }, |
| 47 | 49 |
| 48 /** | 50 /** |
| 49 * Sets the tables device collection. | 51 * Sets the tables device collection. |
| 50 * @param {!device_collection.DeviceCollection} deviceCollection | 52 * @param {!device_collection.DeviceCollection} deviceCollection |
| 51 */ | 53 */ |
| 52 setDevices: function(deviceCollection) { | 54 setDevices: function(deviceCollection) { |
| 53 assert(!this.devices_, 'Devices can only be set once.'); | 55 assert(!this.devices_, 'Devices can only be set once.'); |
| 54 | 56 |
| 55 this.devices_ = deviceCollection; | 57 this.devices_ = deviceCollection; |
| 56 this.devices_.addEventListener('sorted', this.redraw_.bind(this)); | 58 this.devices_.addEventListener('sorted', this.redraw_.bind(this)); |
| 57 this.devices_.addEventListener('change', this.handleChange_.bind(this)); | 59 this.devices_.addEventListener('change', this.handleChange_.bind(this)); |
| 58 this.devices_.addEventListener('splice', this.handleSplice_.bind(this)); | 60 this.devices_.addEventListener('splice', this.handleSplice_.bind(this)); |
| 59 | 61 |
| 60 this.redraw_(); | 62 this.redraw_(); |
| 61 }, | 63 }, |
| 62 | 64 |
| 63 /** | 65 /** |
| 66 * Updates the inspect status of the row matching the given |deviceInfo|. |
| 67 * If |isInspecting| is true, the forget link is enabled otherwise it's |
| 68 * disabled. |
| 69 * @param {!interfaces.BluetoothDevice.DeviceInfo} deviceInfo |
| 70 * @param {boolean} isInspecting |
| 71 */ |
| 72 setInspecting: function(deviceInfo, isInspecting) { |
| 73 this.inspectionMap_.set(deviceInfo, isInspecting); |
| 74 this.updateRow_(deviceInfo, this.devices_.indexOf(deviceInfo)); |
| 75 }, |
| 76 |
| 77 /** |
| 78 * Fires a forget pressed event for the row |index|. |
| 79 * @param {number} index |
| 80 * @private |
| 81 */ |
| 82 handleForgetClick_: function(index) { |
| 83 var event = new CustomEvent('forgetpressed', { |
| 84 bubbles: true, |
| 85 detail: { |
| 86 address: this.devices_.item(index).address, |
| 87 } |
| 88 }); |
| 89 this.dispatchEvent(event); |
| 90 }, |
| 91 |
| 92 /** |
| 64 * Updates table row on change event of the device collection. | 93 * Updates table row on change event of the device collection. |
| 94 * @param {!Event} event |
| 65 * @private | 95 * @private |
| 66 * @param {!Event} event | |
| 67 */ | 96 */ |
| 68 handleChange_: function(event) { | 97 handleChange_: function(event) { |
| 69 this.updateRow_(this.devices_.item(event.index), event.index); | 98 this.updateRow_(this.devices_.item(event.index), event.index); |
| 70 }, | 99 }, |
| 71 | 100 |
| 72 /** | 101 /** |
| 73 * Fires a inspect pressed event for the row |index|. | 102 * Fires an inspect pressed event for the row |index|. |
| 103 * @param {number} index |
| 74 * @private | 104 * @private |
| 75 * @param {number} index | |
| 76 */ | 105 */ |
| 77 handleInspectClick_: function(index) { | 106 handleInspectClick_: function(index) { |
| 78 var event = new CustomEvent('inspectpressed', { | 107 var event = new CustomEvent('inspectpressed', { |
| 79 bubbles: true, | 108 bubbles: true, |
| 80 detail: { | 109 detail: { |
| 81 address: this.devices_.item(index).address, | 110 address: this.devices_.item(index).address, |
| 82 } | 111 } |
| 83 }); | 112 }); |
| 84 this.dispatchEvent(event); | 113 this.dispatchEvent(event); |
| 85 }, | 114 }, |
| 86 | 115 |
| 87 /** | 116 /** |
| 88 * Updates table row on splice event of the device collection. | 117 * Updates table row on splice event of the device collection. |
| 118 * @param {!Event} event |
| 89 * @private | 119 * @private |
| 90 * @param {!Event} event | |
| 91 */ | 120 */ |
| 92 handleSplice_: function(event) { | 121 handleSplice_: function(event) { |
| 93 event.removed.forEach(function() { | 122 event.removed.forEach(function() { |
| 94 this.body_.deleteRow(event.index); | 123 this.body_.deleteRow(event.index); |
| 95 }, this); | 124 }, this); |
| 96 | 125 |
| 97 event.added.forEach(function(device, index) { | 126 event.added.forEach(function(device, index) { |
| 98 this.insertRow_(device, event.index + index); | 127 this.insertRow_(device, event.index + index); |
| 99 }, this); | 128 }, this); |
| 100 }, | 129 }, |
| 101 | 130 |
| 102 /** | 131 /** |
| 103 * Inserts a new row at |index| and updates it with info from |device|. | 132 * Inserts a new row at |index| and updates it with info from |device|. |
| 104 * @private | |
| 105 * @param {!interfaces.BluetoothDevice.DeviceInfo} device | 133 * @param {!interfaces.BluetoothDevice.DeviceInfo} device |
| 106 * @param {?number} index | 134 * @param {?number} index |
| 135 * @private |
| 107 */ | 136 */ |
| 108 insertRow_: function(device, index) { | 137 insertRow_: function(device, index) { |
| 109 var row = this.body_.insertRow(index); | 138 var row = this.body_.insertRow(index); |
| 110 row.id = device.address; | 139 row.id = device.address; |
| 111 | 140 |
| 112 for (var i = 0; i < this.headers_.length; i++) { | 141 for (var i = 0; i < this.headers_.length; i++) { |
| 142 // Skip the LINKS column. It has no data-field attribute. |
| 143 if (i === COLUMNS.LINKS) continue; |
| 113 row.insertCell(); | 144 row.insertCell(); |
| 114 } | 145 } |
| 115 | 146 |
| 116 // Make two extra cells for the inspect link and connect errors. | 147 // Make two extra cells for the inspect link and connect errors. |
| 117 var inspectCell = row.insertCell(); | 148 var inspectCell = row.insertCell(); |
| 118 | 149 |
| 119 var inspectLink = document.createElement('a', 'action-link'); | 150 var inspectLink = document.createElement('a', 'action-link'); |
| 151 inspectLink.textContent = 'Inspect'; |
| 120 inspectCell.appendChild(inspectLink); | 152 inspectCell.appendChild(inspectLink); |
| 121 inspectLink.addEventListener('click', function() { | 153 inspectLink.addEventListener('click', function() { |
| 122 this.handleInspectClick_(row.sectionRowIndex); | 154 this.handleInspectClick_(row.sectionRowIndex); |
| 123 }.bind(this)); | 155 }.bind(this)); |
| 124 | 156 |
| 157 var forgetLink = document.createElement('a', 'action-link'); |
| 158 forgetLink.textContent = 'Forget'; |
| 159 inspectCell.appendChild(forgetLink); |
| 160 forgetLink.addEventListener('click', function() { |
| 161 this.handleForgetClick_(row.sectionRowIndex); |
| 162 }.bind(this)); |
| 163 |
| 125 this.updateRow_(device, row.sectionRowIndex); | 164 this.updateRow_(device, row.sectionRowIndex); |
| 126 }, | 165 }, |
| 127 | 166 |
| 128 /** | 167 /** |
| 129 * Deletes and recreates the table using the cached |devices_|. | 168 * Deletes and recreates the table using the cached |devices_|. |
| 130 * @private | 169 * @private |
| 131 */ | 170 */ |
| 132 redraw_: function() { | 171 redraw_: function() { |
| 133 this.removeChild(this.body_); | 172 this.removeChild(this.body_); |
| 134 this.appendChild(document.createElement('tbody')); | 173 this.appendChild(document.createElement('tbody')); |
| 135 this.body_ = this.tBodies[0]; | 174 this.body_ = this.tBodies[0]; |
| 136 this.body_.classList.add('table-body'); | 175 this.body_.classList.add('table-body'); |
| 137 | 176 |
| 138 for (var i = 0; i < this.devices_.length; i++) { | 177 for (var i = 0; i < this.devices_.length; i++) { |
| 139 this.insertRow_(this.devices_.item(i)); | 178 this.insertRow_(this.devices_.item(i)); |
| 140 } | 179 } |
| 141 }, | 180 }, |
| 142 | 181 |
| 143 /** | 182 /** |
| 144 * Updates the row at |index| with the info from |device|. | 183 * Updates the row at |index| with the info from |device|. |
| 145 * @private | |
| 146 * @param {!interfaces.BluetoothDevice.DeviceInfo} device | 184 * @param {!interfaces.BluetoothDevice.DeviceInfo} device |
| 147 * @param {number} index | 185 * @param {number} index |
| 186 * @private |
| 148 */ | 187 */ |
| 149 updateRow_: function(device, index) { | 188 updateRow_: function(device, index) { |
| 150 var row = this.body_.rows[index]; | 189 var row = this.body_.rows[index]; |
| 151 assert(row, 'Row ' + index + ' is not in the table.'); | 190 assert(row, 'Row ' + index + ' is not in the table.'); |
| 152 | 191 |
| 153 row.classList.toggle('removed', device.removed); | 192 row.classList.toggle('removed', device.removed); |
| 154 | 193 |
| 155 var inspectLink = row.cells[COLUMNS.INSPECT_LINK].children[0]; | 194 var forgetLink = row.cells[COLUMNS.LINKS].children[1]; |
| 156 inspectLink.disabled = false; | 195 |
| 157 switch (device.connectionStatus) { | 196 if (this.inspectionMap_.has(device)) |
| 158 case device_collection.ConnectionStatus.DISCONNECTED: | 197 forgetLink.disabled = !this.inspectionMap_.get(device); |
| 159 inspectLink.textContent = 'Inspect'; | 198 else |
| 160 break; | 199 forgetLink.disabled = true; |
| 161 case device_collection.ConnectionStatus.CONNECTED: | |
| 162 inspectLink.textContent = 'Forget'; | |
| 163 break; | |
| 164 case device_collection.ConnectionStatus.CONNECTING: | |
| 165 inspectLink.disabled = true; | |
| 166 break; | |
| 167 default: assert('case not handled'); | |
| 168 } | |
| 169 | 200 |
| 170 // Update the properties based on the header field path. | 201 // Update the properties based on the header field path. |
| 171 for (var i = 0; i < this.headers_.length; i++) { | 202 for (var i = 0; i < this.headers_.length; i++) { |
| 203 // Skip the LINKS column. It has no data-field attribute. |
| 204 if (i === COLUMNS.LINKS) continue; |
| 205 |
| 172 var header = this.headers_[i]; | 206 var header = this.headers_[i]; |
| 173 var propName = header.dataset.field; | 207 var propName = header.dataset.field; |
| 174 | 208 |
| 175 var parts = propName.split('.'); | 209 var parts = propName.split('.'); |
| 176 var obj = device; | 210 var obj = device; |
| 177 while (obj != null && parts.length > 0) { | 211 while (obj != null && parts.length > 0) { |
| 178 var part = parts.shift(); | 212 var part = parts.shift(); |
| 179 obj = obj[part]; | 213 obj = obj[part]; |
| 180 } | 214 } |
| 181 | 215 |
| 182 if (propName == 'is_gatt_connected') { | 216 if (propName == 'is_gatt_connected') { |
| 183 obj = obj ? 'Connected' : 'Not Connected'; | 217 obj = obj ? 'Connected' : 'Not Connected'; |
| 184 } | 218 } |
| 185 | 219 |
| 186 var cell = row.cells[i]; | 220 var cell = row.cells[i]; |
| 187 cell.textContent = obj == null ? 'Unknown' : obj; | 221 cell.textContent = obj == null ? 'Unknown' : obj; |
| 188 cell.dataset.label = header.textContent; | 222 cell.dataset.label = header.textContent; |
| 189 } | 223 } |
| 190 }, | 224 }, |
| 191 }; | 225 }; |
| 192 | 226 |
| 193 return { | 227 return { |
| 194 DeviceTable: DeviceTable, | 228 DeviceTable: DeviceTable, |
| 195 }; | 229 }; |
| 196 }); | 230 }); |
| OLD | NEW |