You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ConnectionIndicator.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /* global APP, $ */
  2. var JitsiPopover = require("../util/JitsiPopover");
  3. /**
  4. * Constructs new connection indicator.
  5. * @param videoContainer the video container associated with the indicator.
  6. * @constructor
  7. */
  8. function ConnectionIndicator(videoContainer, jid) {
  9. this.videoContainer = videoContainer;
  10. this.bandwidth = null;
  11. this.packetLoss = null;
  12. this.bitrate = null;
  13. this.showMoreValue = false;
  14. this.resolution = null;
  15. this.transport = [];
  16. this.popover = null;
  17. this.jid = jid;
  18. this.create();
  19. }
  20. /**
  21. * Values for the connection quality
  22. * @type {{98: string,
  23. * 81: string,
  24. * 64: string,
  25. * 47: string,
  26. * 30: string,
  27. * 0: string}}
  28. */
  29. ConnectionIndicator.connectionQualityValues = {
  30. 98: "18px", //full
  31. 81: "15px",//4 bars
  32. 64: "11px",//3 bars
  33. 47: "7px",//2 bars
  34. 30: "3px",//1 bar
  35. 0: "0px"//empty
  36. };
  37. ConnectionIndicator.getIP = function(value) {
  38. return value.substring(0, value.lastIndexOf(":"));
  39. };
  40. ConnectionIndicator.getPort = function(value) {
  41. return value.substring(value.lastIndexOf(":") + 1, value.length);
  42. };
  43. ConnectionIndicator.getStringFromArray = function (array) {
  44. var res = "";
  45. for(var i = 0; i < array.length; i++) {
  46. res += (i === 0? "" : ", ") + array[i];
  47. }
  48. return res;
  49. };
  50. /**
  51. * Generates the html content.
  52. * @returns {string} the html content.
  53. */
  54. ConnectionIndicator.prototype.generateText = function () {
  55. var downloadBitrate, uploadBitrate, packetLoss, resolution, i;
  56. var translate = APP.translation.translateString;
  57. if(this.bitrate === null) {
  58. downloadBitrate = "N/A";
  59. uploadBitrate = "N/A";
  60. }
  61. else {
  62. downloadBitrate =
  63. this.bitrate.download? this.bitrate.download + " Kbps" : "N/A";
  64. uploadBitrate =
  65. this.bitrate.upload? this.bitrate.upload + " Kbps" : "N/A";
  66. }
  67. if(this.packetLoss === null) {
  68. packetLoss = "N/A";
  69. } else {
  70. packetLoss = "<span class='jitsipopover_green'>&darr;</span>" +
  71. (this.packetLoss.download !== null ?
  72. this.packetLoss.download : "N/A") +
  73. "% <span class='jitsipopover_orange'>&uarr;</span>" +
  74. (this.packetLoss.upload !== null? this.packetLoss.upload : "N/A") +
  75. "%";
  76. }
  77. var resolutionValue = null;
  78. if(this.resolution && this.jid != null) {
  79. var keys = Object.keys(this.resolution);
  80. for(var ssrc in this.resolution) {
  81. resolutionValue = this.resolution[ssrc];
  82. }
  83. }
  84. if(this.jid === null) {
  85. resolution = "";
  86. if(this.resolution === null || !Object.keys(this.resolution) ||
  87. Object.keys(this.resolution).length === 0) {
  88. resolution = "N/A";
  89. } else {
  90. for (i in this.resolution) {
  91. resolutionValue = this.resolution[i];
  92. if (resolutionValue) {
  93. if (resolutionValue.height &&
  94. resolutionValue.width) {
  95. resolution += (resolution === "" ? "" : ", ") +
  96. resolutionValue.width + "x" +
  97. resolutionValue.height;
  98. }
  99. }
  100. }
  101. }
  102. } else if(!resolutionValue ||
  103. !resolutionValue.height ||
  104. !resolutionValue.width) {
  105. resolution = "N/A";
  106. } else {
  107. resolution = resolutionValue.width + "x" + resolutionValue.height;
  108. }
  109. var result = "<table style='width:100%'>" +
  110. "<tr>" +
  111. "<td><span class='jitsipopover_blue' data-i18n='connectionindicator.bitrate'>" +
  112. translate("connectionindicator.bitrate") + "</span></td>" +
  113. "<td><span class='jitsipopover_green'>&darr;</span>" +
  114. downloadBitrate + " <span class='jitsipopover_orange'>&uarr;</span>" +
  115. uploadBitrate + "</td>" +
  116. "</tr><tr>" +
  117. "<td><span class='jitsipopover_blue' data-i18n='connectionindicator.packetloss'>" +
  118. translate("connectionindicator.packetloss") + "</span></td>" +
  119. "<td>" + packetLoss + "</td>" +
  120. "</tr><tr>" +
  121. "<td><span class='jitsipopover_blue' data-i18n='connectionindicator.resolution'>" +
  122. translate("connectionindicator.resolution") + "</span></td>" +
  123. "<td>" + resolution + "</td></tr></table>";
  124. if(this.videoContainer.videoSpanId == "localVideoContainer") {
  125. result += "<div class=\"jitsipopover_showmore\" " +
  126. "onclick = \"APP.UI.connectionIndicatorShowMore('" +
  127. // FIXME: we do not know local jid when this text is generated
  128. //this.jid + "')\" data-i18n='connectionindicator." +
  129. "local')\" data-i18n='connectionindicator." +
  130. (this.showMoreValue ? "less" : "more") + "'>" +
  131. translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
  132. "</div><br />";
  133. }
  134. if (this.showMoreValue) {
  135. var downloadBandwidth, uploadBandwidth, transport;
  136. if (this.bandwidth === null) {
  137. downloadBandwidth = "N/A";
  138. uploadBandwidth = "N/A";
  139. } else {
  140. downloadBandwidth = this.bandwidth.download?
  141. this.bandwidth.download + " Kbps" :
  142. "N/A";
  143. uploadBandwidth = this.bandwidth.upload?
  144. this.bandwidth.upload + " Kbps" :
  145. "N/A";
  146. }
  147. if (!this.transport || this.transport.length === 0) {
  148. transport = "<tr>" +
  149. "<td><span class='jitsipopover_blue' " +
  150. "data-i18n='connectionindicator.address'>" +
  151. translate("connectionindicator.address") + "</span></td>" +
  152. "<td> N/A</td></tr>";
  153. } else {
  154. var data = {remoteIP: [], localIP:[], remotePort:[], localPort:[]};
  155. for(i = 0; i < this.transport.length; i++) {
  156. var ip = ConnectionIndicator.getIP(this.transport[i].ip);
  157. var port = ConnectionIndicator.getPort(this.transport[i].ip);
  158. var localIP =
  159. ConnectionIndicator.getIP(this.transport[i].localip);
  160. var localPort =
  161. ConnectionIndicator.getPort(this.transport[i].localip);
  162. if(data.remoteIP.indexOf(ip) == -1) {
  163. data.remoteIP.push(ip);
  164. }
  165. if(data.remotePort.indexOf(port) == -1) {
  166. data.remotePort.push(port);
  167. }
  168. if(data.localIP.indexOf(localIP) == -1) {
  169. data.localIP.push(localIP);
  170. }
  171. if(data.localPort.indexOf(localPort) == -1) {
  172. data.localPort.push(localPort);
  173. }
  174. }
  175. var local_address_key = "connectionindicator.localaddress";
  176. var remote_address_key = "connectionindicator.remoteaddress";
  177. var localTransport =
  178. "<tr><td><span class='jitsipopover_blue' data-i18n='" +
  179. local_address_key +"' data-i18n-options='" +
  180. JSON.stringify({count: data.localIP.length}) + "'>" +
  181. translate(local_address_key, {count: data.localIP.length}) +
  182. "</span></td><td> " +
  183. ConnectionIndicator.getStringFromArray(data.localIP) +
  184. "</td></tr>";
  185. transport =
  186. "<tr><td><span class='jitsipopover_blue' data-i18n='" +
  187. remote_address_key + "' data-i18n-options='" +
  188. JSON.stringify({count: data.remoteIP.length}) + "'>" +
  189. translate(remote_address_key,
  190. {count: data.remoteIP.length}) +
  191. "</span></td><td> " +
  192. ConnectionIndicator.getStringFromArray(data.remoteIP) +
  193. "</td></tr>";
  194. var key_remote = "connectionindicator.remoteport",
  195. key_local = "connectionindicator.localport";
  196. transport += "<tr>" +
  197. "<td>" +
  198. "<span class='jitsipopover_blue' data-i18n='" + key_remote +
  199. "' data-i18n-options='" +
  200. JSON.stringify({count: this.transport.length}) + "'>" +
  201. translate(key_remote, {count: this.transport.length}) +
  202. "</span></td><td>";
  203. localTransport += "<tr>" +
  204. "<td>" +
  205. "<span class='jitsipopover_blue' data-i18n='" + key_local +
  206. "' data-i18n-options='" +
  207. JSON.stringify({count: this.transport.length}) + "'>" +
  208. translate(key_local, {count: this.transport.length}) +
  209. "</span></td><td>";
  210. transport +=
  211. ConnectionIndicator.getStringFromArray(data.remotePort);
  212. localTransport +=
  213. ConnectionIndicator.getStringFromArray(data.localPort);
  214. transport += "</td></tr>";
  215. transport += localTransport + "</td></tr>";
  216. transport +="<tr>" +
  217. "<td><span class='jitsipopover_blue' data-i18n='connectionindicator.transport'>" +
  218. translate("connectionindicator.transport") + "</span></td>" +
  219. "<td>" + this.transport[0].type + "</td></tr>";
  220. }
  221. result += "<table style='width:100%'>" +
  222. "<tr>" +
  223. "<td>" +
  224. "<span class='jitsipopover_blue' data-i18n='connectionindicator.bandwidth'>" +
  225. translate("connectionindicator.bandwidth") + "</span>" +
  226. "</td><td>" +
  227. "<span class='jitsipopover_green'>&darr;</span>" +
  228. downloadBandwidth +
  229. " <span class='jitsipopover_orange'>&uarr;</span>" +
  230. uploadBandwidth + "</td></tr>";
  231. result += transport + "</table>";
  232. }
  233. return result;
  234. };
  235. /**
  236. * Shows or hide the additional information.
  237. */
  238. ConnectionIndicator.prototype.showMore = function () {
  239. this.showMoreValue = !this.showMoreValue;
  240. this.updatePopoverData();
  241. };
  242. function createIcon(classes) {
  243. var icon = document.createElement("span");
  244. for(var i in classes) {
  245. icon.classList.add(classes[i]);
  246. }
  247. icon.appendChild(
  248. document.createElement("i")).classList.add("icon-connection");
  249. return icon;
  250. }
  251. /**
  252. * Creates the indicator
  253. */
  254. ConnectionIndicator.prototype.create = function () {
  255. this.connectionIndicatorContainer = document.createElement("div");
  256. this.connectionIndicatorContainer.className = "connectionindicator";
  257. this.connectionIndicatorContainer.style.display = "none";
  258. this.videoContainer.container.appendChild(
  259. this.connectionIndicatorContainer);
  260. this.popover = new JitsiPopover(
  261. $("#" + this.videoContainer.videoSpanId + " > .connectionindicator"),
  262. {content: "<div class=\"connection_info\" data-i18n='connectionindicator.na'>" +
  263. APP.translation.translateString("connectionindicator.na") + "</div>",
  264. skin: "black"});
  265. this.emptyIcon = this.connectionIndicatorContainer.appendChild(
  266. createIcon(["connection", "connection_empty"]));
  267. this.fullIcon = this.connectionIndicatorContainer.appendChild(
  268. createIcon(["connection", "connection_full"]));
  269. };
  270. /**
  271. * Removes the indicator
  272. */
  273. ConnectionIndicator.prototype.remove = function() {
  274. if (this.connectionIndicatorContainer.parentNode) {
  275. this.connectionIndicatorContainer.parentNode.removeChild(
  276. this.connectionIndicatorContainer);
  277. }
  278. this.popover.forceHide();
  279. };
  280. /**
  281. * Updates the data of the indicator
  282. * @param percent the percent of connection quality
  283. * @param object the statistics data.
  284. */
  285. ConnectionIndicator.prototype.updateConnectionQuality =
  286. function (percent, object) {
  287. if (percent === null) {
  288. this.connectionIndicatorContainer.style.display = "none";
  289. this.popover.forceHide();
  290. return;
  291. } else {
  292. if(this.connectionIndicatorContainer.style.display == "none") {
  293. this.connectionIndicatorContainer.style.display = "block";
  294. this.videoContainer.updateIconPositions();
  295. }
  296. }
  297. this.bandwidth = object.bandwidth;
  298. this.bitrate = object.bitrate;
  299. this.packetLoss = object.packetLoss;
  300. this.transport = object.transport;
  301. if (object.resolution) {
  302. this.resolution = object.resolution;
  303. }
  304. for (var quality in ConnectionIndicator.connectionQualityValues) {
  305. if (percent >= quality) {
  306. this.fullIcon.style.width =
  307. ConnectionIndicator.connectionQualityValues[quality];
  308. }
  309. }
  310. this.updatePopoverData();
  311. };
  312. /**
  313. * Updates the resolution
  314. * @param resolution the new resolution
  315. */
  316. ConnectionIndicator.prototype.updateResolution = function (resolution) {
  317. this.resolution = resolution;
  318. this.updatePopoverData();
  319. };
  320. /**
  321. * Updates the content of the popover
  322. */
  323. ConnectionIndicator.prototype.updatePopoverData = function () {
  324. this.popover.updateContent(
  325. "<div class=\"connection_info\">" + this.generateText() + "</div>");
  326. APP.translation.translateElement($(".connection_info"));
  327. };
  328. /**
  329. * Hides the popover
  330. */
  331. ConnectionIndicator.prototype.hide = function () {
  332. this.popover.forceHide();
  333. };
  334. /**
  335. * Hides the indicator
  336. */
  337. ConnectionIndicator.prototype.hideIndicator = function () {
  338. this.connectionIndicatorContainer.style.display = "none";
  339. if(this.popover)
  340. this.popover.forceHide();
  341. };
  342. module.exports = ConnectionIndicator;