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 14KB

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