- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / resources / media / client_renderer.js
1 // Copyright 2013 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 var ClientRenderer = (function() {
6   var ClientRenderer = function() {
7     this.playerListElement = document.getElementById('player-list');
8     this.audioStreamListElement = document.getElementById('audio-stream-list');
9     this.propertiesTable = document.getElementById('property-table');
10     this.logTable = document.getElementById('log');
11     this.graphElement = document.getElementById('graphs');
12
13     this.selectedPlayer = null;
14     this.selectedStream = null;
15
16     this.selectedPlayerLogIndex = 0;
17
18     this.filterFunction = function() { return true; };
19     this.filterText = document.getElementById('filter-text');
20     this.filterText.onkeyup = this.onTextChange_.bind(this);
21
22     this.bufferCanvas = document.createElement('canvas');
23     this.bufferCanvas.width = media.BAR_WIDTH;
24     this.bufferCanvas.height = media.BAR_HEIGHT;
25
26     this.clipboardTextarea = document.getElementById('clipboard-textarea');
27     this.clipboardButton = document.getElementById('copy-button');
28     this.clipboardButton.onclick = this.copyToClipboard_.bind(this);
29   };
30
31   function removeChildren(element) {
32     while (element.hasChildNodes()) {
33       element.removeChild(element.lastChild);
34     }
35   };
36
37   function createButton(text, select_cb) {
38     var button = document.createElement('button');
39
40     button.appendChild(document.createTextNode(text));
41     button.onclick = function() {
42       select_cb();
43     };
44
45     return button;
46   };
47
48   ClientRenderer.prototype = {
49     audioStreamAdded: function(audioStreams, audioStreamAdded) {
50       this.redrawAudioStreamList_(audioStreams);
51     },
52
53     audioStreamUpdated: function(audioStreams, stream, key, value) {
54       if (stream === this.selectedStream) {
55         this.drawProperties_(stream);
56       }
57     },
58
59     audioStreamRemoved: function(audioStreams, audioStreamRemoved) {
60       this.redrawAudioStreamList_(audioStreams);
61     },
62
63     /**
64      * Called when a player is added to the collection.
65      * @param players The entire map of id -> player.
66      * @param player_added The player that is added.
67      */
68     playerAdded: function(players, playerAdded) {
69       this.redrawPlayerList_(players);
70     },
71
72     /**
73      * Called when a playre is removed from the collection.
74      * @param players The entire map of id -> player.
75      * @param player_added The player that was removed.
76      */
77     playerRemoved: function(players, playerRemoved) {
78       this.redrawPlayerList_(players);
79     },
80
81     /**
82      * Called when a property on a player is changed.
83      * @param players The entire map of id -> player.
84      * @param player The player that had its property changed.
85      * @param key The name of the property that was changed.
86      * @param value The new value of the property.
87      */
88     playerUpdated: function(players, player, key, value) {
89       if (player === this.selectedPlayer) {
90         this.drawProperties_(player.properties);
91         this.drawLog_();
92         this.drawGraphs_();
93       }
94       if (key === 'name' || key === 'url') {
95         this.redrawPlayerList_(players);
96       }
97     },
98
99     redrawAudioStreamList_: function(streams) {
100       removeChildren(this.audioStreamListElement);
101
102       for (id in streams) {
103         var li = document.createElement('li');
104         li.appendChild(createButton(
105             id, this.selectAudioStream_.bind(this, streams[id])));
106         this.audioStreamListElement.appendChild(li);
107       }
108     },
109
110     selectAudioStream_: function(audioStream) {
111       this.selectedStream = audioStream;
112       this.selectedPlayer = null;
113       this.drawProperties_(audioStream);
114       removeChildren(this.logTable.querySelector('tbody'));
115       removeChildren(this.graphElement);
116     },
117
118     redrawPlayerList_: function(players) {
119       removeChildren(this.playerListElement);
120
121       for (id in players) {
122         var li = document.createElement('li');
123         var player = players[id];
124         var usableName = player.properties.name ||
125             player.properties.url ||
126             'player ' + player.id;
127
128         li.appendChild(createButton(
129             usableName, this.selectPlayer_.bind(this, player)));
130         this.playerListElement.appendChild(li);
131       }
132     },
133
134     selectPlayer_: function(player) {
135       this.selectedPlayer = player;
136       this.selectedPlayerLogIndex = 0;
137       this.selectedStream = null;
138       this.drawProperties_(player.properties);
139
140       removeChildren(this.logTable.querySelector('tbody'));
141       removeChildren(this.graphElement);
142       this.drawLog_();
143       this.drawGraphs_();
144     },
145
146     drawProperties_: function(propertyMap) {
147       removeChildren(this.propertiesTable);
148
149       for (key in propertyMap) {
150         var value = propertyMap[key];
151
152         var row = this.propertiesTable.insertRow(-1);
153         var keyCell = row.insertCell(-1);
154         var valueCell = row.insertCell(-1);
155
156         keyCell.appendChild(document.createTextNode(key));
157         valueCell.appendChild(document.createTextNode(value));
158       }
159     },
160
161     appendEventToLog_: function(event) {
162       if (this.filterFunction(event.key)) {
163         var row = this.logTable.querySelector('tbody').insertRow(-1);
164
165         row.insertCell(-1).appendChild(document.createTextNode(
166             util.millisecondsToString(event.time)));
167         row.insertCell(-1).appendChild(document.createTextNode(event.key));
168         row.insertCell(-1).appendChild(document.createTextNode(event.value));
169       }
170     },
171
172     drawLog_: function() {
173       var toDraw = this.selectedPlayer.allEvents.slice(
174           this.selectedPlayerLogIndex);
175       toDraw.forEach(this.appendEventToLog_.bind(this));
176       this.selectedPlayerLogIndex = this.selectedPlayer.allEvents.length;
177     },
178
179     drawGraphs_: function() {
180       function addToGraphs(name, graph, graphElement) {
181         var li = document.createElement('li');
182         li.appendChild(graph);
183         li.appendChild(document.createTextNode(name));
184         graphElement.appendChild(li);
185       }
186
187       var url = this.selectedPlayer.properties.url;
188       if (!url) {
189         return;
190       }
191
192       var cache = media.cacheForUrl(url);
193
194       var player = this.selectedPlayer;
195       var props = player.properties;
196
197       var cacheExists = false;
198       var bufferExists = false;
199
200       if (props['buffer_start'] !== undefined &&
201           props['buffer_current'] !== undefined &&
202           props['buffer_end'] !== undefined &&
203           props['total_bytes'] !== undefined) {
204         this.drawBufferGraph_(props['buffer_start'],
205                               props['buffer_current'],
206                               props['buffer_end'],
207                               props['total_bytes']);
208         bufferExists = true;
209       }
210
211       if (cache) {
212         if (player.properties['total_bytes']) {
213           cache.size = Number(player.properties['total_bytes']);
214         }
215         cache.generateDetails();
216         cacheExists = true;
217
218       }
219
220       if (!this.graphElement.hasChildNodes()) {
221         if (bufferExists) {
222           addToGraphs('buffer', this.bufferCanvas, this.graphElement);
223         }
224         if (cacheExists) {
225           addToGraphs('cache read', cache.readCanvas, this.graphElement);
226           addToGraphs('cache write', cache.writeCanvas, this.graphElement);
227         }
228       }
229     },
230
231     drawBufferGraph_: function(start, current, end, size) {
232       var ctx = this.bufferCanvas.getContext('2d');
233       var width = this.bufferCanvas.width;
234       var height = this.bufferCanvas.height;
235       ctx.fillStyle = '#aaa';
236       ctx.fillRect(0, 0, width, height);
237
238       var scale_factor = width / size;
239       var left = start * scale_factor;
240       var middle = current * scale_factor;
241       var right = end * scale_factor;
242
243       ctx.fillStyle = '#a0a';
244       ctx.fillRect(left, 0, middle - left, height);
245       ctx.fillStyle = '#aa0';
246       ctx.fillRect(middle, 0, right - middle, height);
247     },
248
249     copyToClipboard_: function() {
250       var properties = this.selectedStream ||
251           this.selectedPlayer.properties || false;
252       if (!properties) {
253         return;
254       }
255       var stringBuffer = [];
256
257       for (var key in properties) {
258         var value = properties[key];
259         stringBuffer.push(key.toString());
260         stringBuffer.push(': ');
261         stringBuffer.push(value.toString());
262         stringBuffer.push('\n');
263       }
264
265       this.clipboardTextarea.value = stringBuffer.join('');
266       this.clipboardTextarea.classList.remove('hidden');
267       this.clipboardTextarea.focus();
268       this.clipboardTextarea.select();
269
270       // The act of copying anything from the textarea gets canceled
271       // if the element in question gets the class 'hidden' (which contains the
272       // css property display:none) before the event is finished. For this, it
273       // is necessary put the property setting on the event loop to be executed
274       // after the copy has taken place.
275       this.clipboardTextarea.oncopy = function(event) {
276         setTimeout(function(element) {
277           event.target.classList.add('hidden');
278         }, 0);
279       };
280     },
281
282     onTextChange_: function(event) {
283       var text = this.filterText.value.toLowerCase();
284       var parts = text.split(',').map(function(part) {
285         return part.trim();
286       }).filter(function(part) {
287         return part.trim().length > 0;
288       });
289
290       this.filterFunction = function(text) {
291         text = text.toLowerCase();
292         return parts.length === 0 || parts.some(function(part) {
293           return text.indexOf(part) != -1;
294         });
295       };
296
297       if (this.selectedPlayer) {
298         removeChildren(this.logTable.querySelector('tbody'));
299         this.selectedPlayerLogIndex = 0;
300         this.drawLog_();
301       }
302     },
303   };
304
305   return ClientRenderer;
306 })();