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 |