Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / options / chromeos / bluetooth_device_list.js
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6  * @typedef {{name: string,
7  *            address: string,
8  *            paired: boolean,
9  *            connected: boolean,
10  *            connecting: boolean,
11  *            connectable: boolean,
12  *            pairing: (string|undefined),
13  *            passkey: (number|undefined),
14  *            pincode: (string|undefined),
15  *            entered: (number|undefined)}}
16  */
17 var BluetoothDevice;
18
19 cr.define('options.system.bluetooth', function() {
20   /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel;
21   /** @const */ var DeletableItem = options.DeletableItem;
22   /** @const */ var DeletableItemList = options.DeletableItemList;
23   /** @const */ var ListSingleSelectionModel = cr.ui.ListSingleSelectionModel;
24
25   /**
26    * Bluetooth settings constants.
27    */
28   function Constants() {}
29
30   /**
31    * Creates a new bluetooth list item.
32    * @param {BluetoothDevice} device Description of the Bluetooth device.
33    * @constructor
34    * @extends {options.DeletableItem}
35    */
36   function BluetoothListItem(device) {
37     var el = cr.doc.createElement('div');
38     el.__proto__ = BluetoothListItem.prototype;
39     el.data = {};
40     for (var key in device)
41       el.data[key] = device[key];
42     el.decorate();
43     // Only show the close button for paired devices, but not for connecting
44     // devices.
45     el.deletable = device.paired && !device.connecting;
46     return el;
47   }
48
49   BluetoothListItem.prototype = {
50     __proto__: DeletableItem.prototype,
51
52     /**
53      * Description of the Bluetooth device.
54      * @type {?BluetoothDevice}
55      */
56     data: null,
57
58     /** @override */
59     decorate: function() {
60       DeletableItem.prototype.decorate.call(this);
61       var label = this.ownerDocument.createElement('div');
62       label.className = 'bluetooth-device-label';
63       this.classList.add('bluetooth-device');
64       // There are four kinds of devices we want to distinguish:
65       //  * Connecting devices: in bold with a "connecting" label,
66       //  * Connected devices: in bold,
67       //  * Paired, not connected but connectable devices: regular and
68       //  * Paired, not connected and not connectable devices: grayed out.
69       this.connected = this.data.connecting ||
70           (this.data.paired && this.data.connected);
71       this.notconnectable = this.data.paired && !this.data.connecting &&
72           !this.data.connected && !this.data.connectable;
73       // "paired" devices are those that are remembered but not connected.
74       this.paired = this.data.paired && !this.data.connected &&
75           this.data.connectable;
76
77       var content = this.data.name;
78       // Update the device's label according to its state. A "connecting" device
79       // can be in the process of connecting and pairing, so we check connecting
80       // first.
81       if (this.data.connecting) {
82         content = loadTimeData.getStringF('bluetoothDeviceConnecting',
83             this.data.name);
84       }
85       label.textContent = content;
86       this.contentElement.appendChild(label);
87     },
88   };
89
90   /**
91    * Class for displaying a list of Bluetooth devices.
92    * @constructor
93    * @extends {options.DeletableItemList}
94    */
95   var BluetoothDeviceList = cr.ui.define('list');
96
97   BluetoothDeviceList.prototype = {
98     __proto__: DeletableItemList.prototype,
99
100     /**
101      * Height of a list entry in px.
102      * @type {number}
103      * @private
104      */
105     itemHeight_: 32,
106
107     /**
108      * Width of a list entry in px.
109      * @type {number}
110      * @private
111      */
112     itemWidth_: 400,
113
114     /** @override */
115     decorate: function() {
116       DeletableItemList.prototype.decorate.call(this);
117       // Force layout of all items even if not in the viewport to address
118       // errors in scroll positioning when the list is hidden during initial
119       // layout. The impact on performance should be minimal given that the
120       // list is not expected to grow very large. Fixed height items are also
121       // required to avoid caching incorrect sizes during layout of a hidden
122       // list.
123       this.autoExpands = true;
124       this.fixedHeight = true;
125       this.clear();
126       this.selectionModel = new ListSingleSelectionModel();
127     },
128
129     /**
130      * Adds a bluetooth device to the list of available devices. A check is
131      * made to see if the device is already in the list, in which case the
132      * existing device is updated.
133      * @param {{name: string,
134      *          address: string,
135      *          paired: boolean,
136      *          connected: boolean,
137      *          connecting: boolean,
138      *          connectable: boolean,
139      *          pairing: (string|undefined),
140      *          passkey: (number|undefined),
141      *          pincode: (string|undefined),
142      *          entered: (number|undefined)}} device
143      *     Description of the bluetooth device.
144      * @return {boolean} True if the devies was successfully added or updated.
145      */
146     appendDevice: function(device) {
147       var selectedDevice = this.getSelectedDevice_();
148       var index = this.find(device.address);
149       if (index == undefined) {
150         this.dataModel.push(device);
151         this.redraw();
152       } else {
153         this.dataModel.splice(index, 1, device);
154         this.redrawItem(index);
155       }
156       this.updateListVisibility_();
157       if (selectedDevice)
158         this.setSelectedDevice_(selectedDevice);
159       return true;
160     },
161
162     /**
163      * Forces a revailidation of the list content. Deleting a single item from
164      * the list results in a stale cache requiring an invalidation.
165      * @param {string=} opt_selection Optional address of device to select
166      *     after refreshing the list.
167      */
168     refresh: function(opt_selection) {
169       // TODO(kevers): Investigate if the stale cache issue can be fixed in
170       // cr.ui.list.
171       var selectedDevice = opt_selection ? opt_selection :
172           this.getSelectedDevice_();
173       this.invalidate();
174       this.redraw();
175       if (selectedDevice)
176         this.setSelectedDevice_(selectedDevice);
177     },
178
179     /**
180      * Retrieves the address of the selected device, or null if no device is
181      * selected.
182      * @return {(string|undefined)} Address of selected device or null.
183      * @private
184      */
185     getSelectedDevice_: function() {
186       var selection = this.selectedItem;
187       if (selection)
188         return selection.address;
189       return undefined;
190     },
191
192     /**
193      * Selects the device with the matching address.
194      * @param {string} address The unique address of the device.
195      * @private
196      */
197     setSelectedDevice_: function(address) {
198       var index = this.find(address);
199       if (index != undefined)
200         this.selectionModel.selectRange(index, index);
201     },
202
203     /**
204      * Perges all devices from the list.
205      */
206     clear: function() {
207       this.dataModel = new ArrayDataModel([]);
208       this.redraw();
209       this.updateListVisibility_();
210     },
211
212     /**
213      * Returns the index of the list entry with the matching address.
214      * @param {string} address Unique address of the Bluetooth device.
215      * @return {number|undefined} Index of the matching entry or
216      * undefined if no match found.
217      */
218     find: function(address) {
219       var size = this.dataModel.length;
220       for (var i = 0; i < size; i++) {
221         var entry = this.dataModel.item(i);
222         if (entry.address == address)
223           return i;
224       }
225     },
226
227     /**
228      * @override
229      * @param {BluetoothDevice} entry
230      */
231     createItem: function(entry) {
232       return new BluetoothListItem(entry);
233     },
234
235     /**
236      * Overrides the default implementation, which is used to compute the
237      * size of an element in the list.  The default implementation relies
238      * on adding a placeholder item to the list and fetching its size and
239      * position. This strategy does not work if an item is added to the list
240      * while it is hidden, as the computed metrics will all be zero in that
241      * case.
242      * @return {{height: number, marginTop: number, marginBottom: number,
243      *     width: number, marginLeft: number, marginRight: number}}
244      *     The height and width of the item, taking margins into account,
245      *     and the margins themselves.
246      */
247     measureItem: function() {
248       return {
249         height: this.itemHeight_,
250         marginTop: 0,
251         marginBottom: 0,
252         width: this.itemWidth_,
253         marginLeft: 0,
254         marginRight: 0
255       };
256     },
257
258     /**
259      * Override the default implementation to return a predetermined size,
260      * which in turns allows proper layout of items even if the list is hidden.
261      * @return {{height: number, width: number}} Dimensions of a single item in
262      *     the list of bluetooth device.
263      * @private
264      */
265     getDefaultItemSize_: function() {
266       return {
267         height: this.itemHeight_,
268         width: this.itemWidth_
269       };
270     },
271
272     /**
273      * Override base implementation of handleClick, which unconditionally
274      * removes the item.  In this case, removal of the element is deferred
275      * pending confirmation from the Bluetooth adapter.
276      * @param {Event} e The click event object.
277      * @override
278      */
279     handleClick: function(e) {
280       if (this.disabled)
281         return;
282
283       var target = /** @type {HTMLElement} */(e.target);
284       if (!target.classList.contains('row-delete-button'))
285         return;
286
287       var item = this.getListItemAncestor(target);
288       var selected = this.selectionModel.selectedIndex;
289       var index = this.getIndexOfListItem(item);
290       if (item && item.deletable) {
291         if (selected != index)
292           this.setSelectedDevice_(item.data.address);
293         // Device is busy until we hear back from the Bluetooth adapter.
294         // Prevent double removal request.
295         item.deletable = false;
296         // TODO(kevers): Provide visual feedback that the device is busy.
297
298         // Inform the bluetooth adapter that we are disconnecting or
299         // forgetting the device.
300         chrome.send('updateBluetoothDevice',
301           [item.data.address, item.connected ? 'disconnect' : 'forget']);
302       }
303     },
304
305     /** @override */
306     deleteItemAtIndex: function(index) {
307       var selectedDevice = this.getSelectedDevice_();
308       this.dataModel.splice(index, 1);
309       this.refresh(selectedDevice);
310       this.updateListVisibility_();
311     },
312
313     /**
314      * If the list has an associated empty list placholder then update the
315      * visibility of the list and placeholder.
316      * @private
317      */
318     updateListVisibility_: function() {
319       var empty = this.dataModel.length == 0;
320       var listPlaceHolderID = this.id + '-empty-placeholder';
321       if ($(listPlaceHolderID)) {
322         if (this.hidden != empty) {
323           this.hidden = empty;
324           $(listPlaceHolderID).hidden = !empty;
325           this.refresh();
326         }
327       }
328     },
329   };
330
331   cr.defineProperty(BluetoothListItem, 'connected', cr.PropertyKind.BOOL_ATTR);
332
333   cr.defineProperty(BluetoothListItem, 'paired', cr.PropertyKind.BOOL_ATTR);
334
335   cr.defineProperty(BluetoothListItem, 'connecting', cr.PropertyKind.BOOL_ATTR);
336
337   cr.defineProperty(BluetoothListItem, 'notconnectable',
338       cr.PropertyKind.BOOL_ATTR);
339
340   return {
341     BluetoothListItem: BluetoothListItem,
342     BluetoothDeviceList: BluetoothDeviceList,
343     Constants: Constants
344   };
345 });