pixel-react 1.15.31 → 1.15.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +75 -75
  2. package/lib/ThirdPartyPackages/JanusGateway.js +5 -2
  3. package/lib/ThirdPartyPackages/JanusGateway.js.map +1 -1
  4. package/lib/_virtual/index12.js +2 -2
  5. package/lib/_virtual/index9.js +2 -2
  6. package/lib/assets/icons/spinner.svg.js +1 -1
  7. package/lib/assets/icons/spinner.svg.js.map +1 -1
  8. package/lib/components/AppHeader/AppHeader.js +1 -1
  9. package/lib/components/AppHeader/AppHeader.js.map +1 -1
  10. package/lib/components/Excel/ExcelFile/ExcelFileComponents/reducer.js +67 -10
  11. package/lib/components/Excel/ExcelFile/ExcelFileComponents/reducer.js.map +1 -1
  12. package/lib/components/StepsLandingTable/StepLandingTable.js +4 -3
  13. package/lib/components/StepsLandingTable/StepLandingTable.js.map +1 -1
  14. package/lib/index.cjs +3288 -23
  15. package/lib/index.cjs.map +1 -1
  16. package/lib/index.js +1 -1
  17. package/lib/node_modules/janus-gateway/npm/dist/janus.es.js +3210 -0
  18. package/lib/node_modules/janus-gateway/npm/dist/janus.es.js.map +1 -0
  19. package/lib/node_modules/js-beautify/js/src/css/beautifier.js +1 -1
  20. package/lib/node_modules/js-beautify/js/src/css/options.js +1 -1
  21. package/lib/node_modules/js-beautify/js/src/html/index.js +1 -1
  22. package/lib/node_modules/js-beautify/js/src/html/options.js +1 -1
  23. package/lib/node_modules/js-beautify/js/src/javascript/beautifier.js +1 -1
  24. package/lib/node_modules/js-beautify/js/src/javascript/options.js +1 -1
  25. package/lib/node_modules/libphonenumber-js/metadata.min.json.js +4 -4
  26. package/lib/node_modules/libphonenumber-js/metadata.min.json.js.map +1 -1
  27. package/lib/node_modules/prop-types/index.js +1 -1
  28. package/package.json +109 -109
package/lib/index.cjs CHANGED
@@ -3,7 +3,6 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var React = require('react');
5
5
  var ReactDOM = require('react-dom');
6
- var Janus = require('janus-gateway');
7
6
 
8
7
  function _interopNamespaceDefault(e) {
9
8
  var n = Object.create(null);
@@ -18702,7 +18701,7 @@ const EditLabel = ({
18702
18701
  });
18703
18702
  };
18704
18703
 
18705
- const SvgSpinner = (props) => /* @__PURE__ */ React__namespace.createElement("svg", { width: "1em", height: "1em", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", ...props }, /* @__PURE__ */ React__namespace.createElement("style", null, "\r\n .spinner_DupU {\r\n animation: spinner_sM3D 1.2s infinite;\r\n fill: #71347b;\r\n }\r\n .spinner_GWtZ { animation-delay: .1s; }\r\n .spinner_dwN6 { animation-delay: .2s; }\r\n .spinner_46QP { animation-delay: .3s; }\r\n .spinner_PD82 { animation-delay: .4s; }\r\n .spinner_eUgh { animation-delay: .5s; }\r\n .spinner_eUaP { animation-delay: .6s; }\r\n .spinner_j38H { animation-delay: .7s; }\r\n .spinner_tVmX { animation-delay: .8s; }\r\n .spinner_DQhX { animation-delay: .9s; }\r\n .spinner_GIL4 { animation-delay: 1s; }\r\n .spinner_n0Yb { animation-delay: 1.1s; }\r\n\r\n @keyframes spinner_sM3D {\r\n 0%, 50% {\r\n animation-timing-function: cubic-bezier(0, 1, 0, 1);\r\n r: 0;\r\n }\r\n 10% {\r\n animation-timing-function: cubic-bezier(.53, 0, .61, .73);\r\n r: 2px;\r\n }\r\n }\r\n "), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU", cx: 12, cy: 3, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_GWtZ", cx: 16.5, cy: 4.21, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_n0Yb", cx: 7.5, cy: 4.21, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_dwN6", cx: 19.79, cy: 7.5, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_GIL4", cx: 4.21, cy: 7.5, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_46QP", cx: 21, cy: 12, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_DQhX", cx: 3, cy: 12, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_PD82", cx: 19.79, cy: 16.5, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_tVmX", cx: 4.21, cy: 16.5, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_eUgh", cx: 16.5, cy: 19.79, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_j38H", cx: 7.5, cy: 19.79, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_eUaP", cx: 12, cy: 21, r: 0 }));
18704
+ const SvgSpinner = (props) => /* @__PURE__ */ React__namespace.createElement("svg", { width: "1em", height: "1em", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", ...props }, /* @__PURE__ */ React__namespace.createElement("style", null, "\n .spinner_DupU {\n animation: spinner_sM3D 1.2s infinite;\n fill: #71347b;\n }\n .spinner_GWtZ { animation-delay: .1s; }\n .spinner_dwN6 { animation-delay: .2s; }\n .spinner_46QP { animation-delay: .3s; }\n .spinner_PD82 { animation-delay: .4s; }\n .spinner_eUgh { animation-delay: .5s; }\n .spinner_eUaP { animation-delay: .6s; }\n .spinner_j38H { animation-delay: .7s; }\n .spinner_tVmX { animation-delay: .8s; }\n .spinner_DQhX { animation-delay: .9s; }\n .spinner_GIL4 { animation-delay: 1s; }\n .spinner_n0Yb { animation-delay: 1.1s; }\n\n @keyframes spinner_sM3D {\n 0%, 50% {\n animation-timing-function: cubic-bezier(0, 1, 0, 1);\n r: 0;\n }\n 10% {\n animation-timing-function: cubic-bezier(.53, 0, .61, .73);\n r: 2px;\n }\n }\n "), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU", cx: 12, cy: 3, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_GWtZ", cx: 16.5, cy: 4.21, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_n0Yb", cx: 7.5, cy: 4.21, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_dwN6", cx: 19.79, cy: 7.5, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_GIL4", cx: 4.21, cy: 7.5, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_46QP", cx: 21, cy: 12, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_DQhX", cx: 3, cy: 12, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_PD82", cx: 19.79, cy: 16.5, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_tVmX", cx: 4.21, cy: 16.5, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_eUgh", cx: 16.5, cy: 19.79, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_j38H", cx: 7.5, cy: 19.79, r: 0 }), /* @__PURE__ */ React__namespace.createElement("circle", { className: "spinner_DupU spinner_eUaP", cx: 12, cy: 21, r: 0 }));
18706
18705
 
18707
18706
  const renderSpaces$1 = (level, parentSiblings = [], isLast, isContainer) => {
18708
18707
  let siblingsArray = parentSiblings;
@@ -33206,7 +33205,7 @@ const AppHeader = ({
33206
33205
  calculateVisibleItems();
33207
33206
  }, 800);
33208
33207
  return () => clearTimeout(timer);
33209
- }, [projectDetails?.id, selectedMenuItem, scriptId, quickMenuItems]);
33208
+ }, [projectDetails?.id, selectedMenuItem, scriptId, quickMenuItems, selectedSubMenu]);
33210
33209
  const checkIsHavingEntityPendingCounts = data => {
33211
33210
  if (data.label === 'Approval Request' && data.entityPendingCounts) {
33212
33211
  return Object.values(data.entityPendingCounts).some(count => count > 0);
@@ -40956,17 +40955,39 @@ function reducer(state, action) {
40956
40955
  if (checkEmpty(row ?? selectedRow)) {
40957
40956
  return state;
40958
40957
  }
40959
- selectedRow = row ?? selectedRow;
40958
+ const insertIndex = row ?? selectedRow;
40960
40959
  let updatedData = [...model.data];
40961
- const newRow = Array(updatedData[0]?.length || 0).fill(EmptyCell);
40962
- updatedData.splice(row ?? selectedRow, 0, newRow);
40960
+ const EmptyCellRow = Array(updatedData[0]?.length || 0).fill(EmptyCell);
40961
+ const newRow = model?.data?.[insertIndex]?.map(cell => typeof cell === 'object' && cell !== null ? {
40962
+ ...cell,
40963
+ value: '',
40964
+ readOnly: false,
40965
+ contextDisable: undefined,
40966
+ style: {
40967
+ ...cell.style,
40968
+ borderBottom: "",
40969
+ borderLeft: "",
40970
+ borderRight: "",
40971
+ borderTop: ""
40972
+ }
40973
+ } : cell);
40974
+ updatedData.splice(insertIndex, 0, newRow || EmptyCellRow);
40963
40975
  const updatedModel = new Model(model.createFormulaParser, updatedData);
40976
+ const newSelectedRow = insertIndex;
40977
+ const newSelectedColumn = selectedColumn ?? 0;
40978
+ const newActive = updatedData.length > 0 ? {
40979
+ row: newSelectedRow,
40980
+ column: newSelectedColumn
40981
+ } : null;
40982
+ const newSelected = updatedData.length > 0 ? new EntireRowsSelection(newSelectedRow, newSelectedRow) : new EmptySelection();
40964
40983
  return {
40965
40984
  ...state,
40966
40985
  model: updatedModel,
40967
- selectedRow: row ?? selectedRow,
40968
- selectedColumn: selectedColumn,
40969
- rowDimensions: insertRowDimension(rowDimensions, row ?? selectedRow, 32)
40986
+ selectedRow: insertIndex,
40987
+ selectedColumn: newSelectedColumn,
40988
+ selected: newSelected,
40989
+ active: newActive,
40990
+ rowDimensions: insertRowDimension(rowDimensions, insertIndex, 32)
40970
40991
  };
40971
40992
  }
40972
40993
  case ADD_ROW_BOTTOM:
@@ -40985,8 +41006,21 @@ function reducer(state, action) {
40985
41006
  }
40986
41007
  const insertIndex = (row ?? selectedRow) + 1;
40987
41008
  let updatedData = [...model.data];
40988
- const newRow = Array(updatedData[0]?.length || 0).fill(EmptyCell);
40989
- updatedData.splice(insertIndex, 0, newRow);
41009
+ const EmptyCellRow = Array(updatedData[0]?.length || 0).fill(EmptyCell);
41010
+ const newRow = model?.data?.[insertIndex - 1]?.map(cell => typeof cell === 'object' && cell !== null ? {
41011
+ ...cell,
41012
+ value: '',
41013
+ readOnly: false,
41014
+ contextDisable: undefined,
41015
+ style: {
41016
+ ...cell.style,
41017
+ borderBottom: "",
41018
+ borderLeft: "",
41019
+ borderRight: "",
41020
+ borderTop: ""
41021
+ }
41022
+ } : cell);
41023
+ updatedData.splice(insertIndex, 0, newRow ?? EmptyCellRow);
40990
41024
  const updatedModel = new Model(model.createFormulaParser, updatedData);
40991
41025
  const newSelectedRow = insertIndex;
40992
41026
  const newSelectedColumn = selectedColumn ?? 0;
@@ -41023,7 +41057,18 @@ function reducer(state, action) {
41023
41057
  const insertIndex = column ?? selectedColumn;
41024
41058
  let updatedData = [...model.data];
41025
41059
  updatedData = updatedData.map(row => {
41026
- return [...row.slice(0, insertIndex), EmptyCell, ...row.slice(insertIndex)];
41060
+ return [...row.slice(0, insertIndex), ...row.slice(insertIndex, insertIndex + 1).map(cell => typeof cell === 'object' && cell !== null ? {
41061
+ ...cell,
41062
+ value: '',
41063
+ readOnly: false,
41064
+ style: {
41065
+ ...cell.style,
41066
+ borderBottom: "",
41067
+ borderLeft: "",
41068
+ borderRight: "",
41069
+ borderTop: ""
41070
+ }
41071
+ } : cell), ...row.slice(insertIndex)];
41027
41072
  });
41028
41073
  const updatedModel = new Model(model.createFormulaParser, updatedData);
41029
41074
  return {
@@ -41052,7 +41097,18 @@ function reducer(state, action) {
41052
41097
  const insertIndex = (column ?? selectedColumn) + 1;
41053
41098
  let updatedData = [...model.data];
41054
41099
  updatedData = updatedData.map(row => {
41055
- return [...row.slice(0, insertIndex), EmptyCell, ...row.slice(insertIndex)];
41100
+ return [...row.slice(0, insertIndex), ...row.slice(insertIndex - 1, insertIndex).map(cell => typeof cell === 'object' && cell !== null ? {
41101
+ ...cell,
41102
+ value: '',
41103
+ readOnly: false,
41104
+ style: {
41105
+ ...cell.style,
41106
+ borderBottom: "",
41107
+ borderLeft: "",
41108
+ borderRight: "",
41109
+ borderTop: ""
41110
+ }
41111
+ } : cell), ...row.slice(insertIndex)];
41056
41112
  });
41057
41113
  const updatedModel = new Model(model.createFormulaParser, updatedData);
41058
41114
  const newSelectedRow = selectedRow ?? 0;
@@ -66051,7 +66107,7 @@ var metadata$1 = {
66051
66107
  "AR": ["54", "00", "(?:11|[89]\\d\\d)\\d{8}|[2368]\\d{9}", [10, 11], [["(\\d{4})(\\d{2})(\\d{4})", "$1 $2-$3", ["2(?:2[024-9]|3[0-59]|47|6[245]|9[02-8])|3(?:3[28]|4[03-9]|5[2-46-8]|7[1-578]|8[2-9])", "2(?:[23]02|6(?:[25]|4[6-8])|9(?:[02356]|4[02568]|72|8[23]))|3(?:3[28]|4(?:[04679]|3[5-8]|5[4-68]|8[2379])|5(?:[2467]|3[237]|8[2-5])|7[1-578]|8(?:[2469]|3[2578]|5[4-8]|7[36-8]|8[5-8]))|2(?:2[24-9]|3[1-59]|47)", "2(?:[23]02|6(?:[25]|4(?:64|[78]))|9(?:[02356]|4(?:[0268]|5[2-6])|72|8[23]))|3(?:3[28]|4(?:[04679]|3[78]|5(?:4[46]|8)|8[2379])|5(?:[2467]|3[237]|8[23])|7[1-578]|8(?:[2469]|3[278]|5[56][46]|86[3-6]))|2(?:2[24-9]|3[1-59]|47)|38(?:[58][78]|7[378])|3(?:4[35][56]|58[45]|8(?:[38]5|54|76))[4-6]", "2(?:[23]02|6(?:[25]|4(?:64|[78]))|9(?:[02356]|4(?:[0268]|5[2-6])|72|8[23]))|3(?:3[28]|4(?:[04679]|3(?:5(?:4[0-25689]|[56])|[78])|58|8[2379])|5(?:[2467]|3[237]|8(?:[23]|4(?:[45]|60)|5(?:4[0-39]|5|64)))|7[1-578]|8(?:[2469]|3[278]|54(?:4|5[13-7]|6[89])|86[3-6]))|2(?:2[24-9]|3[1-59]|47)|38(?:[58][78]|7[378])|3(?:454|85[56])[46]|3(?:4(?:36|5[56])|8(?:[38]5|76))[4-6]"], "0$1", 1], ["(\\d{2})(\\d{4})(\\d{4})", "$1 $2-$3", ["1"], "0$1", 1], ["(\\d{3})(\\d{3})(\\d{4})", "$1-$2-$3", ["[68]"], "0$1"], ["(\\d{3})(\\d{3})(\\d{4})", "$1 $2-$3", ["[23]"], "0$1", 1], ["(\\d)(\\d{4})(\\d{2})(\\d{4})", "$2 15-$3-$4", ["9(?:2[2-469]|3[3-578])", "9(?:2(?:2[024-9]|3[0-59]|47|6[245]|9[02-8])|3(?:3[28]|4[03-9]|5[2-46-8]|7[1-578]|8[2-9]))", "9(?:2(?:[23]02|6(?:[25]|4[6-8])|9(?:[02356]|4[02568]|72|8[23]))|3(?:3[28]|4(?:[04679]|3[5-8]|5[4-68]|8[2379])|5(?:[2467]|3[237]|8[2-5])|7[1-578]|8(?:[2469]|3[2578]|5[4-8]|7[36-8]|8[5-8])))|92(?:2[24-9]|3[1-59]|47)", "9(?:2(?:[23]02|6(?:[25]|4(?:64|[78]))|9(?:[02356]|4(?:[0268]|5[2-6])|72|8[23]))|3(?:3[28]|4(?:[04679]|3[78]|5(?:4[46]|8)|8[2379])|5(?:[2467]|3[237]|8[23])|7[1-578]|8(?:[2469]|3[278]|5(?:[56][46]|[78])|7[378]|8(?:6[3-6]|[78]))))|92(?:2[24-9]|3[1-59]|47)|93(?:4[35][56]|58[45]|8(?:[38]5|54|76))[4-6]", "9(?:2(?:[23]02|6(?:[25]|4(?:64|[78]))|9(?:[02356]|4(?:[0268]|5[2-6])|72|8[23]))|3(?:3[28]|4(?:[04679]|3(?:5(?:4[0-25689]|[56])|[78])|5(?:4[46]|8)|8[2379])|5(?:[2467]|3[237]|8(?:[23]|4(?:[45]|60)|5(?:4[0-39]|5|64)))|7[1-578]|8(?:[2469]|3[278]|5(?:4(?:4|5[13-7]|6[89])|[56][46]|[78])|7[378]|8(?:6[3-6]|[78]))))|92(?:2[24-9]|3[1-59]|47)|93(?:4(?:36|5[56])|8(?:[38]5|76))[4-6]"], "0$1", 0, "$1 $2 $3-$4"], ["(\\d)(\\d{2})(\\d{4})(\\d{4})", "$2 15-$3-$4", ["91"], "0$1", 0, "$1 $2 $3-$4"], ["(\\d{3})(\\d{3})(\\d{5})", "$1-$2-$3", ["8"], "0$1"], ["(\\d)(\\d{3})(\\d{3})(\\d{4})", "$2 15-$3-$4", ["9"], "0$1", 0, "$1 $2 $3-$4"]], "0", 0, "0?(?:(11|2(?:2(?:02?|[13]|2[13-79]|4[1-6]|5[2457]|6[124-8]|7[1-4]|8[13-6]|9[1267])|3(?:02?|1[467]|2[03-6]|3[13-8]|[49][2-6]|5[2-8]|[67])|4(?:7[3-578]|9)|6(?:[0136]|2[24-6]|4[6-8]?|5[15-8])|80|9(?:0[1-3]|[19]|2\\d|3[1-6]|4[02568]?|5[2-4]|6[2-46]|72?|8[23]?))|3(?:3(?:2[79]|6|8[2578])|4(?:0[0-24-9]|[12]|3[5-8]?|4[24-7]|5[4-68]?|6[02-9]|7[126]|8[2379]?|9[1-36-8])|5(?:1|2[1245]|3[237]?|4[1-46-9]|6[2-4]|7[1-6]|8[2-5]?)|6[24]|7(?:[069]|1[1568]|2[15]|3[145]|4[13]|5[14-8]|7[2-57]|8[126])|8(?:[01]|2[15-7]|3[2578]?|4[13-6]|5[4-8]?|6[1-357-9]|7[36-8]?|8[5-8]?|9[124])))15)?", "9$1"],
66052
66108
  "AS": ["1", "011", "(?:[58]\\d\\d|684|900)\\d{7}", [10], 0, "1", 0, "([267]\\d{6})$|1", "684$1", 0, "684"],
66053
66109
  "AT": ["43", "00", "1\\d{3,12}|2\\d{6,12}|43(?:(?:0\\d|5[02-9])\\d{3,9}|2\\d{4,5}|[3467]\\d{4}|8\\d{4,6}|9\\d{4,7})|5\\d{4,12}|8\\d{7,12}|9\\d{8,12}|(?:[367]\\d|4[0-24-9])\\d{4,11}", [4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [["(\\d)(\\d{3,12})", "$1 $2", ["1(?:11|[2-9])"], "0$1"], ["(\\d{3})(\\d{2})", "$1 $2", ["517"], "0$1"], ["(\\d{2})(\\d{3,5})", "$1 $2", ["5[079]"], "0$1"], ["(\\d{3})(\\d{3,10})", "$1 $2", ["(?:31|4)6|51|6(?:48|5[0-3579]|[6-9])|7(?:20|32|8)|[89]", "(?:31|4)6|51|6(?:485|5[0-3579]|[6-9])|7(?:20|32|8)|[89]"], "0$1"], ["(\\d{4})(\\d{3,9})", "$1 $2", ["[2-467]|5[2-6]"], "0$1"], ["(\\d{2})(\\d{3})(\\d{3,4})", "$1 $2 $3", ["5"], "0$1"], ["(\\d{2})(\\d{4})(\\d{4,7})", "$1 $2 $3", ["5"], "0$1"]], "0"],
66054
- "AU": ["61", "001[14-689]|14(?:1[14]|34|4[17]|[56]6|7[47]|88)0011", "1(?:[0-79]\\d{7}(?:\\d(?:\\d{2})?)?|8[0-24-9]\\d{7})|[2-478]\\d{8}|1\\d{4,7}", [5, 6, 7, 8, 9, 10, 12], [["(\\d{2})(\\d{3,4})", "$1 $2", ["16"], "0$1"], ["(\\d{2})(\\d{3})(\\d{2,4})", "$1 $2 $3", ["16"], "0$1"], ["(\\d{3})(\\d{3})(\\d{3})", "$1 $2 $3", ["14|4"], "0$1"], ["(\\d)(\\d{4})(\\d{4})", "$1 $2 $3", ["[2378]"], "(0$1)"], ["(\\d{4})(\\d{3})(\\d{3})", "$1 $2 $3", ["1(?:30|[89])"]]], "0", 0, "(183[12])|0", 0, 0, 0, [["(?:(?:2(?:(?:[0-26-9]\\d|3[0-8]|5[0135-9])\\d|4(?:[02-9]\\d|10))|3(?:(?:[0-3589]\\d|6[1-9]|7[0-35-9])\\d|4(?:[0-578]\\d|90))|7(?:[013-57-9]\\d|2[0-8])\\d)\\d\\d|8(?:51(?:0(?:0[03-9]|[12479]\\d|3[2-9]|5[0-8]|6[1-9]|8[0-7])|1(?:[0235689]\\d|1[0-69]|4[0-589]|7[0-47-9])|2(?:0[0-79]|[18][13579]|2[14-9]|3[0-46-9]|[4-6]\\d|7[89]|9[0-4])|[34]\\d\\d)|(?:6[0-8]|[78]\\d)\\d{3}|9(?:[02-9]\\d{3}|1(?:(?:[0-58]\\d|6[0135-9])\\d|7(?:0[0-24-9]|[1-9]\\d)|9(?:[0-46-9]\\d|5[0-79])))))\\d{3}", [9]], ["4(?:79[01]|83[0-389]|94[0-478])\\d{5}|4(?:[0-36]\\d|4[047-9]|[58][0-24-9]|7[02-8]|9[0-37-9])\\d{6}", [9]], ["180(?:0\\d{3}|2)\\d{3}", [7, 10]], ["190[0-26]\\d{6}", [10]], 0, 0, 0, ["163\\d{2,6}", [5, 6, 7, 8, 9]], ["14(?:5(?:1[0458]|[23][458])|71\\d)\\d{4}", [9]], ["13(?:00\\d{6}(?:\\d{2})?|45[0-4]\\d{3})|13\\d{4}", [6, 8, 10, 12]]], "0011"],
66110
+ "AU": ["61", "001[14-689]|14(?:1[14]|34|4[17]|[56]6|7[47]|88)0011", "1(?:[0-79]\\d{7}(?:\\d(?:\\d{2})?)?|8[0-24-9]\\d{7})|[2-478]\\d{8}|1\\d{4,7}", [5, 6, 7, 8, 9, 10, 12], [["(\\d{2})(\\d{3,4})", "$1 $2", ["16"], "0$1"], ["(\\d{2})(\\d{3})(\\d{2,4})", "$1 $2 $3", ["16"], "0$1"], ["(\\d{3})(\\d{3})(\\d{3})", "$1 $2 $3", ["14|4"], "0$1"], ["(\\d)(\\d{4})(\\d{4})", "$1 $2 $3", ["[2378]"], "(0$1)"], ["(\\d{4})(\\d{3})(\\d{3})", "$1 $2 $3", ["1(?:30|[89])"]]], "0", 0, "(183[12])|0", 0, 0, 0, [["(?:(?:2(?:(?:[0-26-9]\\d|3[0-8]|5[0135-9])\\d|4(?:[02-9]\\d|10))|3(?:(?:[0-3589]\\d|6[1-9]|7[0-35-9])\\d|4(?:[0-578]\\d|90))|7(?:[013-57-9]\\d|2[0-8])\\d)\\d\\d|8(?:51(?:0(?:0[03-9]|[12479]\\d|3[2-9]|5[0-8]|6[1-9]|8[0-7])|1(?:[0235689]\\d|1[0-69]|4[0-589]|7[0-47-9])|2(?:0[0-79]|[18][13579]|2[14-9]|3[0-46-9]|[4-6]\\d|7[89]|9[0-4])|[34]\\d\\d)|(?:6[0-8]|[78]\\d)\\d{3}|9(?:[02-9]\\d{3}|1(?:(?:[0-58]\\d|6[0135-9])\\d|7(?:0[0-24-9]|[1-9]\\d)|9(?:[0-46-9]\\d|5[0-79])))))\\d{3}", [9]], ["4(?:79[01]|83[0-36-9])\\d{5}|4(?:[0-36]\\d|4[047-9]|[58][0-24-9]|7[02-8]|9[0-47-9])\\d{6}", [9]], ["180(?:0\\d{3}|2)\\d{3}", [7, 10]], ["190[0-26]\\d{6}", [10]], 0, 0, 0, ["163\\d{2,6}", [5, 6, 7, 8, 9]], ["14(?:5(?:1[0458]|[23][458])|71\\d)\\d{4}", [9]], ["13(?:00\\d{6}(?:\\d{2})?|45[0-4]\\d{3})|13\\d{4}", [6, 8, 10, 12]]], "0011"],
66055
66111
  "AW": ["297", "00", "(?:[25-79]\\d\\d|800)\\d{4}", [7], [["(\\d{3})(\\d{4})", "$1 $2", ["[25-9]"]]]],
66056
66112
  "AX": ["358", "00|99(?:[01469]|5(?:[14]1|3[23]|5[59]|77|88|9[09]))", "2\\d{4,9}|35\\d{4,5}|(?:60\\d\\d|800)\\d{4,6}|7\\d{5,11}|(?:[14]\\d|3[0-46-9]|50)\\d{4,8}", [5, 6, 7, 8, 9, 10, 11, 12], 0, "0", 0, 0, 0, 0, "18", 0, "00"],
66057
66113
  "AZ": ["994", "00", "365\\d{6}|(?:[124579]\\d|60|88)\\d{7}", [9], [["(\\d{3})(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["90"], "0$1"], ["(\\d{2})(\\d{3})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["1[28]|2|365|46", "1[28]|2|365[45]|46", "1[28]|2|365(?:4|5[02])|46"], "(0$1)"], ["(\\d{2})(\\d{3})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["[13-9]"], "0$1"]], "0"],
@@ -66076,7 +66132,7 @@ var metadata$1 = {
66076
66132
  "BY": ["375", "810", "(?:[12]\\d|33|44|902)\\d{7}|8(?:0[0-79]\\d{5,7}|[1-7]\\d{9})|8(?:1[0-489]|[5-79]\\d)\\d{7}|8[1-79]\\d{6,7}|8[0-79]\\d{5}|8\\d{5}", [6, 7, 8, 9, 10, 11], [["(\\d{3})(\\d{3})", "$1 $2", ["800"], "8 $1"], ["(\\d{3})(\\d{2})(\\d{2,4})", "$1 $2 $3", ["800"], "8 $1"], ["(\\d{4})(\\d{2})(\\d{3})", "$1 $2-$3", ["1(?:5[169]|6[3-5]|7[179])|2(?:1[35]|2[34]|3[3-5])", "1(?:5[169]|6(?:3[1-3]|4|5[125])|7(?:1[3-9]|7[0-24-6]|9[2-7]))|2(?:1[35]|2[34]|3[3-5])"], "8 0$1"], ["(\\d{3})(\\d{2})(\\d{2})(\\d{2})", "$1 $2-$3-$4", ["1(?:[56]|7[467])|2[1-3]"], "8 0$1"], ["(\\d{2})(\\d{3})(\\d{2})(\\d{2})", "$1 $2-$3-$4", ["[1-4]"], "8 0$1"], ["(\\d{3})(\\d{3,4})(\\d{4})", "$1 $2 $3", ["[89]"], "8 $1"]], "8", 0, "0|80?", 0, 0, 0, 0, "8~10"],
66077
66133
  "BZ": ["501", "00", "(?:0800\\d|[2-8])\\d{6}", [7, 11], [["(\\d{3})(\\d{4})", "$1-$2", ["[2-8]"]], ["(\\d)(\\d{3})(\\d{4})(\\d{3})", "$1-$2-$3-$4", ["0"]]]],
66078
66134
  "CA": ["1", "011", "[2-9]\\d{9}|3\\d{6}", [7, 10], 0, "1", 0, 0, 0, 0, 0, [["(?:2(?:04|[23]6|[48]9|5[07]|63)|3(?:06|43|54|6[578]|82)|4(?:03|1[68]|[26]8|3[178]|50|74)|5(?:06|1[49]|48|79|8[147])|6(?:04|[18]3|39|47|72)|7(?:0[59]|42|53|78|8[02])|8(?:[06]7|19|25|7[39])|9(?:0[25]|42))[2-9]\\d{6}", [10]], ["", [10]], ["8(?:00|33|44|55|66|77|88)[2-9]\\d{6}", [10]], ["900[2-9]\\d{6}", [10]], ["52(?:3(?:[2-46-9][02-9]\\d|5(?:[02-46-9]\\d|5[0-46-9]))|4(?:[2-478][02-9]\\d|5(?:[034]\\d|2[024-9]|5[0-46-9])|6(?:0[1-9]|[2-9]\\d)|9(?:[05-9]\\d|2[0-5]|49)))\\d{4}|52[34][2-9]1[02-9]\\d{4}|(?:5(?:2[125-9]|33|44|66|77|88)|6(?:22|33))[2-9]\\d{6}", [10]], 0, ["310\\d{4}", [7]], 0, ["600[2-9]\\d{6}", [10]]]],
66079
- "CC": ["61", "001[14-689]|14(?:1[14]|34|4[17]|[56]6|7[47]|88)0011", "1(?:[0-79]\\d{8}(?:\\d{2})?|8[0-24-9]\\d{7})|[148]\\d{8}|1\\d{5,7}", [6, 7, 8, 9, 10, 12], 0, "0", 0, "([59]\\d{7})$|0", "8$1", 0, 0, [["8(?:51(?:0(?:02|31|60|89)|1(?:18|76)|223)|91(?:0(?:1[0-2]|29)|1(?:[28]2|50|79)|2(?:10|64)|3(?:[06]8|22)|4[29]8|62\\d|70[23]|959))\\d{3}", [9]], ["4(?:79[01]|83[0-389]|94[0-478])\\d{5}|4(?:[0-36]\\d|4[047-9]|[58][0-24-9]|7[02-8]|9[0-37-9])\\d{6}", [9]], ["180(?:0\\d{3}|2)\\d{3}", [7, 10]], ["190[0-26]\\d{6}", [10]], 0, 0, 0, 0, ["14(?:5(?:1[0458]|[23][458])|71\\d)\\d{4}", [9]], ["13(?:00\\d{6}(?:\\d{2})?|45[0-4]\\d{3})|13\\d{4}", [6, 8, 10, 12]]], "0011"],
66135
+ "CC": ["61", "001[14-689]|14(?:1[14]|34|4[17]|[56]6|7[47]|88)0011", "1(?:[0-79]\\d{8}(?:\\d{2})?|8[0-24-9]\\d{7})|[148]\\d{8}|1\\d{5,7}", [6, 7, 8, 9, 10, 12], 0, "0", 0, "([59]\\d{7})$|0", "8$1", 0, 0, [["8(?:51(?:0(?:02|31|60|89)|1(?:18|76)|223)|91(?:0(?:1[0-2]|29)|1(?:[28]2|50|79)|2(?:10|64)|3(?:[06]8|22)|4[29]8|62\\d|70[23]|959))\\d{3}", [9]], ["4(?:79[01]|83[0-36-9])\\d{5}|4(?:[0-36]\\d|4[047-9]|[58][0-24-9]|7[02-8]|9[0-47-9])\\d{6}", [9]], ["180(?:0\\d{3}|2)\\d{3}", [7, 10]], ["190[0-26]\\d{6}", [10]], 0, 0, 0, 0, ["14(?:5(?:1[0458]|[23][458])|71\\d)\\d{4}", [9]], ["13(?:00\\d{6}(?:\\d{2})?|45[0-4]\\d{3})|13\\d{4}", [6, 8, 10, 12]]], "0011"],
66080
66136
  "CD": ["243", "00", "(?:(?:[189]|5\\d)\\d|2)\\d{7}|[1-68]\\d{6}", [7, 8, 9, 10], [["(\\d{2})(\\d{2})(\\d{3})", "$1 $2 $3", ["88"], "0$1"], ["(\\d{2})(\\d{5})", "$1 $2", ["[1-6]"], "0$1"], ["(\\d{2})(\\d{2})(\\d{4})", "$1 $2 $3", ["2"], "0$1"], ["(\\d{2})(\\d{3})(\\d{4})", "$1 $2 $3", ["1"], "0$1"], ["(\\d{3})(\\d{3})(\\d{3})", "$1 $2 $3", ["[89]"], "0$1"], ["(\\d{2})(\\d{2})(\\d{3})(\\d{3})", "$1 $2 $3 $4", ["5"], "0$1"]], "0"],
66081
66137
  "CF": ["236", "00", "(?:[27]\\d{3}|8776)\\d{4}", [8], [["(\\d{2})(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["[278]"]]]],
66082
66138
  "CG": ["242", "00", "222\\d{6}|(?:0\\d|80)\\d{7}", [9], [["(\\d)(\\d{4})(\\d{4})", "$1 $2 $3", ["8"]], ["(\\d{2})(\\d{3})(\\d{4})", "$1 $2 $3", ["[02]"]]]],
@@ -66091,7 +66147,7 @@ var metadata$1 = {
66091
66147
  "CU": ["53", "119", "(?:[2-7]|8\\d\\d)\\d{7}|[2-47]\\d{6}|[34]\\d{5}", [6, 7, 8, 10], [["(\\d{2})(\\d{4,6})", "$1 $2", ["2[1-4]|[34]"], "(0$1)"], ["(\\d)(\\d{6,7})", "$1 $2", ["7"], "(0$1)"], ["(\\d)(\\d{7})", "$1 $2", ["[56]"], "0$1"], ["(\\d{3})(\\d{7})", "$1 $2", ["8"], "0$1"]], "0"],
66092
66148
  "CV": ["238", "0", "(?:[2-59]\\d\\d|800)\\d{4}", [7], [["(\\d{3})(\\d{2})(\\d{2})", "$1 $2 $3", ["[2-589]"]]]],
66093
66149
  "CW": ["599", "00", "(?:[34]1|60|(?:7|9\\d)\\d)\\d{5}", [7, 8], [["(\\d{3})(\\d{4})", "$1 $2", ["[3467]"]], ["(\\d)(\\d{3})(\\d{4})", "$1 $2 $3", ["9[4-8]"]]], 0, 0, 0, 0, 0, "[69]"],
66094
- "CX": ["61", "001[14-689]|14(?:1[14]|34|4[17]|[56]6|7[47]|88)0011", "1(?:[0-79]\\d{8}(?:\\d{2})?|8[0-24-9]\\d{7})|[148]\\d{8}|1\\d{5,7}", [6, 7, 8, 9, 10, 12], 0, "0", 0, "([59]\\d{7})$|0", "8$1", 0, 0, [["8(?:51(?:0(?:01|30|59|88)|1(?:17|46|75)|2(?:22|35))|91(?:00[6-9]|1(?:[28]1|49|78)|2(?:09|63)|3(?:12|26|75)|4(?:56|97)|64\\d|7(?:0[01]|1[0-2])|958))\\d{3}", [9]], ["4(?:79[01]|83[0-389]|94[0-478])\\d{5}|4(?:[0-36]\\d|4[047-9]|[58][0-24-9]|7[02-8]|9[0-37-9])\\d{6}", [9]], ["180(?:0\\d{3}|2)\\d{3}", [7, 10]], ["190[0-26]\\d{6}", [10]], 0, 0, 0, 0, ["14(?:5(?:1[0458]|[23][458])|71\\d)\\d{4}", [9]], ["13(?:00\\d{6}(?:\\d{2})?|45[0-4]\\d{3})|13\\d{4}", [6, 8, 10, 12]]], "0011"],
66150
+ "CX": ["61", "001[14-689]|14(?:1[14]|34|4[17]|[56]6|7[47]|88)0011", "1(?:[0-79]\\d{8}(?:\\d{2})?|8[0-24-9]\\d{7})|[148]\\d{8}|1\\d{5,7}", [6, 7, 8, 9, 10, 12], 0, "0", 0, "([59]\\d{7})$|0", "8$1", 0, 0, [["8(?:51(?:0(?:01|30|59|88)|1(?:17|46|75)|2(?:22|35))|91(?:00[6-9]|1(?:[28]1|49|78)|2(?:09|63)|3(?:12|26|75)|4(?:56|97)|64\\d|7(?:0[01]|1[0-2])|958))\\d{3}", [9]], ["4(?:79[01]|83[0-36-9])\\d{5}|4(?:[0-36]\\d|4[047-9]|[58][0-24-9]|7[02-8]|9[0-47-9])\\d{6}", [9]], ["180(?:0\\d{3}|2)\\d{3}", [7, 10]], ["190[0-26]\\d{6}", [10]], 0, 0, 0, 0, ["14(?:5(?:1[0458]|[23][458])|71\\d)\\d{4}", [9]], ["13(?:00\\d{6}(?:\\d{2})?|45[0-4]\\d{3})|13\\d{4}", [6, 8, 10, 12]]], "0011"],
66095
66151
  "CY": ["357", "00", "(?:[279]\\d|[58]0)\\d{6}", [8], [["(\\d{2})(\\d{6})", "$1 $2", ["[257-9]"]]]],
66096
66152
  "CZ": ["420", "00", "(?:[2-578]\\d|60)\\d{7}|9\\d{8,11}", [9, 10, 11, 12], [["(\\d{3})(\\d{3})(\\d{3})", "$1 $2 $3", ["[2-8]|9[015-7]"]], ["(\\d{2})(\\d{3})(\\d{3})(\\d{2})", "$1 $2 $3 $4", ["96"]], ["(\\d{2})(\\d{3})(\\d{3})(\\d{3})", "$1 $2 $3 $4", ["9"]], ["(\\d{3})(\\d{3})(\\d{3})(\\d{3})", "$1 $2 $3 $4", ["9"]]]],
66097
66153
  "DE": ["49", "00", "[2579]\\d{5,14}|49(?:[34]0|69|8\\d)\\d\\d?|49(?:37|49|60|7[089]|9\\d)\\d{1,3}|49(?:2[024-9]|3[2-689]|7[1-7])\\d{1,8}|(?:1|[368]\\d|4[0-8])\\d{3,13}|49(?:[015]\\d|2[13]|31|[46][1-8])\\d{1,9}", [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [["(\\d{2})(\\d{3,13})", "$1 $2", ["3[02]|40|[68]9"], "0$1"], ["(\\d{3})(\\d{3,12})", "$1 $2", ["2(?:0[1-389]|1[124]|2[18]|3[14])|3(?:[35-9][15]|4[015])|906|(?:2[4-9]|4[2-9]|[579][1-9]|[68][1-8])1", "2(?:0[1-389]|12[0-8])|3(?:[35-9][15]|4[015])|906|2(?:[13][14]|2[18])|(?:2[4-9]|4[2-9]|[579][1-9]|[68][1-8])1"], "0$1"], ["(\\d{4})(\\d{2,11})", "$1 $2", ["[24-6]|3(?:[3569][02-46-9]|4[2-4679]|7[2-467]|8[2-46-8])|70[2-8]|8(?:0[2-9]|[1-8])|90[7-9]|[79][1-9]", "[24-6]|3(?:3(?:0[1-467]|2[127-9]|3[124578]|7[1257-9]|8[1256]|9[145])|4(?:2[135]|4[13578]|9[1346])|5(?:0[14]|2[1-3589]|6[1-4]|7[13468]|8[13568])|6(?:2[1-489]|3[124-6]|6[13]|7[12579]|8[1-356]|9[135])|7(?:2[1-7]|4[145]|6[1-5]|7[1-4])|8(?:21|3[1468]|6|7[1467]|8[136])|9(?:0[12479]|2[1358]|4[134679]|6[1-9]|7[136]|8[147]|9[1468]))|70[2-8]|8(?:0[2-9]|[1-8])|90[7-9]|[79][1-9]|3[68]4[1347]|3(?:47|60)[1356]|3(?:3[46]|46|5[49])[1246]|3[4579]3[1357]"], "0$1"], ["(\\d{3})(\\d{4})", "$1 $2", ["138"], "0$1"], ["(\\d{5})(\\d{2,10})", "$1 $2", ["3"], "0$1"], ["(\\d{3})(\\d{5,11})", "$1 $2", ["181"], "0$1"], ["(\\d{3})(\\d)(\\d{4,10})", "$1 $2 $3", ["1(?:3|80)|9"], "0$1"], ["(\\d{3})(\\d{7,8})", "$1 $2", ["1[67]"], "0$1"], ["(\\d{3})(\\d{7,12})", "$1 $2", ["8"], "0$1"], ["(\\d{5})(\\d{6})", "$1 $2", ["185", "1850", "18500"], "0$1"], ["(\\d{3})(\\d{4})(\\d{4})", "$1 $2 $3", ["7"], "0$1"], ["(\\d{4})(\\d{7})", "$1 $2", ["18[68]"], "0$1"], ["(\\d{4})(\\d{7})", "$1 $2", ["15[1279]"], "0$1"], ["(\\d{5})(\\d{6})", "$1 $2", ["15[03568]", "15(?:[0568]|3[13])"], "0$1"], ["(\\d{3})(\\d{8})", "$1 $2", ["18"], "0$1"], ["(\\d{3})(\\d{2})(\\d{7,8})", "$1 $2 $3", ["1(?:6[023]|7)"], "0$1"], ["(\\d{4})(\\d{2})(\\d{7})", "$1 $2 $3", ["15[279]"], "0$1"], ["(\\d{3})(\\d{2})(\\d{8})", "$1 $2 $3", ["15"], "0$1"]], "0"],
@@ -66114,7 +66170,7 @@ var metadata$1 = {
66114
66170
  "FO": ["298", "00", "[2-9]\\d{5}", [6], [["(\\d{6})", "$1", ["[2-9]"]]], 0, 0, "(10(?:01|[12]0|88))"],
66115
66171
  "FR": ["33", "00", "[1-9]\\d{8}", [9], [["(\\d{3})(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["8"], "0 $1"], ["(\\d)(\\d{2})(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4 $5", ["[1-79]"], "0$1"]], "0"],
66116
66172
  "GA": ["241", "00", "(?:[067]\\d|11)\\d{6}|[2-7]\\d{6}", [7, 8], [["(\\d)(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["[2-7]"], "0$1"], ["(\\d{2})(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["0"]], ["(\\d{2})(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["11|[67]"], "0$1"]], 0, 0, "0(11\\d{6}|60\\d{6}|61\\d{6}|6[256]\\d{6}|7[467]\\d{6})", "$1"],
66117
- "GB": ["44", "00", "[1-357-9]\\d{9}|[18]\\d{8}|8\\d{6}", [7, 9, 10], [["(\\d{3})(\\d{4})", "$1 $2", ["800", "8001", "80011", "800111", "8001111"], "0$1"], ["(\\d{3})(\\d{2})(\\d{2})", "$1 $2 $3", ["845", "8454", "84546", "845464"], "0$1"], ["(\\d{3})(\\d{6})", "$1 $2", ["800"], "0$1"], ["(\\d{5})(\\d{4,5})", "$1 $2", ["1(?:38|5[23]|69|76|94)", "1(?:(?:38|69)7|5(?:24|39)|768|946)", "1(?:3873|5(?:242|39[4-6])|(?:697|768)[347]|9467)"], "0$1"], ["(\\d{4})(\\d{5,6})", "$1 $2", ["1(?:[2-69][02-9]|[78])"], "0$1"], ["(\\d{2})(\\d{4})(\\d{4})", "$1 $2 $3", ["[25]|7(?:0|6[02-9])", "[25]|7(?:0|6(?:[03-9]|2[356]))"], "0$1"], ["(\\d{4})(\\d{6})", "$1 $2", ["7"], "0$1"], ["(\\d{3})(\\d{3})(\\d{4})", "$1 $2 $3", ["[1389]"], "0$1"]], "0", 0, "0|180020", 0, 0, 0, [["(?:1(?:1(?:3(?:[0-58]\\d\\d|73[0-35])|4(?:(?:[0-5]\\d|70)\\d|69[7-9])|(?:(?:5[0-26-9]|[78][0-49])\\d|6(?:[0-4]\\d|50))\\d)|(?:2(?:(?:0[024-9]|2[3-9]|3[3-79]|4[1-689]|[58][02-9]|6[0-47-9]|7[013-9]|9\\d)\\d|1(?:[0-7]\\d|8[0-3]))|(?:3(?:0\\d|1[0-8]|[25][02-9]|3[02-579]|[468][0-46-9]|7[1-35-79]|9[2-578])|4(?:0[03-9]|[137]\\d|[28][02-57-9]|4[02-69]|5[0-8]|[69][0-79])|5(?:0[1-35-9]|[16]\\d|2[024-9]|3[015689]|4[02-9]|5[03-9]|7[0-35-9]|8[0-468]|9[0-57-9])|6(?:0[034689]|1\\d|2[0-35689]|[38][013-9]|4[1-467]|5[0-69]|6[13-9]|7[0-8]|9[0-24578])|7(?:0[0246-9]|2\\d|3[0236-8]|4[03-9]|5[0-46-9]|6[013-9]|7[0-35-9]|8[024-9]|9[02-9])|8(?:0[35-9]|2[1-57-9]|3[02-578]|4[0-578]|5[124-9]|6[2-69]|7\\d|8[02-9]|9[02569])|9(?:0[02-589]|[18]\\d|2[02-689]|3[1-57-9]|4[2-9]|5[0-579]|6[2-47-9]|7[0-24578]|9[2-57]))\\d)\\d)|2(?:0[013478]|3[0189]|4[017]|8[0-46-9]|9[0-2])\\d{3})\\d{4}|1(?:2(?:0(?:46[1-4]|87[2-9])|545[1-79]|76(?:2\\d|3[1-8]|6[1-6])|9(?:7(?:2[0-4]|3[2-5])|8(?:2[2-8]|7[0-47-9]|8[3-5])))|3(?:6(?:38[2-5]|47[23])|8(?:47[04-9]|64[0157-9]))|4(?:044[1-7]|20(?:2[23]|8\\d)|6(?:0(?:30|5[2-57]|6[1-8]|7[2-8])|140)|8(?:052|87[1-3]))|5(?:2(?:4(?:3[2-79]|6\\d)|76\\d)|6(?:26[06-9]|686))|6(?:06(?:4\\d|7[4-79])|295[5-7]|35[34]\\d|47(?:24|61)|59(?:5[08]|6[67]|74)|9(?:55[0-4]|77[23]))|7(?:26(?:6[13-9]|7[0-7])|(?:442|688)\\d|50(?:2[0-3]|[3-68]2|76))|8(?:27[56]\\d|37(?:5[2-5]|8[239])|843[2-58])|9(?:0(?:0(?:6[1-8]|85)|52\\d)|3583|4(?:66[1-8]|9(?:2[01]|81))|63(?:23|3[1-4])|9561))\\d{3}", [9, 10]], ["7(?:457[0-57-9]|700[01]|911[028])\\d{5}|7(?:[1-3]\\d\\d|4(?:[0-46-9]\\d|5[0-689])|5(?:0[0-8]|[13-9]\\d|2[0-35-9])|7(?:0[1-9]|[1-7]\\d|8[02-9]|9[0-689])|8(?:[014-9]\\d|[23][0-8])|9(?:[024-9]\\d|1[02-9]|3[0-689]))\\d{6}", [10]], ["80[08]\\d{7}|800\\d{6}|8001111"], ["(?:8(?:4[2-5]|7[0-3])|9(?:[01]\\d|8[2-49]))\\d{7}|845464\\d", [7, 10]], ["70\\d{8}", [10]], 0, ["(?:3[0347]|55)\\d{8}", [10]], ["76(?:464|652)\\d{5}|76(?:0[0-28]|2[356]|34|4[01347]|5[49]|6[0-369]|77|8[14]|9[139])\\d{6}", [10]], ["56\\d{8}", [10]]], 0, " x"],
66173
+ "GB": ["44", "00", "[1-357-9]\\d{9}|[18]\\d{8}|8\\d{6}", [7, 9, 10], [["(\\d{3})(\\d{4})", "$1 $2", ["800", "8001", "80011", "800111", "8001111"], "0$1"], ["(\\d{3})(\\d{2})(\\d{2})", "$1 $2 $3", ["845", "8454", "84546", "845464"], "0$1"], ["(\\d{3})(\\d{6})", "$1 $2", ["800"], "0$1"], ["(\\d{5})(\\d{4,5})", "$1 $2", ["1(?:38|5[23]|69|76|94)", "1(?:(?:38|69)7|5(?:24|39)|768|946)", "1(?:3873|5(?:242|39[4-6])|(?:697|768)[347]|9467)"], "0$1"], ["(\\d{4})(\\d{5,6})", "$1 $2", ["1(?:[2-69][02-9]|[78])"], "0$1"], ["(\\d{2})(\\d{4})(\\d{4})", "$1 $2 $3", ["[25]|7(?:0|6[02-9])", "[25]|7(?:0|6(?:[03-9]|2[356]))"], "0$1"], ["(\\d{4})(\\d{6})", "$1 $2", ["7"], "0$1"], ["(\\d{3})(\\d{3})(\\d{4})", "$1 $2 $3", ["[1389]"], "0$1"]], "0", 0, "0|180020", 0, 0, 0, [["(?:1(?:1(?:3(?:[0-58]\\d\\d|73[0-5])|4(?:(?:[0-5]\\d|70)\\d|69[7-9])|(?:(?:5[0-26-9]|[78][0-49])\\d|6(?:[0-4]\\d|5[01]))\\d)|(?:2(?:(?:0[024-9]|2[3-9]|3[3-79]|4[1-689]|[58][02-9]|6[0-47-9]|7[013-9]|9\\d)\\d|1(?:[0-7]\\d|8[0-3]))|(?:3(?:0\\d|1[0-8]|[25][02-9]|3[02-579]|[468][0-46-9]|7[1-35-79]|9[2-578])|4(?:0[03-9]|[137]\\d|[28][02-57-9]|4[02-69]|5[0-8]|[69][0-79])|5(?:0[1-35-9]|[16]\\d|2[024-9]|3[015689]|4[02-9]|5[03-9]|7[0-35-9]|8[0-468]|9[0-57-9])|6(?:0[034689]|1\\d|2[0-35689]|[38][013-9]|4[1-467]|5[0-69]|6[13-9]|7[0-8]|9[0-24578])|7(?:0[0246-9]|2\\d|3[0236-8]|4[03-9]|5[0-46-9]|6[013-9]|7[0-35-9]|8[024-9]|9[02-9])|8(?:0[35-9]|2[1-57-9]|3[02-578]|4[0-578]|5[124-9]|6[2-69]|7\\d|8[02-9]|9[02569])|9(?:0[02-589]|[18]\\d|2[02-689]|3[1-57-9]|4[2-9]|5[0-579]|6[2-47-9]|7[0-24578]|9[2-57]))\\d)\\d)|2(?:0[013478]|3[0189]|4[017]|8[0-46-9]|9[0-2])\\d{3})\\d{4}|1(?:2(?:0(?:46[1-4]|87[2-9])|545[1-79]|76(?:2\\d|3[1-8]|6[1-6])|9(?:7(?:2[0-4]|3[2-5])|8(?:2[2-8]|7[0-47-9]|8[3-5])))|3(?:6(?:38[2-5]|47[23])|8(?:47[04-9]|64[0157-9]))|4(?:044[1-7]|20(?:2[23]|8\\d)|6(?:0(?:30|5[2-57]|6[1-8]|7[2-8])|140)|8(?:052|87[1-3]))|5(?:2(?:4(?:3[2-79]|6\\d)|76\\d)|6(?:26[06-9]|686))|6(?:06(?:4\\d|7[4-79])|295[5-7]|35[34]\\d|47(?:24|61)|59(?:5[08]|6[67]|74)|9(?:55[0-4]|77[23]))|7(?:26(?:6[13-9]|7[0-7])|(?:442|688)\\d|50(?:2[0-3]|[3-68]2|76))|8(?:27[56]\\d|37(?:5[2-5]|8[239])|843[2-58])|9(?:0(?:0(?:6[1-8]|85)|52\\d)|3583|4(?:66[1-8]|9(?:2[01]|81))|63(?:23|3[1-4])|9561))\\d{3}", [9, 10]], ["7(?:457[0-57-9]|700[01]|911[028])\\d{5}|7(?:[1-3]\\d\\d|4(?:[0-46-9]\\d|5[0-689])|5(?:0[0-8]|[13-9]\\d|2[0-35-9])|7(?:0[1-9]|[1-7]\\d|8[02-9]|9[0-689])|8(?:[014-9]\\d|[23][0-8])|9(?:[024-9]\\d|1[02-9]|3[0-689]))\\d{6}", [10]], ["80[08]\\d{7}|800\\d{6}|8001111"], ["(?:8(?:4[2-5]|7[0-3])|9(?:[01]\\d|8[2-49]))\\d{7}|845464\\d", [7, 10]], ["70\\d{8}", [10]], 0, ["(?:3[0347]|55)\\d{8}", [10]], ["76(?:464|652)\\d{5}|76(?:0[0-28]|2[356]|34|4[01347]|5[49]|6[0-369]|77|8[14]|9[139])\\d{6}", [10]], ["56\\d{8}", [10]]], 0, " x"],
66118
66174
  "GD": ["1", "011", "(?:473|[58]\\d\\d|900)\\d{7}", [10], 0, "1", 0, "([2-9]\\d{6})$|1", "473$1", 0, "473"],
66119
66175
  "GE": ["995", "00", "(?:[3-57]\\d\\d|800)\\d{6}", [9], [["(\\d{3})(\\d{3})(\\d{3})", "$1 $2 $3", ["70"], "0$1"], ["(\\d{2})(\\d{3})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["32"], "0$1"], ["(\\d{3})(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["[57]"]], ["(\\d{3})(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["[348]"], "0$1"]], "0"],
66120
66176
  "GF": ["594", "00", "(?:[56]94\\d|7093)\\d{5}|(?:80|9\\d)\\d{7}", [9], [["(\\d{3})(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["[5-7]|9[47]"], "0$1"], ["(\\d{3})(\\d{2})(\\d{2})(\\d{2})", "$1 $2 $3 $4", ["[89]"], "0$1"]], "0"],
@@ -79220,7 +79276,7 @@ const StepLandingTable = /*#__PURE__*/React.forwardRef(({
79220
79276
  handleStepGroupExpand: handleStepGroupExpand,
79221
79277
  isStepGroupExpanded: isStepGroupExpanded,
79222
79278
  stepPartialSelect: stepPartialSelect,
79223
- height: tableData.length === 1 ? height : '',
79279
+ height: height,
79224
79280
  isClientSide: isClientSide
79225
79281
  });
79226
79282
  } else {
@@ -79244,7 +79300,7 @@ const StepLandingTable = /*#__PURE__*/React.forwardRef(({
79244
79300
  handleStepGroupExpand: handleStepGroupExpand,
79245
79301
  isStepGroupExpanded: isStepGroupExpanded,
79246
79302
  isViewPrivilegeMode: isViewPrivilegeMode,
79247
- height: tableData.length === 1 ? height : '',
79303
+ height: height,
79248
79304
  isClientSide: isClientSide
79249
79305
  });
79250
79306
  }
@@ -79310,7 +79366,8 @@ const StepLandingTable = /*#__PURE__*/React.forwardRef(({
79310
79366
  }), jsxRuntime.jsx("div", {
79311
79367
  className: "ff-accordion-table-body",
79312
79368
  style: {
79313
- height: tableData?.length === 1 && '100%' || height
79369
+ height: tableData?.length === 1 ? height : 'auto',
79370
+ overflow: tableData?.length > 1 ? 'auto' : 'visible'
79314
79371
  },
79315
79372
  children: tableData?.map(row => {
79316
79373
  const {
@@ -108777,6 +108834,3214 @@ const SessionManager = /*#__PURE__*/React.forwardRef(({
108777
108834
  });
108778
108835
  SessionManager.displayName = 'SessionManager';
108779
108836
 
108837
+ /*
108838
+ The MIT License (MIT)
108839
+
108840
+ Copyright (c) 2016 Meetecho
108841
+
108842
+ Permission is hereby granted, free of charge, to any person obtaining
108843
+ a copy of this software and associated documentation files (the "Software"),
108844
+ to deal in the Software without restriction, including without limitation
108845
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
108846
+ and/or sell copies of the Software, and to permit persons to whom the
108847
+ Software is furnished to do so, subject to the following conditions:
108848
+
108849
+ The above copyright notice and this permission notice shall be included
108850
+ in all copies or substantial portions of the Software.
108851
+
108852
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
108853
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
108854
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
108855
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
108856
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
108857
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
108858
+ OTHER DEALINGS IN THE SOFTWARE.
108859
+ */
108860
+
108861
+ // eslint-disable-next-line no-unused-vars
108862
+ var Janus = function (factory) {
108863
+ if (typeof define === 'function' && define.amd) {
108864
+ define(factory);
108865
+ } else if (typeof module === 'object' && module.exports) {
108866
+ module.exports = factory();
108867
+ } else if (typeof window === 'object') {
108868
+ return factory();
108869
+ }
108870
+ }(function () {
108871
+ // List of sessions
108872
+ Janus.sessions = new Map();
108873
+ Janus.isExtensionEnabled = function () {
108874
+ if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
108875
+ // No need for the extension, getDisplayMedia is supported
108876
+ return true;
108877
+ }
108878
+ if (window.navigator.userAgent.match('Chrome')) {
108879
+ let chromever = parseInt(window.navigator.userAgent.match(/Chrome\/(.*) /)[1], 10);
108880
+ let maxver = 33;
108881
+ if (window.navigator.userAgent.match('Linux')) maxver = 35; // "known" crash in chrome 34 and 35 on linux
108882
+ if (chromever >= 26 && chromever <= maxver) {
108883
+ // Older versions of Chrome don't support this extension-based approach, so lie
108884
+ return true;
108885
+ }
108886
+ return Janus.extension.isInstalled();
108887
+ } else {
108888
+ // Firefox and others, no need for the extension (but this doesn't mean it will work)
108889
+ return true;
108890
+ }
108891
+ };
108892
+ var defaultExtension = {
108893
+ // Screensharing Chrome Extension ID
108894
+ extensionId: 'hapfgfdkleiggjjpfpenajgdnfckjpaj',
108895
+ isInstalled: function () {
108896
+ return document.querySelector('#janus-extension-installed') !== null;
108897
+ },
108898
+ getScreen: function (callback) {
108899
+ let pending = window.setTimeout(function () {
108900
+ let error = new Error('NavigatorUserMediaError');
108901
+ error.name = 'The required Chrome extension is not installed: click <a href="#">here</a> to install it. (NOTE: this will need you to refresh the page)';
108902
+ return callback(error);
108903
+ }, 1000);
108904
+ this.cache[pending] = callback;
108905
+ window.postMessage({
108906
+ type: 'janusGetScreen',
108907
+ id: pending
108908
+ }, '*');
108909
+ },
108910
+ init: function () {
108911
+ let cache = {};
108912
+ this.cache = cache;
108913
+ // Wait for events from the Chrome Extension
108914
+ window.addEventListener('message', function (event) {
108915
+ if (event.origin != window.location.origin) return;
108916
+ if (event.data.type == 'janusGotScreen' && cache[event.data.id]) {
108917
+ let callback = cache[event.data.id];
108918
+ delete cache[event.data.id];
108919
+ if (event.data.sourceId === '') {
108920
+ // user canceled
108921
+ let error = new Error('NavigatorUserMediaError');
108922
+ error.name = 'You cancelled the request for permission, giving up...';
108923
+ callback(error);
108924
+ } else {
108925
+ callback(null, event.data.sourceId);
108926
+ }
108927
+ } else if (event.data.type == 'janusGetScreenPending') {
108928
+ window.clearTimeout(event.data.id);
108929
+ }
108930
+ });
108931
+ }
108932
+ };
108933
+ Janus.useDefaultDependencies = function (deps) {
108934
+ let f = deps && deps.fetch || fetch;
108935
+ let p = deps && deps.Promise || Promise;
108936
+ let socketCls = deps && deps.WebSocket || WebSocket;
108937
+ return {
108938
+ newWebSocket: function (server, proto) {
108939
+ return new socketCls(server, proto);
108940
+ },
108941
+ extension: deps && deps.extension || defaultExtension,
108942
+ isArray: function (arr) {
108943
+ return Array.isArray(arr);
108944
+ },
108945
+ webRTCAdapter: deps && deps.adapter || adapter,
108946
+ httpAPICall: function (url, options) {
108947
+ let fetchOptions = {
108948
+ method: options.verb,
108949
+ headers: {
108950
+ 'Accept': 'application/json, text/plain, */*'
108951
+ },
108952
+ cache: 'no-cache'
108953
+ };
108954
+ if (options.verb === "POST") {
108955
+ fetchOptions.headers['Content-Type'] = 'application/json';
108956
+ }
108957
+ if (typeof options.withCredentials !== 'undefined') {
108958
+ fetchOptions.credentials = options.withCredentials === true ? 'include' : options.withCredentials ? options.withCredentials : 'omit';
108959
+ }
108960
+ if (options.body) {
108961
+ fetchOptions.body = JSON.stringify(options.body);
108962
+ }
108963
+ let fetching = f(url, fetchOptions).catch(function (error) {
108964
+ return p.reject({
108965
+ message: 'Probably a network error, is the server down?',
108966
+ error: error
108967
+ });
108968
+ });
108969
+
108970
+ /*
108971
+ * fetch() does not natively support timeouts.
108972
+ * Work around this by starting a timeout manually, and racing it agains the fetch() to see which thing resolves first.
108973
+ */
108974
+
108975
+ if (options.timeout) {
108976
+ // eslint-disable-next-line no-unused-vars
108977
+ let timeout = new p(function (resolve, reject) {
108978
+ let timerId = setTimeout(function () {
108979
+ clearTimeout(timerId);
108980
+ return reject({
108981
+ message: 'Request timed out',
108982
+ timeout: options.timeout
108983
+ });
108984
+ }, options.timeout);
108985
+ });
108986
+ fetching = p.race([fetching, timeout]);
108987
+ }
108988
+ fetching.then(function (response) {
108989
+ if (response.ok) {
108990
+ if (typeof options.success === typeof Janus.noop) {
108991
+ return response.json().then(function (parsed) {
108992
+ try {
108993
+ options.success(parsed);
108994
+ } catch (error) {
108995
+ Janus.error('Unhandled httpAPICall success callback error', error);
108996
+ }
108997
+ }, function (error) {
108998
+ return p.reject({
108999
+ message: 'Failed to parse response body',
109000
+ error: error,
109001
+ response: response
109002
+ });
109003
+ });
109004
+ }
109005
+ } else {
109006
+ return p.reject({
109007
+ message: 'API call failed',
109008
+ response: response
109009
+ });
109010
+ }
109011
+ }).catch(function (error) {
109012
+ if (typeof options.error === typeof Janus.noop) {
109013
+ options.error(error.message || '<< internal error >>', error);
109014
+ }
109015
+ });
109016
+ return fetching;
109017
+ }
109018
+ };
109019
+ };
109020
+ Janus.useOldDependencies = function (deps) {
109021
+ let jq = deps && deps.jQuery || jQuery;
109022
+ let socketCls = deps && deps.WebSocket || WebSocket;
109023
+ return {
109024
+ newWebSocket: function (server, proto) {
109025
+ return new socketCls(server, proto);
109026
+ },
109027
+ isArray: function (arr) {
109028
+ return jq.isArray(arr);
109029
+ },
109030
+ extension: deps && deps.extension || defaultExtension,
109031
+ webRTCAdapter: deps && deps.adapter || adapter,
109032
+ httpAPICall: function (url, options) {
109033
+ let payload = typeof options.body !== 'undefined' ? {
109034
+ contentType: 'application/json',
109035
+ data: JSON.stringify(options.body)
109036
+ } : {};
109037
+ let credentials = typeof options.withCredentials !== 'undefined' ? {
109038
+ xhrFields: {
109039
+ withCredentials: options.withCredentials
109040
+ }
109041
+ } : {};
109042
+ return jq.ajax(jq.extend(payload, credentials, {
109043
+ url: url,
109044
+ type: options.verb,
109045
+ cache: false,
109046
+ dataType: 'json',
109047
+ async: options.async,
109048
+ timeout: options.timeout,
109049
+ success: function (result) {
109050
+ if (typeof options.success === typeof Janus.noop) {
109051
+ options.success(result);
109052
+ }
109053
+ },
109054
+ // eslint-disable-next-line no-unused-vars
109055
+ error: function (xhr, status, err) {
109056
+ if (typeof options.error === typeof Janus.noop) {
109057
+ options.error(status, err);
109058
+ }
109059
+ }
109060
+ }));
109061
+ }
109062
+ };
109063
+ };
109064
+
109065
+ // Helper function to convert a deprecated media object to a tracks array
109066
+ Janus.mediaToTracks = function (media) {
109067
+ let tracks = [];
109068
+ if (!media) {
109069
+ // Default is bidirectional audio and video, using default devices
109070
+ tracks.push({
109071
+ type: 'audio',
109072
+ capture: true,
109073
+ recv: true
109074
+ });
109075
+ tracks.push({
109076
+ type: 'video',
109077
+ capture: true,
109078
+ recv: true
109079
+ });
109080
+ } else {
109081
+ if (!media.keepAudio && media.audio !== false && (typeof media.audio === 'undefined' || media.audio || media.audioSend || media.audioRecv || media.addAudio || media.replaceAudio || media.removeAudio)) {
109082
+ // We may need an audio track
109083
+ let track = {
109084
+ type: 'audio'
109085
+ };
109086
+ if (media.removeAudio) {
109087
+ track.remove = true;
109088
+ } else {
109089
+ if (media.addAudio) track.add = true;else if (media.replaceAudio) track.replace = true;
109090
+ // Check if we need to capture an audio device
109091
+ if (media.audioSend !== false) track.capture = media.audio || true;
109092
+ // Check if we need to receive audio
109093
+ if (media.audioRecv !== false) track.recv = true;
109094
+ }
109095
+ // Add an audio track if needed
109096
+ if (track.remove || track.capture || track.recv) tracks.push(track);
109097
+ }
109098
+ if (!media.keepVideo && media.video !== false && (typeof media.video === 'undefined' || media.video || media.videoSend || media.videoRecv || media.addVideo || media.replaceVideo || media.removeVideo)) {
109099
+ // We may need a video track
109100
+ let track = {
109101
+ type: 'video'
109102
+ };
109103
+ if (media.removeVideo) {
109104
+ track.remove = true;
109105
+ } else {
109106
+ if (media.addVideo) track.add = true;else if (media.replaceVideo) track.replace = true;
109107
+ // Check if we need to capture a video device
109108
+ if (media.videoSend !== false) {
109109
+ track.capture = media.video || true;
109110
+ if (['screen', 'window', 'desktop'].includes(track.capture)) {
109111
+ // Change the type to 'screen'
109112
+ track.type = 'screen';
109113
+ track.capture = {
109114
+ video: {}
109115
+ };
109116
+ // Check if there's constraints
109117
+ if (media.screenshareFrameRate) track.capture.frameRate = media.screenshareFrameRate;
109118
+ if (media.screenshareHeight) track.capture.height = media.screenshareHeight;
109119
+ if (media.screenshareWidth) track.capture.width = media.screenshareWidth;
109120
+ }
109121
+ }
109122
+ // Check if we need to receive video
109123
+ if (media.videoRecv !== false) track.recv = true;
109124
+ }
109125
+ // Add a video track if needed
109126
+ if (track.remove || track.capture || track.recv) tracks.push(track);
109127
+ }
109128
+ if (media.data) {
109129
+ // We need a data channel
109130
+ tracks.push({
109131
+ type: 'data'
109132
+ });
109133
+ }
109134
+ }
109135
+ // Done
109136
+ return tracks;
109137
+ };
109138
+
109139
+ // Helper function to convert a track object to a set of constraints
109140
+ Janus.trackConstraints = function (track) {
109141
+ let constraints = {};
109142
+ if (!track || !track.capture) return constraints;
109143
+ if (track.type === 'audio') {
109144
+ // Just put the capture part in the constraints
109145
+ constraints.audio = track.capture;
109146
+ } else if (track.type === 'video') {
109147
+ // Check if one of the keywords was passed
109148
+ if ((track.simulcast || track.svc) && track.capture === true) track.capture = 'hires';
109149
+ if (track.capture === true || typeof track.capture === 'object') {
109150
+ // Use the provided capture object as video constraint
109151
+ constraints.video = track.capture;
109152
+ } else {
109153
+ let width = 0;
109154
+ let height = 0;
109155
+ if (track.capture === 'lowres') {
109156
+ // Small resolution, 4:3
109157
+ width = 320;
109158
+ height = 240;
109159
+ } else if (track.capture === 'lowres-16:9') {
109160
+ // Small resolution, 16:9
109161
+ width = 320;
109162
+ height = 180;
109163
+ } else if (track.capture === 'hires' || track.capture === 'hires-16:9' || track.capture === 'hdres') {
109164
+ // High(HD) resolution is only 16:9
109165
+ width = 1280;
109166
+ height = 720;
109167
+ } else if (track.capture === 'fhdres') {
109168
+ // Full HD resolution is only 16:9
109169
+ width = 1920;
109170
+ height = 1080;
109171
+ } else if (track.capture === '4kres') {
109172
+ // 4K resolution is only 16:9
109173
+ width = 3840;
109174
+ height = 2160;
109175
+ } else if (track.capture === 'stdres') {
109176
+ // Normal resolution, 4:3
109177
+ width = 640;
109178
+ height = 480;
109179
+ } else if (track.capture === 'stdres-16:9') {
109180
+ // Normal resolution, 16:9
109181
+ width = 640;
109182
+ height = 360;
109183
+ } else {
109184
+ Janus.log('Default video setting is stdres 4:3');
109185
+ width = 640;
109186
+ height = 480;
109187
+ }
109188
+ constraints.video = {
109189
+ width: {
109190
+ ideal: width
109191
+ },
109192
+ height: {
109193
+ ideal: height
109194
+ }
109195
+ };
109196
+ }
109197
+ } else if (track.type === 'screen') {
109198
+ // Use the provided capture object as video constraint
109199
+ constraints.video = track.capture;
109200
+ }
109201
+ return constraints;
109202
+ };
109203
+ Janus.noop = function () {};
109204
+ Janus.dataChanDefaultLabel = "JanusDataChannel";
109205
+
109206
+ // Note: in the future we may want to change this, e.g., as was
109207
+ // attempted in https://github.com/meetecho/janus-gateway/issues/1670
109208
+ Janus.endOfCandidates = null;
109209
+
109210
+ // Stop all tracks from a given stream
109211
+ Janus.stopAllTracks = function (stream) {
109212
+ try {
109213
+ // Try a MediaStreamTrack.stop() for each track
109214
+ let tracks = stream.getTracks();
109215
+ for (let mst of tracks) {
109216
+ Janus.log(mst);
109217
+ if (mst && mst.dontStop !== true) {
109218
+ mst.stop();
109219
+ }
109220
+ }
109221
+ // eslint-disable-next-line no-unused-vars
109222
+ } catch (e) {
109223
+ // Do nothing if this fails
109224
+ }
109225
+ };
109226
+
109227
+ // Initialization
109228
+ Janus.init = function (options) {
109229
+ options = options || {};
109230
+ options.callback = typeof options.callback == "function" ? options.callback : Janus.noop;
109231
+ if (Janus.initDone) {
109232
+ // Already initialized
109233
+ options.callback();
109234
+ } else {
109235
+ if (typeof console.log == "undefined") {
109236
+ console.log = function () {};
109237
+ }
109238
+ // Console logging (all debugging disabled by default)
109239
+ Janus.trace = Janus.noop;
109240
+ Janus.debug = Janus.noop;
109241
+ Janus.vdebug = Janus.noop;
109242
+ Janus.log = Janus.noop;
109243
+ Janus.warn = Janus.noop;
109244
+ Janus.error = Janus.noop;
109245
+ if (options.debug === true || options.debug === "all") {
109246
+ // Enable all debugging levels
109247
+ Janus.trace = console.trace.bind(console);
109248
+ Janus.debug = console.debug.bind(console);
109249
+ Janus.vdebug = console.debug.bind(console);
109250
+ Janus.log = console.log.bind(console);
109251
+ Janus.warn = console.warn.bind(console);
109252
+ Janus.error = console.error.bind(console);
109253
+ } else if (Array.isArray(options.debug)) {
109254
+ for (let d of options.debug) {
109255
+ switch (d) {
109256
+ case "trace":
109257
+ Janus.trace = console.trace.bind(console);
109258
+ break;
109259
+ case "debug":
109260
+ Janus.debug = console.debug.bind(console);
109261
+ break;
109262
+ case "vdebug":
109263
+ Janus.vdebug = console.debug.bind(console);
109264
+ break;
109265
+ case "log":
109266
+ Janus.log = console.log.bind(console);
109267
+ break;
109268
+ case "warn":
109269
+ Janus.warn = console.warn.bind(console);
109270
+ break;
109271
+ case "error":
109272
+ Janus.error = console.error.bind(console);
109273
+ break;
109274
+ }
109275
+ }
109276
+ }
109277
+ Janus.log("Initializing library");
109278
+ let usedDependencies = options.dependencies || Janus.useDefaultDependencies();
109279
+ Janus.isArray = usedDependencies.isArray;
109280
+ Janus.webRTCAdapter = usedDependencies.webRTCAdapter;
109281
+ Janus.httpAPICall = usedDependencies.httpAPICall;
109282
+ Janus.newWebSocket = usedDependencies.newWebSocket;
109283
+ Janus.extension = usedDependencies.extension;
109284
+ Janus.extension.init();
109285
+
109286
+ // Helper method to enumerate devices
109287
+ Janus.listDevices = function (callback, config) {
109288
+ callback = typeof callback == "function" ? callback : Janus.noop;
109289
+ if (!config) config = {
109290
+ audio: true,
109291
+ video: true
109292
+ };
109293
+ if (Janus.isGetUserMediaAvailable()) {
109294
+ navigator.mediaDevices.getUserMedia(config).then(function (stream) {
109295
+ navigator.mediaDevices.enumerateDevices().then(function (devices) {
109296
+ Janus.debug(devices);
109297
+ callback(devices);
109298
+ // Get rid of the now useless stream
109299
+ Janus.stopAllTracks(stream);
109300
+ });
109301
+ }).catch(function (err) {
109302
+ Janus.error(err);
109303
+ callback([]);
109304
+ });
109305
+ } else {
109306
+ Janus.warn("navigator.mediaDevices unavailable");
109307
+ callback([]);
109308
+ }
109309
+ };
109310
+ // Helper methods to attach/reattach a stream to a video element (previously part of adapter.js)
109311
+ Janus.attachMediaStream = function (element, stream) {
109312
+ try {
109313
+ element.srcObject = stream;
109314
+ // eslint-disable-next-line no-unused-vars
109315
+ } catch (e) {
109316
+ try {
109317
+ element.src = URL.createObjectURL(stream);
109318
+ } catch (e) {
109319
+ Janus.error("Error attaching stream to element", e);
109320
+ }
109321
+ }
109322
+ };
109323
+ Janus.reattachMediaStream = function (to, from) {
109324
+ try {
109325
+ to.srcObject = from.srcObject;
109326
+ // eslint-disable-next-line no-unused-vars
109327
+ } catch (e) {
109328
+ try {
109329
+ to.src = from.src;
109330
+ } catch (e) {
109331
+ Janus.error("Error reattaching stream to element", e);
109332
+ }
109333
+ }
109334
+ };
109335
+ // Detect tab close: make sure we don't loose existing onbeforeunload handlers
109336
+ // (note: for iOS we need to subscribe to a different event, 'pagehide', see
109337
+ // https://gist.github.com/thehunmonkgroup/6bee8941a49b86be31a787fe8f4b8cfe)
109338
+ let iOS = ['iPad', 'iPhone', 'iPod'].indexOf(navigator.platform) >= 0;
109339
+ let eventName = iOS ? 'pagehide' : 'beforeunload';
109340
+ let oldOBF = window["on" + eventName];
109341
+ window.addEventListener(eventName, function () {
109342
+ Janus.log("Closing window");
109343
+ for (const [sessionId, session] of Janus.sessions) {
109344
+ if (session && session.destroyOnUnload) {
109345
+ Janus.log("Destroying session " + sessionId);
109346
+ session.destroy({
109347
+ unload: true,
109348
+ notifyDestroyed: false
109349
+ });
109350
+ }
109351
+ }
109352
+ if (oldOBF && typeof oldOBF == "function") {
109353
+ oldOBF();
109354
+ }
109355
+ });
109356
+ // If this is a Safari, check if VP8 or VP9 are supported
109357
+ Janus.safariVp8 = false;
109358
+ Janus.safariVp9 = false;
109359
+ if (Janus.webRTCAdapter.browserDetails.browser === 'safari' && Janus.webRTCAdapter.browserDetails.version >= 605) {
109360
+ // Let's see if RTCRtpSender.getCapabilities() is there
109361
+ if (RTCRtpSender && RTCRtpSender.getCapabilities && RTCRtpSender.getCapabilities("video") && RTCRtpSender.getCapabilities("video").codecs && RTCRtpSender.getCapabilities("video").codecs.length) {
109362
+ for (let codec of RTCRtpSender.getCapabilities("video").codecs) {
109363
+ if (codec && codec.mimeType && codec.mimeType.toLowerCase() === "video/vp8") {
109364
+ Janus.safariVp8 = true;
109365
+ } else if (codec && codec.mimeType && codec.mimeType.toLowerCase() === "video/vp9") {
109366
+ Janus.safariVp9 = true;
109367
+ }
109368
+ }
109369
+ if (Janus.safariVp8) {
109370
+ Janus.log("This version of Safari supports VP8");
109371
+ } else {
109372
+ Janus.warn("This version of Safari does NOT support VP8: if you're using a Technology Preview, " + "try enabling the 'WebRTC VP8 codec' setting in the 'Experimental Features' Develop menu");
109373
+ }
109374
+ } else {
109375
+ // We do it in a very ugly way, as there's no alternative...
109376
+ // We create a PeerConnection to see if VP8 is in an offer
109377
+ let testpc = new RTCPeerConnection({});
109378
+ testpc.createOffer({
109379
+ offerToReceiveVideo: true
109380
+ }).then(function (offer) {
109381
+ Janus.safariVp8 = offer.sdp.indexOf("VP8") !== -1;
109382
+ Janus.safariVp9 = offer.sdp.indexOf("VP9") !== -1;
109383
+ if (Janus.safariVp8) {
109384
+ Janus.log("This version of Safari supports VP8");
109385
+ } else {
109386
+ Janus.warn("This version of Safari does NOT support VP8: if you're using a Technology Preview, " + "try enabling the 'WebRTC VP8 codec' setting in the 'Experimental Features' Develop menu");
109387
+ }
109388
+ testpc.close();
109389
+ testpc = null;
109390
+ });
109391
+ }
109392
+ }
109393
+ Janus.initDone = true;
109394
+ options.callback();
109395
+ }
109396
+ };
109397
+
109398
+ // Helper method to check whether WebRTC is supported by this browser
109399
+ Janus.isWebrtcSupported = function () {
109400
+ return !!window.RTCPeerConnection;
109401
+ };
109402
+ // Helper method to check whether devices can be accessed by this browser (e.g., not possible via plain HTTP)
109403
+ Janus.isGetUserMediaAvailable = function () {
109404
+ return navigator.mediaDevices && navigator.mediaDevices.getUserMedia;
109405
+ };
109406
+
109407
+ // Helper method to create random identifiers (e.g., transaction)
109408
+ Janus.randomString = function (len) {
109409
+ let charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
109410
+ let randomString = '';
109411
+ for (let i = 0; i < len; i++) {
109412
+ let randomPoz = Math.floor(Math.random() * charSet.length);
109413
+ randomString += charSet.charAt(randomPoz);
109414
+ }
109415
+ return randomString;
109416
+ };
109417
+ function Janus(gatewayCallbacks) {
109418
+ gatewayCallbacks = gatewayCallbacks || {};
109419
+ gatewayCallbacks.success = typeof gatewayCallbacks.success == "function" ? gatewayCallbacks.success : Janus.noop;
109420
+ gatewayCallbacks.error = typeof gatewayCallbacks.error == "function" ? gatewayCallbacks.error : Janus.noop;
109421
+ gatewayCallbacks.destroyed = typeof gatewayCallbacks.destroyed == "function" ? gatewayCallbacks.destroyed : Janus.noop;
109422
+ if (!Janus.initDone) {
109423
+ gatewayCallbacks.error("Library not initialized");
109424
+ return {};
109425
+ }
109426
+ if (!Janus.isWebrtcSupported()) {
109427
+ gatewayCallbacks.error("WebRTC not supported by this browser");
109428
+ return {};
109429
+ }
109430
+ Janus.log("Library initialized: " + Janus.initDone);
109431
+ if (!gatewayCallbacks.server) {
109432
+ gatewayCallbacks.error("Invalid server url");
109433
+ return {};
109434
+ }
109435
+ let websockets = false;
109436
+ let ws = null;
109437
+ let wsHandlers = {};
109438
+ let wsKeepaliveTimeoutId = null;
109439
+ let servers = null;
109440
+ let serversIndex = 0;
109441
+ let server = gatewayCallbacks.server;
109442
+ if (Janus.isArray(server)) {
109443
+ Janus.log("Multiple servers provided (" + server.length + "), will use the first that works");
109444
+ server = null;
109445
+ servers = gatewayCallbacks.server;
109446
+ Janus.debug(servers);
109447
+ } else {
109448
+ if (server.indexOf("ws") === 0) {
109449
+ websockets = true;
109450
+ Janus.log("Using WebSockets to contact Janus: " + server);
109451
+ } else {
109452
+ websockets = false;
109453
+ Janus.log("Using REST API to contact Janus: " + server);
109454
+ }
109455
+ }
109456
+ let iceServers = gatewayCallbacks.iceServers || [{
109457
+ urls: "stun:stun.l.google.com:19302"
109458
+ }];
109459
+ let iceTransportPolicy = gatewayCallbacks.iceTransportPolicy;
109460
+ let bundlePolicy = gatewayCallbacks.bundlePolicy;
109461
+ // Whether we should enable the withCredentials flag for XHR requests
109462
+ let withCredentials = false;
109463
+ if (typeof gatewayCallbacks.withCredentials !== 'undefined' && gatewayCallbacks.withCredentials !== null) withCredentials = gatewayCallbacks.withCredentials === true;
109464
+ // Optional max events
109465
+ let maxev = 10;
109466
+ if (typeof gatewayCallbacks.max_poll_events !== 'undefined' && gatewayCallbacks.max_poll_events !== null) maxev = gatewayCallbacks.max_poll_events;
109467
+ if (maxev < 1) maxev = 1;
109468
+ // Token to use (only if the token based authentication mechanism is enabled)
109469
+ let token = null;
109470
+ if (typeof gatewayCallbacks.token !== 'undefined' && gatewayCallbacks.token !== null) token = gatewayCallbacks.token;
109471
+ // API secret to use (only if the shared API secret is enabled)
109472
+ let apisecret = null;
109473
+ if (typeof gatewayCallbacks.apisecret !== 'undefined' && gatewayCallbacks.apisecret !== null) apisecret = gatewayCallbacks.apisecret;
109474
+ // Whether we should destroy this session when onbeforeunload is called
109475
+ this.destroyOnUnload = true;
109476
+ if (typeof gatewayCallbacks.destroyOnUnload !== 'undefined' && gatewayCallbacks.destroyOnUnload !== null) this.destroyOnUnload = gatewayCallbacks.destroyOnUnload === true;
109477
+ // Some timeout-related values
109478
+ let keepAlivePeriod = 25000;
109479
+ if (typeof gatewayCallbacks.keepAlivePeriod !== 'undefined' && gatewayCallbacks.keepAlivePeriod !== null) keepAlivePeriod = gatewayCallbacks.keepAlivePeriod;
109480
+ if (isNaN(keepAlivePeriod)) keepAlivePeriod = 25000;
109481
+ let longPollTimeout = 60000;
109482
+ if (typeof gatewayCallbacks.longPollTimeout !== 'undefined' && gatewayCallbacks.longPollTimeout !== null) longPollTimeout = gatewayCallbacks.longPollTimeout;
109483
+ if (isNaN(longPollTimeout)) longPollTimeout = 60000;
109484
+
109485
+ // overrides for default maxBitrate values for simulcasting
109486
+ function getMaxBitrates(simulcastMaxBitrates) {
109487
+ let maxBitrates = {
109488
+ high: 900000,
109489
+ medium: 300000,
109490
+ low: 100000
109491
+ };
109492
+ if (typeof simulcastMaxBitrates !== 'undefined' && simulcastMaxBitrates !== null) {
109493
+ if (simulcastMaxBitrates.high) maxBitrates.high = simulcastMaxBitrates.high;
109494
+ if (simulcastMaxBitrates.medium) maxBitrates.medium = simulcastMaxBitrates.medium;
109495
+ if (simulcastMaxBitrates.low) maxBitrates.low = simulcastMaxBitrates.low;
109496
+ }
109497
+ return maxBitrates;
109498
+ }
109499
+ let connected = false;
109500
+ let sessionId = null;
109501
+ let pluginHandles = new Map();
109502
+ let that = this;
109503
+ let retries = 0;
109504
+ let transactions = new Map();
109505
+ createSession(gatewayCallbacks);
109506
+
109507
+ // Public methods
109508
+ this.getServer = function () {
109509
+ return server;
109510
+ };
109511
+ this.isConnected = function () {
109512
+ return connected;
109513
+ };
109514
+ this.reconnect = function (callbacks) {
109515
+ callbacks = callbacks || {};
109516
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
109517
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : Janus.noop;
109518
+ callbacks["reconnect"] = true;
109519
+ createSession(callbacks);
109520
+ };
109521
+ this.getSessionId = function () {
109522
+ return sessionId;
109523
+ };
109524
+ this.getInfo = function (callbacks) {
109525
+ getInfo(callbacks);
109526
+ };
109527
+ this.destroy = function (callbacks) {
109528
+ destroySession(callbacks);
109529
+ };
109530
+ this.attach = function (callbacks) {
109531
+ createHandle(callbacks);
109532
+ };
109533
+ function eventHandler() {
109534
+ if (sessionId == null) return;
109535
+ Janus.debug('Long poll...');
109536
+ if (!connected) {
109537
+ Janus.warn("Is the server down? (connected=false)");
109538
+ return;
109539
+ }
109540
+ let longpoll = server + "/" + sessionId + "?rid=" + new Date().getTime();
109541
+ if (maxev) longpoll = longpoll + "&maxev=" + maxev;
109542
+ if (token) longpoll = longpoll + "&token=" + encodeURIComponent(token);
109543
+ if (apisecret) longpoll = longpoll + "&apisecret=" + encodeURIComponent(apisecret);
109544
+ Janus.httpAPICall(longpoll, {
109545
+ verb: 'GET',
109546
+ withCredentials: withCredentials,
109547
+ success: handleEvent,
109548
+ timeout: longPollTimeout,
109549
+ error: function (textStatus, errorThrown) {
109550
+ Janus.error(textStatus + ":", errorThrown);
109551
+ retries++;
109552
+ if (retries > 3) {
109553
+ // Did we just lose the server? :-(
109554
+ connected = false;
109555
+ gatewayCallbacks.error("Lost connection to the server (is it down?)");
109556
+ return;
109557
+ }
109558
+ eventHandler();
109559
+ }
109560
+ });
109561
+ }
109562
+
109563
+ // Private event handler: this will trigger plugin callbacks, if set
109564
+ function handleEvent(json, skipTimeout) {
109565
+ retries = 0;
109566
+ if (!websockets && typeof sessionId !== 'undefined' && sessionId !== null && skipTimeout !== true) eventHandler();
109567
+ if (!websockets && Janus.isArray(json)) {
109568
+ // We got an array: it means we passed a maxev > 1, iterate on all objects
109569
+ for (let i = 0; i < json.length; i++) {
109570
+ handleEvent(json[i], true);
109571
+ }
109572
+ return;
109573
+ }
109574
+ if (json["janus"] === "keepalive") {
109575
+ // Nothing happened
109576
+ Janus.vdebug("Got a keepalive on session " + sessionId);
109577
+ return;
109578
+ } else if (json["janus"] === "server_info") {
109579
+ // Just info on the Janus instance
109580
+ Janus.debug("Got info on the Janus instance");
109581
+ Janus.debug(json);
109582
+ const transaction = json["transaction"];
109583
+ if (transaction) {
109584
+ const reportSuccess = transactions.get(transaction);
109585
+ if (reportSuccess) reportSuccess(json);
109586
+ transactions.delete(transaction);
109587
+ }
109588
+ return;
109589
+ } else if (json["janus"] === "ack") {
109590
+ // Just an ack, we can probably ignore
109591
+ Janus.debug("Got an ack on session " + sessionId);
109592
+ Janus.debug(json);
109593
+ const transaction = json["transaction"];
109594
+ if (transaction) {
109595
+ const reportSuccess = transactions.get(transaction);
109596
+ if (reportSuccess) reportSuccess(json);
109597
+ transactions.delete(transaction);
109598
+ }
109599
+ return;
109600
+ } else if (json["janus"] === "success") {
109601
+ // Success!
109602
+ Janus.debug("Got a success on session " + sessionId);
109603
+ Janus.debug(json);
109604
+ const transaction = json["transaction"];
109605
+ if (transaction) {
109606
+ const reportSuccess = transactions.get(transaction);
109607
+ if (reportSuccess) reportSuccess(json);
109608
+ transactions.delete(transaction);
109609
+ }
109610
+ return;
109611
+ } else if (json["janus"] === "trickle") {
109612
+ // We got a trickle candidate from Janus
109613
+ const sender = json["sender"];
109614
+ if (!sender) {
109615
+ Janus.warn("Missing sender...");
109616
+ return;
109617
+ }
109618
+ const pluginHandle = pluginHandles.get(sender);
109619
+ if (!pluginHandle) {
109620
+ Janus.debug("This handle is not attached to this session");
109621
+ return;
109622
+ }
109623
+ let candidate = json["candidate"];
109624
+ Janus.debug("Got a trickled candidate on session " + sessionId);
109625
+ Janus.debug(candidate);
109626
+ let config = pluginHandle.webrtcStuff;
109627
+ if (config.pc && config.remoteSdp) {
109628
+ // Add candidate right now
109629
+ Janus.debug("Adding remote candidate:", candidate);
109630
+ if (!candidate || candidate.completed === true) {
109631
+ // end-of-candidates
109632
+ config.pc.addIceCandidate(Janus.endOfCandidates);
109633
+ } else {
109634
+ // New candidate
109635
+ config.pc.addIceCandidate(candidate);
109636
+ }
109637
+ } else {
109638
+ // We didn't do setRemoteDescription (trickle got here before the offer?)
109639
+ Janus.debug("We didn't do setRemoteDescription (trickle got here before the offer?), caching candidate");
109640
+ if (!config.candidates) config.candidates = [];
109641
+ config.candidates.push(candidate);
109642
+ Janus.debug(config.candidates);
109643
+ }
109644
+ } else if (json["janus"] === "webrtcup") {
109645
+ // The PeerConnection with the server is up! Notify this
109646
+ Janus.debug("Got a webrtcup event on session " + sessionId);
109647
+ Janus.debug(json);
109648
+ const sender = json["sender"];
109649
+ if (!sender) {
109650
+ Janus.warn("Missing sender...");
109651
+ return;
109652
+ }
109653
+ const pluginHandle = pluginHandles.get(sender);
109654
+ if (!pluginHandle) {
109655
+ Janus.debug("This handle is not attached to this session");
109656
+ return;
109657
+ }
109658
+ pluginHandle.webrtcState(true);
109659
+ return;
109660
+ } else if (json["janus"] === "hangup") {
109661
+ // A plugin asked the core to hangup a PeerConnection on one of our handles
109662
+ Janus.debug("Got a hangup event on session " + sessionId);
109663
+ Janus.debug(json);
109664
+ const sender = json["sender"];
109665
+ if (!sender) {
109666
+ Janus.warn("Missing sender...");
109667
+ return;
109668
+ }
109669
+ const pluginHandle = pluginHandles.get(sender);
109670
+ if (!pluginHandle) {
109671
+ Janus.debug("This handle is not attached to this session");
109672
+ return;
109673
+ }
109674
+ pluginHandle.webrtcState(false, json["reason"]);
109675
+ pluginHandle.hangup();
109676
+ } else if (json["janus"] === "detached") {
109677
+ // A plugin asked the core to detach one of our handles
109678
+ Janus.debug("Got a detached event on session " + sessionId);
109679
+ Janus.debug(json);
109680
+ const sender = json["sender"];
109681
+ if (!sender) {
109682
+ Janus.warn("Missing sender...");
109683
+ return;
109684
+ }
109685
+ const pluginHandle = pluginHandles.get(sender);
109686
+ if (!pluginHandle) {
109687
+ // Don't warn here because destroyHandle causes this situation.
109688
+ return;
109689
+ }
109690
+ pluginHandle.ondetached();
109691
+ pluginHandle.detach();
109692
+ } else if (json["janus"] === "media") {
109693
+ // Media started/stopped flowing
109694
+ Janus.debug("Got a media event on session " + sessionId);
109695
+ Janus.debug(json);
109696
+ const sender = json["sender"];
109697
+ if (!sender) {
109698
+ Janus.warn("Missing sender...");
109699
+ return;
109700
+ }
109701
+ const pluginHandle = pluginHandles.get(sender);
109702
+ if (!pluginHandle) {
109703
+ Janus.debug("This handle is not attached to this session");
109704
+ return;
109705
+ }
109706
+ pluginHandle.mediaState(json["type"], json["receiving"], json["mid"]);
109707
+ } else if (json["janus"] === "slowlink") {
109708
+ Janus.debug("Got a slowlink event on session " + sessionId);
109709
+ Janus.debug(json);
109710
+ // Trouble uplink or downlink
109711
+ const sender = json["sender"];
109712
+ if (!sender) {
109713
+ Janus.warn("Missing sender...");
109714
+ return;
109715
+ }
109716
+ const pluginHandle = pluginHandles.get(sender);
109717
+ if (!pluginHandle) {
109718
+ Janus.debug("This handle is not attached to this session");
109719
+ return;
109720
+ }
109721
+ pluginHandle.slowLink(json["uplink"], json["lost"], json["mid"]);
109722
+ } else if (json["janus"] === "error") {
109723
+ // Oops, something wrong happened
109724
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
109725
+ Janus.debug(json);
109726
+ let transaction = json["transaction"];
109727
+ if (transaction) {
109728
+ let reportSuccess = transactions.get(transaction);
109729
+ if (reportSuccess) {
109730
+ reportSuccess(json);
109731
+ }
109732
+ transactions.delete(transaction);
109733
+ }
109734
+ return;
109735
+ } else if (json["janus"] === "event") {
109736
+ Janus.debug("Got a plugin event on session " + sessionId);
109737
+ Janus.debug(json);
109738
+ const sender = json["sender"];
109739
+ if (!sender) {
109740
+ Janus.warn("Missing sender...");
109741
+ return;
109742
+ }
109743
+ let plugindata = json["plugindata"];
109744
+ if (!plugindata) {
109745
+ Janus.warn("Missing plugindata...");
109746
+ return;
109747
+ }
109748
+ Janus.debug(" -- Event is coming from " + sender + " (" + plugindata["plugin"] + ")");
109749
+ let data = plugindata["data"];
109750
+ Janus.debug(data);
109751
+ const pluginHandle = pluginHandles.get(sender);
109752
+ if (!pluginHandle) {
109753
+ Janus.warn("This handle is not attached to this session");
109754
+ return;
109755
+ }
109756
+ let jsep = json["jsep"];
109757
+ if (jsep) {
109758
+ Janus.debug("Handling SDP as well...");
109759
+ Janus.debug(jsep);
109760
+ }
109761
+ let callback = pluginHandle.onmessage;
109762
+ if (callback) {
109763
+ Janus.debug("Notifying application...");
109764
+ // Send to callback specified when attaching plugin handle
109765
+ callback(data, jsep);
109766
+ } else {
109767
+ // Send to generic callback (?)
109768
+ Janus.debug("No provided notification callback");
109769
+ }
109770
+ } else if (json["janus"] === "timeout") {
109771
+ Janus.error("Timeout on session " + sessionId);
109772
+ Janus.debug(json);
109773
+ if (websockets) {
109774
+ ws.close(3504, "Gateway timeout");
109775
+ }
109776
+ return;
109777
+ } else {
109778
+ Janus.warn("Unknown message/event '" + json["janus"] + "' on session " + sessionId);
109779
+ Janus.debug(json);
109780
+ }
109781
+ }
109782
+
109783
+ // Private helper to send keep-alive messages on WebSockets
109784
+ function keepAlive() {
109785
+ if (!server || !websockets || !connected) return;
109786
+ wsKeepaliveTimeoutId = setTimeout(keepAlive, keepAlivePeriod);
109787
+ let request = {
109788
+ "janus": "keepalive",
109789
+ "session_id": sessionId,
109790
+ "transaction": Janus.randomString(12)
109791
+ };
109792
+ if (token) request["token"] = token;
109793
+ if (apisecret) request["apisecret"] = apisecret;
109794
+ ws.send(JSON.stringify(request));
109795
+ }
109796
+
109797
+ // Private method to create a session
109798
+ function createSession(callbacks) {
109799
+ let transaction = Janus.randomString(12);
109800
+ let request = {
109801
+ "janus": "create",
109802
+ "transaction": transaction
109803
+ };
109804
+ if (callbacks["reconnect"]) {
109805
+ // We're reconnecting, claim the session
109806
+ connected = false;
109807
+ request["janus"] = "claim";
109808
+ request["session_id"] = sessionId;
109809
+ // If we were using websockets, ignore the old connection
109810
+ if (ws) {
109811
+ ws.onopen = null;
109812
+ ws.onerror = null;
109813
+ ws.onclose = null;
109814
+ if (wsKeepaliveTimeoutId) {
109815
+ clearTimeout(wsKeepaliveTimeoutId);
109816
+ wsKeepaliveTimeoutId = null;
109817
+ }
109818
+ }
109819
+ }
109820
+ if (token) request["token"] = token;
109821
+ if (apisecret) request["apisecret"] = apisecret;
109822
+ if (!server && Janus.isArray(servers)) {
109823
+ // We still need to find a working server from the list we were given
109824
+ server = servers[serversIndex];
109825
+ if (server.indexOf("ws") === 0) {
109826
+ websockets = true;
109827
+ Janus.log("Server #" + (serversIndex + 1) + ": trying WebSockets to contact Janus (" + server + ")");
109828
+ } else {
109829
+ websockets = false;
109830
+ Janus.log("Server #" + (serversIndex + 1) + ": trying REST API to contact Janus (" + server + ")");
109831
+ }
109832
+ }
109833
+ if (websockets) {
109834
+ ws = Janus.newWebSocket(server, 'janus-protocol');
109835
+ wsHandlers = {
109836
+ 'error': function () {
109837
+ Janus.error("Error connecting to the Janus WebSockets server... " + server);
109838
+ if (Janus.isArray(servers) && !callbacks["reconnect"]) {
109839
+ serversIndex++;
109840
+ if (serversIndex === servers.length) {
109841
+ // We tried all the servers the user gave us and they all failed
109842
+ callbacks.error("Error connecting to any of the provided Janus servers: Is the server down?");
109843
+ return;
109844
+ }
109845
+ // Let's try the next server
109846
+ server = null;
109847
+ setTimeout(function () {
109848
+ createSession(callbacks);
109849
+ }, 200);
109850
+ return;
109851
+ }
109852
+ callbacks.error("Error connecting to the Janus WebSockets server: Is the server down?");
109853
+ },
109854
+ 'open': function () {
109855
+ // We need to be notified about the success
109856
+ transactions.set(transaction, function (json) {
109857
+ Janus.debug(json);
109858
+ if (json["janus"] !== "success") {
109859
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
109860
+ callbacks.error(json["error"].reason);
109861
+ return;
109862
+ }
109863
+ wsKeepaliveTimeoutId = setTimeout(keepAlive, keepAlivePeriod);
109864
+ connected = true;
109865
+ sessionId = json["session_id"] ? json["session_id"] : json.data["id"];
109866
+ if (callbacks["reconnect"]) {
109867
+ Janus.log("Claimed session: " + sessionId);
109868
+ } else {
109869
+ Janus.log("Created session: " + sessionId);
109870
+ }
109871
+ Janus.sessions.set(sessionId, that);
109872
+ callbacks.success();
109873
+ });
109874
+ ws.send(JSON.stringify(request));
109875
+ },
109876
+ 'message': function (event) {
109877
+ handleEvent(JSON.parse(event.data));
109878
+ },
109879
+ 'close': function () {
109880
+ if (!server || !connected) {
109881
+ return;
109882
+ }
109883
+ connected = false;
109884
+ // FIXME What if this is called when the page is closed?
109885
+ gatewayCallbacks.error("Lost connection to the server (is it down?)");
109886
+ }
109887
+ };
109888
+ for (let eventName in wsHandlers) {
109889
+ ws.addEventListener(eventName, wsHandlers[eventName]);
109890
+ }
109891
+ return;
109892
+ }
109893
+ Janus.httpAPICall(server, {
109894
+ verb: 'POST',
109895
+ withCredentials: withCredentials,
109896
+ body: request,
109897
+ success: function (json) {
109898
+ Janus.debug(json);
109899
+ if (json["janus"] !== "success") {
109900
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
109901
+ callbacks.error(json["error"].reason);
109902
+ return;
109903
+ }
109904
+ connected = true;
109905
+ sessionId = json["session_id"] ? json["session_id"] : json.data["id"];
109906
+ if (callbacks["reconnect"]) {
109907
+ Janus.log("Claimed session: " + sessionId);
109908
+ } else {
109909
+ Janus.log("Created session: " + sessionId);
109910
+ }
109911
+ Janus.sessions.set(sessionId, that);
109912
+ eventHandler();
109913
+ callbacks.success();
109914
+ },
109915
+ error: function (textStatus, errorThrown) {
109916
+ Janus.error(textStatus + ":", errorThrown); // FIXME
109917
+ if (Janus.isArray(servers) && !callbacks["reconnect"]) {
109918
+ serversIndex++;
109919
+ if (serversIndex === servers.length) {
109920
+ // We tried all the servers the user gave us and they all failed
109921
+ callbacks.error("Error connecting to any of the provided Janus servers: Is the server down?");
109922
+ return;
109923
+ }
109924
+ // Let's try the next server
109925
+ server = null;
109926
+ setTimeout(function () {
109927
+ createSession(callbacks);
109928
+ }, 200);
109929
+ return;
109930
+ }
109931
+ if (errorThrown === "") callbacks.error(textStatus + ": Is the server down?");else if (errorThrown && errorThrown.error) callbacks.error(textStatus + ": " + errorThrown.error.message);else callbacks.error(textStatus + ": " + errorThrown);
109932
+ }
109933
+ });
109934
+ }
109935
+
109936
+ // Private method to get info on the server
109937
+ function getInfo(callbacks) {
109938
+ callbacks = callbacks || {};
109939
+ // FIXME This method triggers a success even when we fail
109940
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
109941
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : Janus.noop;
109942
+ Janus.log("Getting info on Janus instance");
109943
+ if (!connected) {
109944
+ Janus.warn("Is the server down? (connected=false)");
109945
+ callbacks.error("Is the server down? (connected=false)");
109946
+ return;
109947
+ }
109948
+ // We just need to send an "info" request
109949
+ let transaction = Janus.randomString(12);
109950
+ let request = {
109951
+ "janus": "info",
109952
+ "transaction": transaction
109953
+ };
109954
+ if (token) request["token"] = token;
109955
+ if (apisecret) request["apisecret"] = apisecret;
109956
+ if (websockets) {
109957
+ transactions.set(transaction, function (json) {
109958
+ Janus.log("Server info:");
109959
+ Janus.debug(json);
109960
+ if (json["janus"] !== "server_info") {
109961
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
109962
+ }
109963
+ callbacks.success(json);
109964
+ });
109965
+ ws.send(JSON.stringify(request));
109966
+ return;
109967
+ }
109968
+ Janus.httpAPICall(server, {
109969
+ verb: 'POST',
109970
+ withCredentials: withCredentials,
109971
+ body: request,
109972
+ success: function (json) {
109973
+ Janus.log("Server info:");
109974
+ Janus.debug(json);
109975
+ if (json["janus"] !== "server_info") {
109976
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
109977
+ }
109978
+ callbacks.success(json);
109979
+ },
109980
+ error: function (textStatus, errorThrown) {
109981
+ Janus.error(textStatus + ":", errorThrown); // FIXME
109982
+ if (errorThrown === "") callbacks.error(textStatus + ": Is the server down?");else callbacks.error(textStatus + ": " + errorThrown);
109983
+ }
109984
+ });
109985
+ }
109986
+
109987
+ // Private method to destroy a session
109988
+ function destroySession(callbacks) {
109989
+ callbacks = callbacks || {};
109990
+ // FIXME This method triggers a success even when we fail
109991
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
109992
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : Janus.noop;
109993
+ let unload = callbacks.unload === true;
109994
+ let notifyDestroyed = true;
109995
+ if (typeof callbacks.notifyDestroyed !== 'undefined' && callbacks.notifyDestroyed !== null) notifyDestroyed = callbacks.notifyDestroyed === true;
109996
+ let cleanupHandles = callbacks.cleanupHandles === true;
109997
+ Janus.log("Destroying session " + sessionId + " (unload=" + unload + ")");
109998
+ if (!sessionId) {
109999
+ Janus.warn("No session to destroy");
110000
+ callbacks.success();
110001
+ if (notifyDestroyed) gatewayCallbacks.destroyed();
110002
+ return;
110003
+ }
110004
+ if (cleanupHandles) {
110005
+ for (const handleId of pluginHandles.keys()) destroyHandle(handleId, {
110006
+ noRequest: true
110007
+ });
110008
+ }
110009
+ if (!connected) {
110010
+ Janus.warn("Is the server down? (connected=false)");
110011
+ sessionId = null;
110012
+ callbacks.success();
110013
+ return;
110014
+ }
110015
+ // No need to destroy all handles first, Janus will do that itself
110016
+ let request = {
110017
+ "janus": "destroy",
110018
+ "transaction": Janus.randomString(12)
110019
+ };
110020
+ if (token) request["token"] = token;
110021
+ if (apisecret) request["apisecret"] = apisecret;
110022
+ if (unload) {
110023
+ // We're unloading the page: use sendBeacon for HTTP instead,
110024
+ // or just close the WebSocket connection if we're using that
110025
+ if (websockets) {
110026
+ ws.onclose = null;
110027
+ ws.close();
110028
+ ws = null;
110029
+ } else {
110030
+ navigator.sendBeacon(server + "/" + sessionId, JSON.stringify(request));
110031
+ }
110032
+ Janus.log("Destroyed session:");
110033
+ sessionId = null;
110034
+ connected = false;
110035
+ callbacks.success();
110036
+ if (notifyDestroyed) gatewayCallbacks.destroyed();
110037
+ return;
110038
+ }
110039
+ if (websockets) {
110040
+ request["session_id"] = sessionId;
110041
+ let unbindWebSocket = function () {
110042
+ for (let eventName in wsHandlers) {
110043
+ ws.removeEventListener(eventName, wsHandlers[eventName]);
110044
+ }
110045
+ ws.removeEventListener('message', onUnbindMessage);
110046
+ ws.removeEventListener('error', onUnbindError);
110047
+ if (wsKeepaliveTimeoutId) {
110048
+ clearTimeout(wsKeepaliveTimeoutId);
110049
+ }
110050
+ ws.close();
110051
+ };
110052
+ let onUnbindMessage = function (event) {
110053
+ let data = JSON.parse(event.data);
110054
+ if (data.session_id == request.session_id && data.transaction == request.transaction) {
110055
+ unbindWebSocket();
110056
+ callbacks.success();
110057
+ if (notifyDestroyed) gatewayCallbacks.destroyed();
110058
+ }
110059
+ };
110060
+ let onUnbindError = function () {
110061
+ unbindWebSocket();
110062
+ callbacks.error("Failed to destroy the server: Is the server down?");
110063
+ if (notifyDestroyed) gatewayCallbacks.destroyed();
110064
+ };
110065
+ ws.addEventListener('message', onUnbindMessage);
110066
+ ws.addEventListener('error', onUnbindError);
110067
+ if (ws.readyState === 1) {
110068
+ ws.send(JSON.stringify(request));
110069
+ } else {
110070
+ onUnbindError();
110071
+ }
110072
+ return;
110073
+ }
110074
+ Janus.httpAPICall(server + "/" + sessionId, {
110075
+ verb: 'POST',
110076
+ withCredentials: withCredentials,
110077
+ body: request,
110078
+ success: function (json) {
110079
+ Janus.log("Destroyed session:");
110080
+ Janus.debug(json);
110081
+ sessionId = null;
110082
+ connected = false;
110083
+ if (json["janus"] !== "success") {
110084
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
110085
+ }
110086
+ callbacks.success();
110087
+ if (notifyDestroyed) gatewayCallbacks.destroyed();
110088
+ },
110089
+ error: function (textStatus, errorThrown) {
110090
+ Janus.error(textStatus + ":", errorThrown); // FIXME
110091
+ // Reset everything anyway
110092
+ sessionId = null;
110093
+ connected = false;
110094
+ callbacks.success();
110095
+ if (notifyDestroyed) gatewayCallbacks.destroyed();
110096
+ }
110097
+ });
110098
+ }
110099
+
110100
+ // Private method to create a plugin handle
110101
+ function createHandle(callbacks) {
110102
+ callbacks = callbacks || {};
110103
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
110104
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : Janus.noop;
110105
+ callbacks.dataChannelOptions = callbacks.dataChannelOptions || {
110106
+ ordered: true
110107
+ };
110108
+ callbacks.consentDialog = typeof callbacks.consentDialog == "function" ? callbacks.consentDialog : Janus.noop;
110109
+ callbacks.connectionState = typeof callbacks.connectionState == "function" ? callbacks.connectionState : Janus.noop;
110110
+ callbacks.iceState = typeof callbacks.iceState == "function" ? callbacks.iceState : Janus.noop;
110111
+ callbacks.mediaState = typeof callbacks.mediaState == "function" ? callbacks.mediaState : Janus.noop;
110112
+ callbacks.webrtcState = typeof callbacks.webrtcState == "function" ? callbacks.webrtcState : Janus.noop;
110113
+ callbacks.slowLink = typeof callbacks.slowLink == "function" ? callbacks.slowLink : Janus.noop;
110114
+ callbacks.onmessage = typeof callbacks.onmessage == "function" ? callbacks.onmessage : Janus.noop;
110115
+ callbacks.onlocaltrack = typeof callbacks.onlocaltrack == "function" ? callbacks.onlocaltrack : Janus.noop;
110116
+ callbacks.onremotetrack = typeof callbacks.onremotetrack == "function" ? callbacks.onremotetrack : Janus.noop;
110117
+ callbacks.ondata = typeof callbacks.ondata == "function" ? callbacks.ondata : Janus.noop;
110118
+ callbacks.ondataopen = typeof callbacks.ondataopen == "function" ? callbacks.ondataopen : Janus.noop;
110119
+ callbacks.oncleanup = typeof callbacks.oncleanup == "function" ? callbacks.oncleanup : Janus.noop;
110120
+ callbacks.ondetached = typeof callbacks.ondetached == "function" ? callbacks.ondetached : Janus.noop;
110121
+ if (!connected) {
110122
+ Janus.warn("Is the server down? (connected=false)");
110123
+ callbacks.error("Is the server down? (connected=false)");
110124
+ return;
110125
+ }
110126
+ let plugin = callbacks.plugin;
110127
+ if (!plugin) {
110128
+ Janus.error("Invalid plugin");
110129
+ callbacks.error("Invalid plugin");
110130
+ return;
110131
+ }
110132
+ let opaqueId = callbacks.opaqueId;
110133
+ let loopIndex = callbacks.loopIndex;
110134
+ let handleToken = callbacks.token ? callbacks.token : token;
110135
+ let transaction = Janus.randomString(12);
110136
+ let request = {
110137
+ "janus": "attach",
110138
+ "plugin": plugin,
110139
+ "opaque_id": opaqueId,
110140
+ "loop_index": loopIndex,
110141
+ "transaction": transaction
110142
+ };
110143
+ if (handleToken) request["token"] = handleToken;
110144
+ if (apisecret) request["apisecret"] = apisecret;
110145
+ if (websockets) {
110146
+ transactions.set(transaction, function (json) {
110147
+ Janus.debug(json);
110148
+ if (json["janus"] !== "success") {
110149
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
110150
+ callbacks.error("Ooops: " + json["error"].code + " " + json["error"].reason);
110151
+ return;
110152
+ }
110153
+ let handleId = json.data["id"];
110154
+ Janus.log("Created handle: " + handleId);
110155
+ let pluginHandle = {
110156
+ session: that,
110157
+ plugin: plugin,
110158
+ id: handleId,
110159
+ token: handleToken,
110160
+ detached: false,
110161
+ webrtcStuff: {
110162
+ started: false,
110163
+ myStream: null,
110164
+ streamExternal: false,
110165
+ mySdp: null,
110166
+ mediaConstraints: null,
110167
+ pc: null,
110168
+ dataChannelOptions: callbacks.dataChannelOptions,
110169
+ dataChannel: {},
110170
+ dtmfSender: null,
110171
+ trickle: true,
110172
+ iceDone: false,
110173
+ bitrate: {}
110174
+ },
110175
+ getId: function () {
110176
+ return handleId;
110177
+ },
110178
+ getPlugin: function () {
110179
+ return plugin;
110180
+ },
110181
+ getVolume: function (mid, result) {
110182
+ return getVolume(handleId, mid, true, result);
110183
+ },
110184
+ getRemoteVolume: function (mid, result) {
110185
+ return getVolume(handleId, mid, true, result);
110186
+ },
110187
+ getLocalVolume: function (mid, result) {
110188
+ return getVolume(handleId, mid, false, result);
110189
+ },
110190
+ isAudioMuted: function (mid) {
110191
+ return isMuted(handleId, mid, false);
110192
+ },
110193
+ muteAudio: function (mid) {
110194
+ return mute(handleId, mid, false, true);
110195
+ },
110196
+ unmuteAudio: function (mid) {
110197
+ return mute(handleId, mid, false, false);
110198
+ },
110199
+ isVideoMuted: function (mid) {
110200
+ return isMuted(handleId, mid, true);
110201
+ },
110202
+ muteVideo: function (mid) {
110203
+ return mute(handleId, mid, true, true);
110204
+ },
110205
+ unmuteVideo: function (mid) {
110206
+ return mute(handleId, mid, true, false);
110207
+ },
110208
+ getBitrate: function (mid) {
110209
+ return getBitrate(handleId, mid);
110210
+ },
110211
+ setMaxBitrate: function (mid, bitrate) {
110212
+ return setBitrate(handleId, mid, bitrate);
110213
+ },
110214
+ send: function (callbacks) {
110215
+ sendMessage(handleId, callbacks);
110216
+ },
110217
+ data: function (callbacks) {
110218
+ sendData(handleId, callbacks);
110219
+ },
110220
+ dtmf: function (callbacks) {
110221
+ sendDtmf(handleId, callbacks);
110222
+ },
110223
+ consentDialog: callbacks.consentDialog,
110224
+ connectionState: callbacks.connectionState,
110225
+ iceState: callbacks.iceState,
110226
+ mediaState: callbacks.mediaState,
110227
+ webrtcState: callbacks.webrtcState,
110228
+ slowLink: callbacks.slowLink,
110229
+ onmessage: callbacks.onmessage,
110230
+ createOffer: function (callbacks) {
110231
+ prepareWebrtc(handleId, true, callbacks);
110232
+ },
110233
+ createAnswer: function (callbacks) {
110234
+ prepareWebrtc(handleId, false, callbacks);
110235
+ },
110236
+ handleRemoteJsep: function (callbacks) {
110237
+ prepareWebrtcPeer(handleId, callbacks);
110238
+ },
110239
+ replaceTracks: function (callbacks) {
110240
+ replaceTracks(handleId, callbacks);
110241
+ },
110242
+ getLocalTracks: function () {
110243
+ return getLocalTracks(handleId);
110244
+ },
110245
+ getRemoteTracks: function () {
110246
+ return getRemoteTracks(handleId);
110247
+ },
110248
+ onlocaltrack: callbacks.onlocaltrack,
110249
+ onremotetrack: callbacks.onremotetrack,
110250
+ ondata: callbacks.ondata,
110251
+ ondataopen: callbacks.ondataopen,
110252
+ oncleanup: callbacks.oncleanup,
110253
+ ondetached: callbacks.ondetached,
110254
+ hangup: function (sendRequest) {
110255
+ cleanupWebrtc(handleId, sendRequest === true);
110256
+ },
110257
+ detach: function (callbacks) {
110258
+ destroyHandle(handleId, callbacks);
110259
+ }
110260
+ };
110261
+ pluginHandles.set(handleId, pluginHandle);
110262
+ callbacks.success(pluginHandle);
110263
+ });
110264
+ request["session_id"] = sessionId;
110265
+ ws.send(JSON.stringify(request));
110266
+ return;
110267
+ }
110268
+ Janus.httpAPICall(server + "/" + sessionId, {
110269
+ verb: 'POST',
110270
+ withCredentials: withCredentials,
110271
+ body: request,
110272
+ success: function (json) {
110273
+ Janus.debug(json);
110274
+ if (json["janus"] !== "success") {
110275
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
110276
+ callbacks.error("Ooops: " + json["error"].code + " " + json["error"].reason);
110277
+ return;
110278
+ }
110279
+ let handleId = json.data["id"];
110280
+ Janus.log("Created handle: " + handleId);
110281
+ let pluginHandle = {
110282
+ session: that,
110283
+ plugin: plugin,
110284
+ id: handleId,
110285
+ token: handleToken,
110286
+ detached: false,
110287
+ webrtcStuff: {
110288
+ started: false,
110289
+ myStream: null,
110290
+ streamExternal: false,
110291
+ mySdp: null,
110292
+ mediaConstraints: null,
110293
+ pc: null,
110294
+ dataChannelOptions: callbacks.dataChannelOptions,
110295
+ dataChannel: {},
110296
+ dtmfSender: null,
110297
+ trickle: true,
110298
+ iceDone: false,
110299
+ bitrate: {}
110300
+ },
110301
+ getId: function () {
110302
+ return handleId;
110303
+ },
110304
+ getPlugin: function () {
110305
+ return plugin;
110306
+ },
110307
+ getVolume: function (mid, result) {
110308
+ return getVolume(handleId, mid, true, result);
110309
+ },
110310
+ getRemoteVolume: function (mid, result) {
110311
+ return getVolume(handleId, mid, true, result);
110312
+ },
110313
+ getLocalVolume: function (mid, result) {
110314
+ return getVolume(handleId, mid, false, result);
110315
+ },
110316
+ isAudioMuted: function (mid) {
110317
+ return isMuted(handleId, mid, false);
110318
+ },
110319
+ muteAudio: function (mid) {
110320
+ return mute(handleId, mid, false, true);
110321
+ },
110322
+ unmuteAudio: function (mid) {
110323
+ return mute(handleId, mid, false, false);
110324
+ },
110325
+ isVideoMuted: function (mid) {
110326
+ return isMuted(handleId, mid, true);
110327
+ },
110328
+ muteVideo: function (mid) {
110329
+ return mute(handleId, mid, true, true);
110330
+ },
110331
+ unmuteVideo: function (mid) {
110332
+ return mute(handleId, mid, true, false);
110333
+ },
110334
+ getBitrate: function (mid) {
110335
+ return getBitrate(handleId, mid);
110336
+ },
110337
+ setMaxBitrate: function (mid, bitrate) {
110338
+ return setBitrate(handleId, mid, bitrate);
110339
+ },
110340
+ send: function (callbacks) {
110341
+ sendMessage(handleId, callbacks);
110342
+ },
110343
+ data: function (callbacks) {
110344
+ sendData(handleId, callbacks);
110345
+ },
110346
+ dtmf: function (callbacks) {
110347
+ sendDtmf(handleId, callbacks);
110348
+ },
110349
+ consentDialog: callbacks.consentDialog,
110350
+ connectionState: callbacks.connectionState,
110351
+ iceState: callbacks.iceState,
110352
+ mediaState: callbacks.mediaState,
110353
+ webrtcState: callbacks.webrtcState,
110354
+ slowLink: callbacks.slowLink,
110355
+ onmessage: callbacks.onmessage,
110356
+ createOffer: function (callbacks) {
110357
+ prepareWebrtc(handleId, true, callbacks);
110358
+ },
110359
+ createAnswer: function (callbacks) {
110360
+ prepareWebrtc(handleId, false, callbacks);
110361
+ },
110362
+ handleRemoteJsep: function (callbacks) {
110363
+ prepareWebrtcPeer(handleId, callbacks);
110364
+ },
110365
+ replaceTracks: function (callbacks) {
110366
+ replaceTracks(handleId, callbacks);
110367
+ },
110368
+ getLocalTracks: function () {
110369
+ return getLocalTracks(handleId);
110370
+ },
110371
+ getRemoteTracks: function () {
110372
+ return getRemoteTracks(handleId);
110373
+ },
110374
+ onlocaltrack: callbacks.onlocaltrack,
110375
+ onremotetrack: callbacks.onremotetrack,
110376
+ ondata: callbacks.ondata,
110377
+ ondataopen: callbacks.ondataopen,
110378
+ oncleanup: callbacks.oncleanup,
110379
+ ondetached: callbacks.ondetached,
110380
+ hangup: function (sendRequest) {
110381
+ cleanupWebrtc(handleId, sendRequest === true);
110382
+ },
110383
+ detach: function (callbacks) {
110384
+ destroyHandle(handleId, callbacks);
110385
+ }
110386
+ };
110387
+ pluginHandles.set(handleId, pluginHandle);
110388
+ callbacks.success(pluginHandle);
110389
+ },
110390
+ error: function (textStatus, errorThrown) {
110391
+ Janus.error(textStatus + ":", errorThrown); // FIXME
110392
+ if (errorThrown === "") callbacks.error(textStatus + ": Is the server down?");else callbacks.error(textStatus + ": " + errorThrown);
110393
+ }
110394
+ });
110395
+ }
110396
+
110397
+ // Private method to send a message
110398
+ function sendMessage(handleId, callbacks) {
110399
+ callbacks = callbacks || {};
110400
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
110401
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : Janus.noop;
110402
+ if (!connected) {
110403
+ Janus.warn("Is the server down? (connected=false)");
110404
+ callbacks.error("Is the server down? (connected=false)");
110405
+ return;
110406
+ }
110407
+ let pluginHandle = pluginHandles.get(handleId);
110408
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
110409
+ Janus.warn("Invalid handle");
110410
+ callbacks.error("Invalid handle");
110411
+ return;
110412
+ }
110413
+ let message = callbacks.message;
110414
+ let jsep = callbacks.jsep;
110415
+ let transaction = Janus.randomString(12);
110416
+ let request = {
110417
+ "janus": "message",
110418
+ "body": message,
110419
+ "transaction": transaction
110420
+ };
110421
+ if (pluginHandle.token) request["token"] = pluginHandle.token;
110422
+ if (apisecret) request["apisecret"] = apisecret;
110423
+ if (jsep) {
110424
+ request.jsep = {
110425
+ type: jsep.type,
110426
+ sdp: jsep.sdp
110427
+ };
110428
+ if (jsep.e2ee) request.jsep.e2ee = true;
110429
+ if (jsep.rid_order === "hml" || jsep.rid_order === "lmh") request.jsep.rid_order = jsep.rid_order;
110430
+ if (jsep.force_relay) request.jsep.force_relay = true;
110431
+ // Check if there's SVC video streams to tell Janus about
110432
+ let svc = null;
110433
+ let config = pluginHandle.webrtcStuff;
110434
+ if (config.pc) {
110435
+ let transceivers = config.pc.getTransceivers();
110436
+ if (transceivers && transceivers.length > 0) {
110437
+ for (let mindex in transceivers) {
110438
+ let tr = transceivers[mindex];
110439
+ if (tr && tr.sender && tr.sender.track && tr.sender.track.kind === 'video') {
110440
+ let params = tr.sender.getParameters();
110441
+ if (params && params.encodings && params.encodings.length === 1 && params.encodings[0] && params.encodings[0].scalabilityMode) {
110442
+ // This video stream uses SVC
110443
+ if (!svc) svc = [];
110444
+ svc.push({
110445
+ mindex: parseInt(mindex),
110446
+ mid: tr.mid,
110447
+ svc: params.encodings[0].scalabilityMode
110448
+ });
110449
+ }
110450
+ }
110451
+ }
110452
+ }
110453
+ }
110454
+ if (svc) request.jsep.svc = svc;
110455
+ }
110456
+ Janus.debug("Sending message to plugin (handle=" + handleId + "):");
110457
+ Janus.debug(request);
110458
+ if (websockets) {
110459
+ request["session_id"] = sessionId;
110460
+ request["handle_id"] = handleId;
110461
+ transactions.set(transaction, function (json) {
110462
+ Janus.debug("Message sent!");
110463
+ Janus.debug(json);
110464
+ if (json["janus"] === "success") {
110465
+ // We got a success, must have been a synchronous transaction
110466
+ let plugindata = json["plugindata"];
110467
+ if (!plugindata) {
110468
+ Janus.warn("Request succeeded, but missing plugindata...");
110469
+ callbacks.success();
110470
+ return;
110471
+ }
110472
+ Janus.log("Synchronous transaction successful (" + plugindata["plugin"] + ")");
110473
+ let data = plugindata["data"];
110474
+ Janus.debug(data);
110475
+ callbacks.success(data);
110476
+ return;
110477
+ } else if (json["janus"] !== "ack") {
110478
+ // Not a success and not an ack, must be an error
110479
+ if (json["error"]) {
110480
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
110481
+ callbacks.error(json["error"].code + " " + json["error"].reason);
110482
+ } else {
110483
+ Janus.error("Unknown error"); // FIXME
110484
+ callbacks.error("Unknown error");
110485
+ }
110486
+ return;
110487
+ }
110488
+ // If we got here, the plugin decided to handle the request asynchronously
110489
+ callbacks.success();
110490
+ });
110491
+ ws.send(JSON.stringify(request));
110492
+ return;
110493
+ }
110494
+ Janus.httpAPICall(server + "/" + sessionId + "/" + handleId, {
110495
+ verb: 'POST',
110496
+ withCredentials: withCredentials,
110497
+ body: request,
110498
+ success: function (json) {
110499
+ Janus.debug("Message sent!");
110500
+ Janus.debug(json);
110501
+ if (json["janus"] === "success") {
110502
+ // We got a success, must have been a synchronous transaction
110503
+ let plugindata = json["plugindata"];
110504
+ if (!plugindata) {
110505
+ Janus.warn("Request succeeded, but missing plugindata...");
110506
+ callbacks.success();
110507
+ return;
110508
+ }
110509
+ Janus.log("Synchronous transaction successful (" + plugindata["plugin"] + ")");
110510
+ let data = plugindata["data"];
110511
+ Janus.debug(data);
110512
+ callbacks.success(data);
110513
+ return;
110514
+ } else if (json["janus"] !== "ack") {
110515
+ // Not a success and not an ack, must be an error
110516
+ if (json["error"]) {
110517
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
110518
+ callbacks.error(json["error"].code + " " + json["error"].reason);
110519
+ } else {
110520
+ Janus.error("Unknown error"); // FIXME
110521
+ callbacks.error("Unknown error");
110522
+ }
110523
+ return;
110524
+ }
110525
+ // If we got here, the plugin decided to handle the request asynchronously
110526
+ callbacks.success();
110527
+ },
110528
+ error: function (textStatus, errorThrown) {
110529
+ Janus.error(textStatus + ":", errorThrown); // FIXME
110530
+ callbacks.error(textStatus + ": " + errorThrown);
110531
+ }
110532
+ });
110533
+ }
110534
+
110535
+ // Private method to send a trickle candidate
110536
+ function sendTrickleCandidate(handleId, candidate) {
110537
+ if (!connected) {
110538
+ Janus.warn("Is the server down? (connected=false)");
110539
+ return;
110540
+ }
110541
+ let pluginHandle = pluginHandles.get(handleId);
110542
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
110543
+ Janus.warn("Invalid handle");
110544
+ return;
110545
+ }
110546
+ let request = {
110547
+ "janus": "trickle",
110548
+ "candidate": candidate,
110549
+ "transaction": Janus.randomString(12)
110550
+ };
110551
+ if (pluginHandle.token) request["token"] = pluginHandle.token;
110552
+ if (apisecret) request["apisecret"] = apisecret;
110553
+ Janus.vdebug("Sending trickle candidate (handle=" + handleId + "):");
110554
+ Janus.vdebug(request);
110555
+ if (websockets) {
110556
+ request["session_id"] = sessionId;
110557
+ request["handle_id"] = handleId;
110558
+ ws.send(JSON.stringify(request));
110559
+ return;
110560
+ }
110561
+ Janus.httpAPICall(server + "/" + sessionId + "/" + handleId, {
110562
+ verb: 'POST',
110563
+ withCredentials: withCredentials,
110564
+ body: request,
110565
+ success: function (json) {
110566
+ Janus.vdebug("Candidate sent!");
110567
+ Janus.vdebug(json);
110568
+ if (json["janus"] !== "ack") {
110569
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
110570
+ return;
110571
+ }
110572
+ },
110573
+ error: function (textStatus, errorThrown) {
110574
+ Janus.error(textStatus + ":", errorThrown); // FIXME
110575
+ }
110576
+ });
110577
+ }
110578
+
110579
+ // Private method to create a data channel
110580
+ function createDataChannel(handleId, dclabel, dcprotocol, incoming, pendingData) {
110581
+ let pluginHandle = pluginHandles.get(handleId);
110582
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
110583
+ Janus.warn("Invalid handle");
110584
+ return;
110585
+ }
110586
+ let config = pluginHandle.webrtcStuff;
110587
+ if (!config.pc) {
110588
+ Janus.warn("Invalid PeerConnection");
110589
+ return;
110590
+ }
110591
+ let onDataChannelMessage = function (event) {
110592
+ Janus.log('Received message on data channel:', event);
110593
+ let label = event.target.label;
110594
+ pluginHandle.ondata(event.data, label);
110595
+ };
110596
+ let onDataChannelStateChange = function (event) {
110597
+ Janus.log('Received state change on data channel:', event);
110598
+ let label = event.target.label;
110599
+ let protocol = event.target.protocol;
110600
+ let dcState = config.dataChannel[label] ? config.dataChannel[label].readyState : "null";
110601
+ Janus.log('State change on <' + label + '> data channel: ' + dcState);
110602
+ if (dcState === 'open') {
110603
+ // Any pending messages to send?
110604
+ if (config.dataChannel[label].pending && config.dataChannel[label].pending.length > 0) {
110605
+ Janus.log("Sending pending messages on <" + label + ">:", config.dataChannel[label].pending.length);
110606
+ for (let data of config.dataChannel[label].pending) {
110607
+ Janus.log("Sending data on data channel <" + label + ">");
110608
+ Janus.debug(data);
110609
+ config.dataChannel[label].send(data);
110610
+ }
110611
+ config.dataChannel[label].pending = [];
110612
+ }
110613
+ // Notify the open data channel
110614
+ pluginHandle.ondataopen(label, protocol);
110615
+ }
110616
+ };
110617
+ let onDataChannelError = function (error) {
110618
+ Janus.error('Got error on data channel:', error);
110619
+ // TODO
110620
+ };
110621
+ if (!incoming) {
110622
+ // FIXME Add options (ordered, maxRetransmits, etc.)
110623
+ let dcoptions = config.dataChannelOptions;
110624
+ if (dcprotocol) dcoptions.protocol = dcprotocol;
110625
+ config.dataChannel[dclabel] = config.pc.createDataChannel(dclabel, dcoptions);
110626
+ } else {
110627
+ // The channel was created by Janus
110628
+ config.dataChannel[dclabel] = incoming;
110629
+ }
110630
+ config.dataChannel[dclabel].onmessage = onDataChannelMessage;
110631
+ config.dataChannel[dclabel].onopen = onDataChannelStateChange;
110632
+ config.dataChannel[dclabel].onclose = onDataChannelStateChange;
110633
+ config.dataChannel[dclabel].onerror = onDataChannelError;
110634
+ config.dataChannel[dclabel].pending = [];
110635
+ if (pendingData) config.dataChannel[dclabel].pending.push(pendingData);
110636
+ }
110637
+
110638
+ // Private method to send a data channel message
110639
+ function sendData(handleId, callbacks) {
110640
+ callbacks = callbacks || {};
110641
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
110642
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : Janus.noop;
110643
+ let pluginHandle = pluginHandles.get(handleId);
110644
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
110645
+ Janus.warn("Invalid handle");
110646
+ callbacks.error("Invalid handle");
110647
+ return;
110648
+ }
110649
+ let config = pluginHandle.webrtcStuff;
110650
+ let data = callbacks.text || callbacks.data;
110651
+ if (!data) {
110652
+ Janus.warn("Invalid data");
110653
+ callbacks.error("Invalid data");
110654
+ return;
110655
+ }
110656
+ let label = callbacks.label ? callbacks.label : Janus.dataChanDefaultLabel;
110657
+ if (!config.dataChannel[label]) {
110658
+ // Create new data channel and wait for it to open
110659
+ createDataChannel(handleId, label, callbacks.protocol, false, data, callbacks.protocol);
110660
+ callbacks.success();
110661
+ return;
110662
+ }
110663
+ if (config.dataChannel[label].readyState !== "open") {
110664
+ config.dataChannel[label].pending.push(data);
110665
+ callbacks.success();
110666
+ return;
110667
+ }
110668
+ Janus.log("Sending data on data channel <" + label + ">");
110669
+ Janus.debug(data);
110670
+ config.dataChannel[label].send(data);
110671
+ callbacks.success();
110672
+ }
110673
+
110674
+ // Private method to send a DTMF tone
110675
+ function sendDtmf(handleId, callbacks) {
110676
+ callbacks = callbacks || {};
110677
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
110678
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : Janus.noop;
110679
+ let pluginHandle = pluginHandles.get(handleId);
110680
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
110681
+ Janus.warn("Invalid handle");
110682
+ callbacks.error("Invalid handle");
110683
+ return;
110684
+ }
110685
+ let config = pluginHandle.webrtcStuff;
110686
+ if (!config.dtmfSender) {
110687
+ // Create the DTMF sender the proper way, if possible
110688
+ if (config.pc) {
110689
+ let senders = config.pc.getSenders();
110690
+ let audioSender = senders.find(function (sender) {
110691
+ return sender.track && sender.track.kind === 'audio';
110692
+ });
110693
+ if (!audioSender) {
110694
+ Janus.warn("Invalid DTMF configuration (no audio track)");
110695
+ callbacks.error("Invalid DTMF configuration (no audio track)");
110696
+ return;
110697
+ }
110698
+ config.dtmfSender = audioSender.dtmf;
110699
+ if (config.dtmfSender) {
110700
+ Janus.log("Created DTMF Sender");
110701
+ config.dtmfSender.ontonechange = function (tone) {
110702
+ Janus.debug("Sent DTMF tone: " + tone.tone);
110703
+ };
110704
+ }
110705
+ }
110706
+ if (!config.dtmfSender) {
110707
+ Janus.warn("Invalid DTMF configuration");
110708
+ callbacks.error("Invalid DTMF configuration");
110709
+ return;
110710
+ }
110711
+ }
110712
+ let dtmf = callbacks.dtmf;
110713
+ if (!dtmf) {
110714
+ Janus.warn("Invalid DTMF parameters");
110715
+ callbacks.error("Invalid DTMF parameters");
110716
+ return;
110717
+ }
110718
+ let tones = dtmf.tones;
110719
+ if (!tones) {
110720
+ Janus.warn("Invalid DTMF string");
110721
+ callbacks.error("Invalid DTMF string");
110722
+ return;
110723
+ }
110724
+ let duration = typeof dtmf.duration === 'number' ? dtmf.duration : 500; // We choose 500ms as the default duration for a tone
110725
+ let gap = typeof dtmf.gap === 'number' ? dtmf.gap : 50; // We choose 50ms as the default gap between tones
110726
+ Janus.debug("Sending DTMF string " + tones + " (duration " + duration + "ms, gap " + gap + "ms)");
110727
+ config.dtmfSender.insertDTMF(tones, duration, gap);
110728
+ callbacks.success();
110729
+ }
110730
+
110731
+ // Private method to destroy a plugin handle
110732
+ function destroyHandle(handleId, callbacks) {
110733
+ callbacks = callbacks || {};
110734
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
110735
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : Janus.noop;
110736
+ let noRequest = callbacks.noRequest === true;
110737
+ Janus.log("Destroying handle " + handleId + " (only-locally=" + noRequest + ")");
110738
+ cleanupWebrtc(handleId);
110739
+ let pluginHandle = pluginHandles.get(handleId);
110740
+ if (!pluginHandle || pluginHandle.detached) {
110741
+ // Plugin was already detached by Janus, calling detach again will return a handle not found error, so just exit here
110742
+ pluginHandles.delete(handleId);
110743
+ callbacks.success();
110744
+ return;
110745
+ }
110746
+ pluginHandle.detached = true;
110747
+ if (noRequest) {
110748
+ // We're only removing the handle locally
110749
+ pluginHandles.delete(handleId);
110750
+ callbacks.success();
110751
+ return;
110752
+ }
110753
+ if (!connected) {
110754
+ Janus.warn("Is the server down? (connected=false)");
110755
+ callbacks.error("Is the server down? (connected=false)");
110756
+ return;
110757
+ }
110758
+ let request = {
110759
+ "janus": "detach",
110760
+ "transaction": Janus.randomString(12)
110761
+ };
110762
+ if (pluginHandle.token) request["token"] = pluginHandle.token;
110763
+ if (apisecret) request["apisecret"] = apisecret;
110764
+ if (websockets) {
110765
+ request["session_id"] = sessionId;
110766
+ request["handle_id"] = handleId;
110767
+ ws.send(JSON.stringify(request));
110768
+ pluginHandles.delete(handleId);
110769
+ callbacks.success();
110770
+ return;
110771
+ }
110772
+ Janus.httpAPICall(server + "/" + sessionId + "/" + handleId, {
110773
+ verb: 'POST',
110774
+ withCredentials: withCredentials,
110775
+ body: request,
110776
+ success: function (json) {
110777
+ Janus.log("Destroyed handle:");
110778
+ Janus.debug(json);
110779
+ if (json["janus"] !== "success") {
110780
+ Janus.error("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
110781
+ }
110782
+ pluginHandles.delete(handleId);
110783
+ callbacks.success();
110784
+ },
110785
+ error: function (textStatus, errorThrown) {
110786
+ Janus.error(textStatus + ":", errorThrown); // FIXME
110787
+ // We cleanup anyway
110788
+ pluginHandles.delete(handleId);
110789
+ callbacks.success();
110790
+ }
110791
+ });
110792
+ }
110793
+
110794
+ // WebRTC stuff
110795
+ // Helper function to create a new PeerConnection, if we need one
110796
+ function createPeerconnectionIfNeeded(handleId, callbacks) {
110797
+ let pluginHandle = pluginHandles.get(handleId);
110798
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
110799
+ Janus.warn("Invalid handle");
110800
+ throw "Invalid handle";
110801
+ }
110802
+ let config = pluginHandle.webrtcStuff;
110803
+ if (config.pc) {
110804
+ // Nothing to do, we have a PeerConnection already
110805
+ return;
110806
+ }
110807
+ let pc_config = {
110808
+ iceServers: iceServers,
110809
+ iceTransportPolicy: iceTransportPolicy,
110810
+ bundlePolicy: bundlePolicy
110811
+ };
110812
+ pc_config.sdpSemantics = 'unified-plan';
110813
+ // Check if a sender or receiver transform has been provided
110814
+ let insertableStreams = false;
110815
+ if (callbacks.tracks) {
110816
+ for (let track of callbacks.tracks) {
110817
+ if (track.transforms && (track.transforms.sender || track.transforms.receiver)) {
110818
+ insertableStreams = true;
110819
+ break;
110820
+ }
110821
+ }
110822
+ }
110823
+ if (callbacks.externalEncryption) {
110824
+ insertableStreams = true;
110825
+ config.externalEncryption = true;
110826
+ }
110827
+ if (RTCRtpSender && (RTCRtpSender.prototype.createEncodedStreams || RTCRtpSender.prototype.createEncodedAudioStreams && RTCRtpSender.prototype.createEncodedVideoStreams) && insertableStreams) {
110828
+ config.insertableStreams = true;
110829
+ pc_config.forceEncodedAudioInsertableStreams = true;
110830
+ pc_config.forceEncodedVideoInsertableStreams = true;
110831
+ pc_config.encodedInsertableStreams = true;
110832
+ }
110833
+ Janus.log('Creating PeerConnection');
110834
+ config.pc = new RTCPeerConnection(pc_config);
110835
+ Janus.debug(config.pc);
110836
+ if (config.pc.getStats) {
110837
+ // FIXME
110838
+ config.volume = {};
110839
+ config.bitrate.value = '0 kbits/sec';
110840
+ }
110841
+ Janus.log('Preparing local SDP and gathering candidates (trickle=' + config.trickle + ')');
110842
+ config.pc.onconnectionstatechange = function () {
110843
+ if (config.pc) pluginHandle.connectionState(config.pc.connectionState);
110844
+ };
110845
+ config.pc.oniceconnectionstatechange = function () {
110846
+ if (config.pc) pluginHandle.iceState(config.pc.iceConnectionState);
110847
+ };
110848
+ config.pc.onicecandidate = function (event) {
110849
+ if (!event.candidate || event.candidate.candidate && event.candidate.candidate.indexOf('endOfCandidates') > 0) {
110850
+ Janus.log('End of candidates.');
110851
+ config.iceDone = true;
110852
+ if (config.trickle === true) {
110853
+ // Notify end of candidates
110854
+ sendTrickleCandidate(handleId, {
110855
+ completed: true
110856
+ });
110857
+ } else {
110858
+ // No trickle, time to send the complete SDP (including all candidates)
110859
+ sendSDP(handleId, callbacks);
110860
+ }
110861
+ } else {
110862
+ // JSON.stringify doesn't work on some WebRTC objects anymore
110863
+ // See https://code.google.com/p/chromium/issues/detail?id=467366
110864
+ let candidate = {
110865
+ candidate: event.candidate.candidate,
110866
+ sdpMid: event.candidate.sdpMid,
110867
+ sdpMLineIndex: event.candidate.sdpMLineIndex
110868
+ };
110869
+ if (config.trickle === true) {
110870
+ // Send candidate
110871
+ sendTrickleCandidate(handleId, candidate);
110872
+ }
110873
+ }
110874
+ };
110875
+ config.pc.ontrack = function (event) {
110876
+ Janus.log('Handling Remote Track', event);
110877
+ if (!event.streams) return;
110878
+ if (!event.track) return;
110879
+ // Notify about the new track event
110880
+ let mid = event.transceiver ? event.transceiver.mid : event.track.id;
110881
+ try {
110882
+ if (event.transceiver && event.transceiver.mid && event.track.id) {
110883
+ // Keep track of the mapping between track ID and mid, since
110884
+ // when a track is removed the transceiver may be gone already
110885
+ if (!pluginHandle.mids) pluginHandle.mids = {};
110886
+ pluginHandle.mids[event.track.id] = event.transceiver.mid;
110887
+ }
110888
+ pluginHandle.onremotetrack(event.track, mid, true, {
110889
+ reason: 'created'
110890
+ });
110891
+ } catch (e) {
110892
+ Janus.error("Error calling onremotetrack", e);
110893
+ }
110894
+ if (event.track.onended) return;
110895
+ let trackMutedTimeoutId = null;
110896
+ Janus.log('Adding onended callback to track:', event.track);
110897
+ event.track.onended = function (ev) {
110898
+ Janus.log('Remote track removed:', ev);
110899
+ clearTimeout(trackMutedTimeoutId);
110900
+ // Notify the application
110901
+ let transceivers = config.pc ? config.pc.getTransceivers() : null;
110902
+ let transceiver = transceivers ? transceivers.find(t => t.receiver.track === ev.target) : null;
110903
+ let mid = transceiver ? transceiver.mid : ev.target.id;
110904
+ if (mid === ev.target.id && pluginHandle.mids && pluginHandle.mids[event.track.id]) mid = pluginHandle.mids[event.track.id];
110905
+ try {
110906
+ pluginHandle.onremotetrack(ev.target, mid, false, {
110907
+ reason: 'ended'
110908
+ });
110909
+ } catch (e) {
110910
+ Janus.error("Error calling onremotetrack on removal", e);
110911
+ }
110912
+ delete pluginHandle.mids[event.track.id];
110913
+ };
110914
+ event.track.onmute = function (ev) {
110915
+ Janus.log('Remote track muted:', ev);
110916
+ if (!trackMutedTimeoutId) {
110917
+ trackMutedTimeoutId = setTimeout(function () {
110918
+ Janus.log('Removing remote track');
110919
+ // Notify the application the track is gone
110920
+ let transceivers = config.pc ? config.pc.getTransceivers() : null;
110921
+ let transceiver = transceivers ? transceivers.find(t => t.receiver.track === ev.target) : null;
110922
+ let mid = transceiver ? transceiver.mid : ev.target.id;
110923
+ if (mid === ev.target.id && pluginHandle.mids && pluginHandle.mids[event.track.id]) mid = pluginHandle.mids[event.track.id];
110924
+ try {
110925
+ pluginHandle.onremotetrack(ev.target, mid, false, {
110926
+ reason: 'mute'
110927
+ });
110928
+ } catch (e) {
110929
+ Janus.error("Error calling onremotetrack on mute", e);
110930
+ }
110931
+ trackMutedTimeoutId = null;
110932
+ // Chrome seems to raise mute events only at multiples of 834ms;
110933
+ // we set the timeout to three times this value (rounded to 840ms)
110934
+ }, 3 * 840);
110935
+ }
110936
+ };
110937
+ event.track.onunmute = function (ev) {
110938
+ Janus.log('Remote track flowing again:', ev);
110939
+ if (trackMutedTimeoutId != null) {
110940
+ clearTimeout(trackMutedTimeoutId);
110941
+ trackMutedTimeoutId = null;
110942
+ } else {
110943
+ try {
110944
+ // Notify the application the track is back
110945
+ let transceivers = config.pc ? config.pc.getTransceivers() : null;
110946
+ let transceiver = transceivers ? transceivers.find(t => t.receiver.track === ev.target) : null;
110947
+ let mid = transceiver ? transceiver.mid : ev.target.id;
110948
+ pluginHandle.onremotetrack(ev.target, mid, true, {
110949
+ reason: 'unmute'
110950
+ });
110951
+ } catch (e) {
110952
+ Janus.error("Error calling onremotetrack on unmute", e);
110953
+ }
110954
+ }
110955
+ };
110956
+ };
110957
+ }
110958
+
110959
+ // Helper function used when creating either an offer or answer: it
110960
+ // prepares what needs to be prepared, including creating a new
110961
+ // PeerConnection (if needed) and updating the tracks configuration,
110962
+ // before invoking the function to actually generate the offer/answer
110963
+ async function prepareWebrtc(handleId, offer, callbacks) {
110964
+ callbacks = callbacks || {};
110965
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
110966
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : webrtcError;
110967
+ let jsep = callbacks.jsep;
110968
+ if (offer && jsep) {
110969
+ Janus.error("Provided a JSEP to a createOffer");
110970
+ callbacks.error("Provided a JSEP to a createOffer");
110971
+ return;
110972
+ } else if (!offer && (!jsep || !jsep.type || !jsep.sdp)) {
110973
+ Janus.error("A valid JSEP is required for createAnswer");
110974
+ callbacks.error("A valid JSEP is required for createAnswer");
110975
+ return;
110976
+ }
110977
+ // If the deprecated media was provided instead of tracks, translate it
110978
+ if (callbacks.media && !callbacks.tracks) {
110979
+ callbacks.tracks = Janus.mediaToTracks(callbacks.media);
110980
+ if (callbacks.simulcast === true || callbacks.simulcast2 === true || callbacks.svc) {
110981
+ // Find the video track and add simulcast/SVC info there
110982
+ for (let track of callbacks.tracks) {
110983
+ if (track.type === 'video') {
110984
+ if (callbacks.simulcast === true || callbacks.simulcast2 === true) track.simulcast = true;else if (callbacks.svc) track.svc = callbacks.svc;
110985
+ break;
110986
+ }
110987
+ }
110988
+ }
110989
+ Janus.warn('Deprecated media object passed, use tracks instead. Automatically translated to:', callbacks.tracks);
110990
+ }
110991
+ // Check that callbacks.array is a valid array
110992
+ if (callbacks.tracks && !Array.isArray(callbacks.tracks)) {
110993
+ Janus.error("Tracks must be an array");
110994
+ callbacks.error("Tracks must be an array");
110995
+ return;
110996
+ }
110997
+ // Get the plugin handle
110998
+ let pluginHandle = pluginHandles.get(handleId);
110999
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111000
+ Janus.warn("Invalid handle");
111001
+ callbacks.error("Invalid handle");
111002
+ return;
111003
+ }
111004
+ let config = pluginHandle.webrtcStuff;
111005
+ config.trickle = isTrickleEnabled(callbacks.trickle);
111006
+ try {
111007
+ // Create a PeerConnection, if needed
111008
+ createPeerconnectionIfNeeded(handleId, callbacks);
111009
+ if (offer) {
111010
+ // Capture devices and setup tracks, if needed
111011
+ await captureDevices(handleId, callbacks);
111012
+ }
111013
+ // Create offer or answer now (depending on the context)
111014
+ if (!jsep) {
111015
+ let offer = await createOffer(handleId, callbacks);
111016
+ callbacks.success(offer);
111017
+ } else {
111018
+ await config.pc.setRemoteDescription(jsep);
111019
+ Janus.log("Remote description accepted!");
111020
+ config.remoteSdp = jsep.sdp;
111021
+ // Any trickle candidate we cached?
111022
+ if (config.candidates && config.candidates.length > 0) {
111023
+ for (let i = 0; i < config.candidates.length; i++) {
111024
+ let candidate = config.candidates[i];
111025
+ Janus.debug("Adding remote candidate:", candidate);
111026
+ if (!candidate || candidate.completed === true) {
111027
+ // end-of-candidates
111028
+ config.pc.addIceCandidate(Janus.endOfCandidates);
111029
+ } else {
111030
+ // New candidate
111031
+ config.pc.addIceCandidate(candidate);
111032
+ }
111033
+ }
111034
+ config.candidates = [];
111035
+ }
111036
+ // Capture devices and setup tracks, if needed
111037
+ await captureDevices(handleId, callbacks);
111038
+ // Create the answer now
111039
+ let answer = await createAnswer(handleId, callbacks);
111040
+ callbacks.success(answer);
111041
+ }
111042
+ } catch (err) {
111043
+ Janus.error(err);
111044
+ callbacks.error(err);
111045
+ }
111046
+ }
111047
+ function prepareWebrtcPeer(handleId, callbacks) {
111048
+ callbacks = callbacks || {};
111049
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
111050
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : webrtcError;
111051
+ callbacks.customizeSdp = typeof callbacks.customizeSdp == "function" ? callbacks.customizeSdp : Janus.noop;
111052
+ let jsep = callbacks.jsep;
111053
+ let pluginHandle = pluginHandles.get(handleId);
111054
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111055
+ Janus.warn("Invalid handle");
111056
+ callbacks.error("Invalid handle");
111057
+ return;
111058
+ }
111059
+ let config = pluginHandle.webrtcStuff;
111060
+ if (jsep) {
111061
+ if (!config.pc) {
111062
+ Janus.warn("Wait, no PeerConnection?? if this is an answer, use createAnswer and not handleRemoteJsep");
111063
+ callbacks.error("No PeerConnection: if this is an answer, use createAnswer and not handleRemoteJsep");
111064
+ return;
111065
+ }
111066
+ callbacks.customizeSdp(jsep);
111067
+ config.pc.setRemoteDescription(jsep).then(function () {
111068
+ Janus.log("Remote description accepted!");
111069
+ config.remoteSdp = jsep.sdp;
111070
+ // Any trickle candidate we cached?
111071
+ if (config.candidates && config.candidates.length > 0) {
111072
+ for (let i = 0; i < config.candidates.length; i++) {
111073
+ let candidate = config.candidates[i];
111074
+ Janus.debug("Adding remote candidate:", candidate);
111075
+ if (!candidate || candidate.completed === true) {
111076
+ // end-of-candidates
111077
+ config.pc.addIceCandidate(Janus.endOfCandidates);
111078
+ } else {
111079
+ // New candidate
111080
+ config.pc.addIceCandidate(candidate);
111081
+ }
111082
+ }
111083
+ config.candidates = [];
111084
+ }
111085
+ // Done
111086
+ callbacks.success();
111087
+ }, callbacks.error);
111088
+ } else {
111089
+ callbacks.error("Invalid JSEP");
111090
+ }
111091
+ }
111092
+ async function createOffer(handleId, callbacks) {
111093
+ callbacks = callbacks || {};
111094
+ callbacks.customizeSdp = typeof callbacks.customizeSdp == "function" ? callbacks.customizeSdp : Janus.noop;
111095
+ let pluginHandle = pluginHandles.get(handleId);
111096
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111097
+ Janus.warn("Invalid handle");
111098
+ throw "Invalid handle";
111099
+ }
111100
+ let config = pluginHandle.webrtcStuff;
111101
+ Janus.log("Creating offer (iceDone=" + config.iceDone + ")");
111102
+ // https://code.google.com/p/webrtc/issues/detail?id=3508
111103
+ let mediaConstraints = {};
111104
+ let iceRestart = callbacks.iceRestart === true;
111105
+ // If we need an ICE restart, set the related constraint
111106
+ if (iceRestart) mediaConstraints.iceRestart = true;
111107
+ Janus.debug(mediaConstraints);
111108
+ let offer = await config.pc.createOffer(mediaConstraints);
111109
+ Janus.debug(offer);
111110
+ // JSON.stringify doesn't work on some WebRTC objects anymore
111111
+ // See https://code.google.com/p/chromium/issues/detail?id=467366
111112
+ let jsep = {
111113
+ type: 'offer',
111114
+ sdp: offer.sdp
111115
+ };
111116
+ callbacks.customizeSdp(jsep);
111117
+ offer.sdp = jsep.sdp;
111118
+ Janus.log("Setting local description");
111119
+ config.mySdp = {
111120
+ type: 'offer',
111121
+ sdp: offer.sdp
111122
+ };
111123
+ await config.pc.setLocalDescription(offer);
111124
+ config.mediaConstraints = mediaConstraints;
111125
+ if (!config.iceDone && !config.trickle) {
111126
+ // FIXME Don't do anything until we have all candidates
111127
+ Janus.log("Waiting for all candidates...");
111128
+ return null;
111129
+ }
111130
+ // If transforms are present, notify Janus that the media is end-to-end encrypted
111131
+ if (config.insertableStreams || config.externalEncryption) offer.e2ee = true;
111132
+ return offer;
111133
+ }
111134
+ async function createAnswer(handleId, callbacks) {
111135
+ callbacks = callbacks || {};
111136
+ callbacks.customizeSdp = typeof callbacks.customizeSdp == "function" ? callbacks.customizeSdp : Janus.noop;
111137
+ let pluginHandle = pluginHandles.get(handleId);
111138
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111139
+ Janus.warn("Invalid handle");
111140
+ throw "Invalid handle";
111141
+ }
111142
+ let config = pluginHandle.webrtcStuff;
111143
+ Janus.log("Creating answer (iceDone=" + config.iceDone + ")");
111144
+ let answer = await config.pc.createAnswer();
111145
+ Janus.debug(answer);
111146
+ // JSON.stringify doesn't work on some WebRTC objects anymore
111147
+ // See https://code.google.com/p/chromium/issues/detail?id=467366
111148
+ let jsep = {
111149
+ type: 'answer',
111150
+ sdp: answer.sdp
111151
+ };
111152
+ callbacks.customizeSdp(jsep);
111153
+ answer.sdp = jsep.sdp;
111154
+ Janus.log("Setting local description");
111155
+ config.mySdp = {
111156
+ type: 'answer',
111157
+ sdp: answer.sdp
111158
+ };
111159
+ await config.pc.setLocalDescription(answer);
111160
+ if (!config.iceDone && !config.trickle) {
111161
+ // FIXME Don't do anything until we have all candidates
111162
+ Janus.log("Waiting for all candidates...");
111163
+ return null;
111164
+ }
111165
+ // If transforms are present, notify Janus that the media is end-to-end encrypted
111166
+ if (config.insertableStreams || config.externalEncryption) answer.e2ee = true;
111167
+ return answer;
111168
+ }
111169
+ function sendSDP(handleId, callbacks) {
111170
+ callbacks = callbacks || {};
111171
+ callbacks.success = typeof callbacks.success == "function" ? callbacks.success : Janus.noop;
111172
+ callbacks.error = typeof callbacks.error == "function" ? callbacks.error : Janus.noop;
111173
+ let pluginHandle = pluginHandles.get(handleId);
111174
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111175
+ Janus.warn("Invalid handle, not sending anything");
111176
+ return;
111177
+ }
111178
+ let config = pluginHandle.webrtcStuff;
111179
+ Janus.log("Sending offer/answer SDP...");
111180
+ if (!config.mySdp) {
111181
+ Janus.warn("Local SDP instance is invalid, not sending anything...");
111182
+ return;
111183
+ }
111184
+ config.mySdp = {
111185
+ type: config.pc.localDescription.type,
111186
+ sdp: config.pc.localDescription.sdp
111187
+ };
111188
+ if (config.trickle === false) config.mySdp["trickle"] = false;
111189
+ Janus.debug(callbacks);
111190
+ config.sdpSent = true;
111191
+ callbacks.success(config.mySdp);
111192
+ }
111193
+ async function replaceTracks(handleId, callbacks) {
111194
+ callbacks = callbacks || {};
111195
+ callbacks.success = typeof callbacks.success == 'function' ? callbacks.success : Janus.noop;
111196
+ callbacks.error = typeof callbacks.error == 'function' ? callbacks.error : Janus.noop;
111197
+ // Check that callbacks.array is a valid array
111198
+ if (callbacks.tracks && !Array.isArray(callbacks.tracks)) {
111199
+ Janus.error('Tracks must be an array');
111200
+ callbacks.error('Tracks must be an array');
111201
+ return;
111202
+ }
111203
+ // Add the replace:true if it's missing
111204
+ for (let track of callbacks.tracks) {
111205
+ if (track.add || !track.replace && !track.remove) track.replace = true;
111206
+ }
111207
+ try {
111208
+ await captureDevices(handleId, callbacks);
111209
+ callbacks.success();
111210
+ } catch (err) {
111211
+ Janus.error(err);
111212
+ callbacks.error(err);
111213
+ }
111214
+ }
111215
+ async function captureDevices(handleId, callbacks) {
111216
+ let pluginHandle = pluginHandles.get(handleId);
111217
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111218
+ Janus.warn('Invalid handle, not sending anything');
111219
+ throw 'Invalid handle';
111220
+ }
111221
+ let config = pluginHandle.webrtcStuff;
111222
+ if (!config.pc) {
111223
+ Janus.warn('Invalid PeerConnection');
111224
+ throw 'Invalid PeerConnection';
111225
+ }
111226
+ let tracks = callbacks.tracks;
111227
+ if (!tracks || !Array.isArray(tracks) || tracks.length === 0) {
111228
+ // Nothing to do
111229
+ return;
111230
+ }
111231
+ let openedConsentDialog = false;
111232
+ // Check if we can/should group getUserMedia calls
111233
+ let groups = {};
111234
+ for (let track of tracks) {
111235
+ delete track.gumGroup;
111236
+ if (!track.type || !['audio', 'video'].includes(track.type)) continue;
111237
+ if (!track.capture || track.capture instanceof MediaStreamTrack) continue;
111238
+ let group = track.group ? track.group : 'default';
111239
+ if (!groups[group]) groups[group] = {};
111240
+ if (groups[group][track.type]) continue;
111241
+ track.gumGroup = group;
111242
+ groups[group][track.type] = track;
111243
+ }
111244
+ let keys = Object.keys(groups);
111245
+ for (let key of keys) {
111246
+ let group = groups[key];
111247
+ if (!group.audio || !group.video) {
111248
+ if (group.audio) delete group.audio.gumGroup;
111249
+ if (group.video) delete group.video.gumGroup;
111250
+ delete groups[key];
111251
+ }
111252
+ }
111253
+ let answer = callbacks.jsep ? true : false;
111254
+ for (let track of tracks) {
111255
+ if (!track.type) {
111256
+ Janus.warn('Missing track type:', track);
111257
+ continue;
111258
+ }
111259
+ if (track.type === 'data') {
111260
+ // Easy enough: create a datachannel if we don't have one already
111261
+ if (config.pc.ondatachannel) {
111262
+ Janus.warn('Data channel exists already, not creating another one');
111263
+ continue;
111264
+ }
111265
+ Janus.log('Creating default data channel');
111266
+ createDataChannel(handleId, Janus.dataChanDefaultLabel, null, false);
111267
+ config.pc.ondatachannel = function (event) {
111268
+ Janus.log('Data channel created by Janus:', event);
111269
+ createDataChannel(handleId, event.channel.label, event.channel.protocol, event.channel);
111270
+ };
111271
+ continue;
111272
+ }
111273
+ if ((typeof track.add === 'undefined' || track.add === null) && (typeof track.remove === 'undefined' || track.remove === null) && (typeof track.replace === 'undefined' || track.replace === null)) {
111274
+ // Let's default to 'add'
111275
+ track.add = true;
111276
+ }
111277
+ if (track.add && track.remove || track.add && track.remove && track.replace) {
111278
+ Janus.warn('Conflicting actions for track, ignoring:', track);
111279
+ continue;
111280
+ }
111281
+ if (track.add && track.replace) {
111282
+ Janus.warn('Both add and replace provided, falling back to replace:', track);
111283
+ delete track.add;
111284
+ } else if (track.remove && track.replace) {
111285
+ Janus.warn('Both remove and replace provided, falling back to remove:', track);
111286
+ delete track.replace;
111287
+ }
111288
+ let kind = track.type;
111289
+ if (track.type === 'screen') kind = 'video'; // FIXME
111290
+ let transceiver = null,
111291
+ sender = null;
111292
+ if (track.mid) {
111293
+ // Search by mid
111294
+ transceiver = config.pc.getTransceivers().find(t => t.mid === track.mid && t.receiver.track.kind === kind);
111295
+ } else if (!track.add) {
111296
+ // Find the first track of this type
111297
+ transceiver = config.pc.getTransceivers().find(t => t.receiver.track.kind === kind);
111298
+ }
111299
+ if (track.replace || track.remove) {
111300
+ if (!transceiver) {
111301
+ Janus.warn("Couldn't find a transceiver for track:", track);
111302
+ continue;
111303
+ }
111304
+ if (!transceiver.sender) {
111305
+ Janus.warn('No sender in the transceiver for track:', track);
111306
+ continue;
111307
+ }
111308
+ sender = transceiver.sender;
111309
+ }
111310
+ if (answer && !transceiver) {
111311
+ transceiver = config.pc.getTransceivers().find(t => t.receiver.track.kind === kind);
111312
+ if (!transceiver) {
111313
+ Janus.warn("Couldn't find a transceiver for track:", track);
111314
+ continue;
111315
+ }
111316
+ }
111317
+ // Capture the new track, if we need to
111318
+ let nt = null,
111319
+ trackId = null;
111320
+ if (track.remove || track.replace) {
111321
+ Janus.log('Removing track from PeerConnection', track);
111322
+ trackId = sender.track ? sender.track.id : null;
111323
+ await sender.replaceTrack(null);
111324
+ // Get rid of the old track
111325
+ if (trackId && config.myStream) {
111326
+ let rt = null;
111327
+ if (kind === 'audio' && config.myStream.getAudioTracks() && config.myStream.getAudioTracks().length) {
111328
+ for (let t of config.myStream.getAudioTracks()) {
111329
+ if (t.id === trackId) {
111330
+ rt = t;
111331
+ Janus.log('Removing audio track:', rt);
111332
+ }
111333
+ }
111334
+ } else if (kind === 'video' && config.myStream.getVideoTracks() && config.myStream.getVideoTracks().length) {
111335
+ for (let t of config.myStream.getVideoTracks()) {
111336
+ if (t.id === trackId) {
111337
+ rt = t;
111338
+ Janus.log('Removing video track:', rt);
111339
+ }
111340
+ }
111341
+ }
111342
+ if (rt) {
111343
+ // Remove the track and notify the application
111344
+ try {
111345
+ config.myStream.removeTrack(rt);
111346
+ pluginHandle.onlocaltrack(rt, false);
111347
+ } catch (e) {
111348
+ Janus.error("Error calling onlocaltrack on removal for renegotiation", e);
111349
+ }
111350
+ // Close the old track (unless we've been asked not to)
111351
+ if (rt.dontStop !== true) {
111352
+ try {
111353
+ rt.stop();
111354
+ // eslint-disable-next-line no-unused-vars
111355
+ } catch (e) {}
111356
+ }
111357
+ }
111358
+ }
111359
+ }
111360
+ if (track.capture) {
111361
+ if (track.gumGroup && groups[track.gumGroup] && groups[track.gumGroup].stream) {
111362
+ // We did a getUserMedia before already
111363
+ let stream = groups[track.gumGroup].stream;
111364
+ nt = track.type === 'audio' ? stream.getAudioTracks()[0] : stream.getVideoTracks()[0];
111365
+ delete groups[track.gumGroup].stream;
111366
+ delete groups[track.gumGroup];
111367
+ delete track.gumGroup;
111368
+ } else if (track.capture instanceof MediaStreamTrack) {
111369
+ // An external track was provided, use that
111370
+ nt = track.capture;
111371
+ } else {
111372
+ if (!openedConsentDialog) {
111373
+ openedConsentDialog = true;
111374
+ pluginHandle.consentDialog(true);
111375
+ }
111376
+ let constraints = Janus.trackConstraints(track),
111377
+ stream = null;
111378
+ if (track.type === 'audio' || track.type === 'video') {
111379
+ // Use getUserMedia: check if we need to group audio and video together
111380
+ if (track.gumGroup) {
111381
+ let otherType = track.type === 'audio' ? 'video' : 'audio';
111382
+ if (groups[track.gumGroup] && groups[track.gumGroup][otherType]) {
111383
+ let otherTrack = groups[track.gumGroup][otherType];
111384
+ let otherConstraints = Janus.trackConstraints(otherTrack);
111385
+ constraints[otherType] = otherConstraints[otherType];
111386
+ }
111387
+ }
111388
+ stream = await navigator.mediaDevices.getUserMedia(constraints);
111389
+ if (track.gumGroup && constraints.audio && constraints.video) {
111390
+ // We just performed a grouped getUserMedia, keep track of the
111391
+ // stream so that we can immediately assign the track later
111392
+ groups[track.gumGroup].stream = stream;
111393
+ delete track.gumGroup;
111394
+ }
111395
+ } else {
111396
+ // Use getDisplayMedia
111397
+ stream = await navigator.mediaDevices.getDisplayMedia(constraints);
111398
+ }
111399
+ nt = track.type === 'audio' ? stream.getAudioTracks()[0] : stream.getVideoTracks()[0];
111400
+ }
111401
+ if (track.replace) {
111402
+ // Replace the track
111403
+ await sender.replaceTrack(nt);
111404
+ // Update the transceiver direction
111405
+ let newDirection = 'sendrecv';
111406
+ if (track.recv === false || transceiver.direction === 'inactive' || transceiver.direction === 'sendonly') newDirection = 'sendonly';
111407
+ if (transceiver.setDirection) transceiver.setDirection(newDirection);else transceiver.direction = newDirection;
111408
+ } else {
111409
+ // FIXME Add as a new track
111410
+ if (!config.myStream) config.myStream = new MediaStream();
111411
+ if (kind === 'audio' || !track.simulcast && !track.svc) {
111412
+ sender = config.pc.addTrack(nt, config.myStream);
111413
+ transceiver = config.pc.getTransceivers().find(t => t.sender === sender);
111414
+ } else if (track.simulcast) {
111415
+ // Standard RID
111416
+ Janus.log('Enabling rid-based simulcasting:', nt);
111417
+ let maxBitrates = getMaxBitrates(track.simulcastMaxBitrates);
111418
+ transceiver = config.pc.addTransceiver(nt, {
111419
+ direction: 'sendrecv',
111420
+ streams: [config.myStream],
111421
+ sendEncodings: track.sendEncodings || [{
111422
+ rid: 'h',
111423
+ active: true,
111424
+ scalabilityMode: 'L1T2',
111425
+ maxBitrate: maxBitrates.high
111426
+ }, {
111427
+ rid: 'm',
111428
+ active: true,
111429
+ scalabilityMode: 'L1T2',
111430
+ maxBitrate: maxBitrates.medium,
111431
+ scaleResolutionDownBy: 2
111432
+ }, {
111433
+ rid: 'l',
111434
+ active: true,
111435
+ scalabilityMode: 'L1T2',
111436
+ maxBitrate: maxBitrates.low,
111437
+ scaleResolutionDownBy: 4
111438
+ }]
111439
+ });
111440
+ } else {
111441
+ Janus.log('Enabling SVC (' + track.svc + '):', nt);
111442
+ transceiver = config.pc.addTransceiver(nt, {
111443
+ direction: 'sendrecv',
111444
+ streams: [config.myStream],
111445
+ sendEncodings: [{
111446
+ scalabilityMode: track.svc
111447
+ }]
111448
+ });
111449
+ }
111450
+ if (!sender) sender = transceiver ? transceiver.sender : null;
111451
+ // Check if we need to override some settings
111452
+ if (track.codec) {
111453
+ if (Janus.webRTCAdapter.browserDetails.browser === 'firefox') {
111454
+ Janus.warn('setCodecPreferences not supported in Firefox, ignoring codec for track:', track);
111455
+ } else if (typeof track.codec !== 'string') {
111456
+ Janus.warn('Invalid codec value, ignoring for track:', track);
111457
+ } else {
111458
+ let mimeType = kind + '/' + track.codec.toLowerCase();
111459
+ let codecs = RTCRtpReceiver.getCapabilities(kind).codecs.filter(function (codec) {
111460
+ return codec.mimeType.toLowerCase() === mimeType;
111461
+ });
111462
+ if (!codecs || codecs.length === 0) {
111463
+ Janus.warn('Codec not supported in this browser for this track, ignoring:', track);
111464
+ } else if (transceiver) {
111465
+ try {
111466
+ transceiver.setCodecPreferences(codecs);
111467
+ } catch (err) {
111468
+ Janus.warn('Failed enforcing codec for this ' + kind + ' track:', err);
111469
+ }
111470
+ }
111471
+ }
111472
+ }
111473
+ if (track.bitrate) {
111474
+ // Override maximum bitrate
111475
+ if (track.simulcast || track.svc) {
111476
+ Janus.warn('Ignoring bitrate for simulcast/SVC track, use sendEncodings for that');
111477
+ } else if (isNaN(track.bitrate) || track.bitrate < 0) {
111478
+ Janus.warn('Ignoring invalid bitrate for track:', track);
111479
+ } else if (sender) {
111480
+ let params = sender.getParameters();
111481
+ if (!params || !params.encodings || params.encodings.length === 0) {
111482
+ Janus.warn('No encodings in the sender parameters, ignoring bitrate for track:', track);
111483
+ } else {
111484
+ params.encodings[0].maxBitrate = track.bitrate;
111485
+ await sender.setParameters(params);
111486
+ }
111487
+ }
111488
+ }
111489
+ if (kind === 'video' && track.framerate) {
111490
+ // Override maximum framerate
111491
+ if (track.simulcast || track.svc) {
111492
+ Janus.warn('Ignoring framerate for simulcast/SVC track, use sendEncodings for that');
111493
+ } else if (isNaN(track.framerate) || track.framerate < 0) {
111494
+ Janus.warn('Ignoring invalid framerate for track:', track);
111495
+ } else if (sender) {
111496
+ let params = sender.getParameters();
111497
+ if (!params || !params.encodings || params.encodings.length === 0) {
111498
+ Janus.warn('No encodings in the sender parameters, ignoring framerate for track:', track);
111499
+ } else {
111500
+ params.encodings[0].maxFramerate = track.framerate;
111501
+ await sender.setParameters(params);
111502
+ }
111503
+ }
111504
+ }
111505
+ // Check if insertable streams are involved
111506
+ if (track.transforms) {
111507
+ if (sender && track.transforms.sender) {
111508
+ // There's a sender transform, set it on the transceiver sender
111509
+ let senderStreams = null;
111510
+ if (RTCRtpSender.prototype.createEncodedStreams) {
111511
+ senderStreams = sender.createEncodedStreams();
111512
+ } else if (RTCRtpSender.prototype.createAudioEncodedStreams || RTCRtpSender.prototype.createEncodedVideoStreams) {
111513
+ if (kind === 'audio') {
111514
+ senderStreams = sender.createEncodedAudioStreams();
111515
+ } else if (kind === 'video') {
111516
+ senderStreams = sender.createEncodedVideoStreams();
111517
+ }
111518
+ }
111519
+ if (senderStreams) {
111520
+ if (senderStreams.readableStream && senderStreams.writableStream) {
111521
+ senderStreams.readableStream.pipeThrough(track.transforms.sender).pipeTo(senderStreams.writableStream);
111522
+ } else if (senderStreams.readable && senderStreams.writable) {
111523
+ senderStreams.readable.pipeThrough(track.transforms.sender).pipeTo(senderStreams.writable);
111524
+ }
111525
+ }
111526
+ }
111527
+ if (transceiver && transceiver.receiver && track.transforms.receiver) {
111528
+ // There's a receiver transform, set it on the transceiver receiver
111529
+ let receiverStreams = null;
111530
+ if (RTCRtpReceiver.prototype.createEncodedStreams) {
111531
+ receiverStreams = transceiver.receiver.createEncodedStreams();
111532
+ } else if (RTCRtpReceiver.prototype.createAudioEncodedStreams || RTCRtpReceiver.prototype.createEncodedVideoStreams) {
111533
+ if (kind === 'audio') {
111534
+ receiverStreams = transceiver.receiver.createEncodedAudioStreams();
111535
+ } else if (kind === 'video') {
111536
+ receiverStreams = transceiver.receiver.createEncodedVideoStreams();
111537
+ }
111538
+ }
111539
+ if (receiverStreams) {
111540
+ if (receiverStreams.readableStream && receiverStreams.writableStream) {
111541
+ receiverStreams.readableStream.pipeThrough(track.transforms.receiver).pipeTo(receiverStreams.writableStream);
111542
+ } else if (receiverStreams.readable && receiverStreams.writable) {
111543
+ receiverStreams.readable.pipeThrough(track.transforms.receiver).pipeTo(receiverStreams.writable);
111544
+ }
111545
+ }
111546
+ }
111547
+ }
111548
+ }
111549
+ if (nt && track.dontStop === true) nt.dontStop = true;
111550
+ } else if (track.recv) {
111551
+ // Maybe a new recvonly track
111552
+ if (!transceiver) transceiver = config.pc.addTransceiver(kind);
111553
+ if (transceiver) {
111554
+ // Check if we need to override some settings
111555
+ if (track.codec) {
111556
+ if (Janus.webRTCAdapter.browserDetails.browser === 'firefox') {
111557
+ Janus.warn('setCodecPreferences not supported in Firefox, ignoring codec for track:', track);
111558
+ } else if (typeof track.codec !== 'string') {
111559
+ Janus.warn('Invalid codec value, ignoring for track:', track);
111560
+ } else {
111561
+ let mimeType = kind + '/' + track.codec.toLowerCase();
111562
+ let codecs = RTCRtpReceiver.getCapabilities(kind).codecs.filter(function (codec) {
111563
+ return codec.mimeType.toLowerCase() === mimeType;
111564
+ });
111565
+ if (!codecs || codecs.length === 0) {
111566
+ Janus.warn('Codec not supported in this browser for this track, ignoring:', track);
111567
+ } else {
111568
+ try {
111569
+ transceiver.setCodecPreferences(codecs);
111570
+ } catch (err) {
111571
+ Janus.warn('Failed enforcing codec for this ' + kind + ' track:', err);
111572
+ }
111573
+ }
111574
+ }
111575
+ }
111576
+ // Check if insertable streams are involved
111577
+ if (transceiver.receiver && track.transforms && track.transforms.receiver) {
111578
+ // There's a receiver transform, set it on the transceiver receiver
111579
+ let receiverStreams = null;
111580
+ if (RTCRtpReceiver.prototype.createEncodedStreams) {
111581
+ receiverStreams = transceiver.receiver.createEncodedStreams();
111582
+ } else if (RTCRtpReceiver.prototype.createAudioEncodedStreams || RTCRtpReceiver.prototype.createEncodedVideoStreams) {
111583
+ if (kind === 'audio') {
111584
+ receiverStreams = transceiver.receiver.createEncodedAudioStreams();
111585
+ } else if (kind === 'video') {
111586
+ receiverStreams = transceiver.receiver.createEncodedVideoStreams();
111587
+ }
111588
+ }
111589
+ if (receiverStreams) {
111590
+ if (receiverStreams.readableStream && receiverStreams.writableStream) {
111591
+ receiverStreams.readableStream.pipeThrough(track.transforms.receiver).pipeTo(receiverStreams.writableStream);
111592
+ } else if (receiverStreams.readable && receiverStreams.writable) {
111593
+ receiverStreams.readable.pipeThrough(track.transforms.receiver).pipeTo(receiverStreams.writable);
111594
+ }
111595
+ }
111596
+ }
111597
+ }
111598
+ }
111599
+ if (nt) {
111600
+ // FIXME Add the new track locally
111601
+ config.myStream.addTrack(nt);
111602
+ // Notify the application about the new local track, if any
111603
+ nt.onended = function (ev) {
111604
+ Janus.log('Local track removed:', ev);
111605
+ try {
111606
+ pluginHandle.onlocaltrack(ev.target, false);
111607
+ } catch (e) {
111608
+ Janus.error("Error calling onlocaltrack following end", e);
111609
+ }
111610
+ };
111611
+ try {
111612
+ pluginHandle.onlocaltrack(nt, true);
111613
+ } catch (e) {
111614
+ Janus.error("Error calling onlocaltrack for track add", e);
111615
+ }
111616
+ }
111617
+ // Update the direction of the transceiver
111618
+ if (transceiver) {
111619
+ let curdir = transceiver.direction,
111620
+ newdir = null;
111621
+ let send = nt && transceiver.sender.track,
111622
+ recv = track.recv !== false && transceiver.receiver.track;
111623
+ if (send && recv) newdir = 'sendrecv';else if (send && !recv) newdir = 'sendonly';else if (!send && recv) newdir = 'recvonly';else if (!send && !recv) newdir = 'inactive';
111624
+ if (newdir && newdir !== curdir) {
111625
+ Janus.warn('Changing direction of transceiver to ' + newdir + ' (was ' + curdir + ')', track);
111626
+ if (transceiver.setDirection) transceiver.setDirection(newdir);else transceiver.direction = newdir;
111627
+ }
111628
+ }
111629
+ }
111630
+ if (openedConsentDialog) pluginHandle.consentDialog(false);
111631
+ }
111632
+ function getLocalTracks(handleId) {
111633
+ let pluginHandle = pluginHandles.get(handleId);
111634
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111635
+ Janus.warn('Invalid handle');
111636
+ return null;
111637
+ }
111638
+ let config = pluginHandle.webrtcStuff;
111639
+ if (!config.pc) {
111640
+ Janus.warn('Invalid PeerConnection');
111641
+ return null;
111642
+ }
111643
+ let tracks = [];
111644
+ let transceivers = config.pc.getTransceivers();
111645
+ for (let tr of transceivers) {
111646
+ let track = null;
111647
+ if (tr.sender && tr.sender.track) {
111648
+ track = {
111649
+ mid: tr.mid
111650
+ };
111651
+ track.type = tr.sender.track.kind;
111652
+ track.id = tr.sender.track.id;
111653
+ track.label = tr.sender.track.label;
111654
+ }
111655
+ if (track) tracks.push(track);
111656
+ }
111657
+ return tracks;
111658
+ }
111659
+ function getRemoteTracks(handleId) {
111660
+ let pluginHandle = pluginHandles.get(handleId);
111661
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111662
+ Janus.warn('Invalid handle');
111663
+ return null;
111664
+ }
111665
+ let config = pluginHandle.webrtcStuff;
111666
+ if (!config.pc) {
111667
+ Janus.warn('Invalid PeerConnection');
111668
+ return null;
111669
+ }
111670
+ let tracks = [];
111671
+ let transceivers = config.pc.getTransceivers();
111672
+ for (let tr of transceivers) {
111673
+ let track = null;
111674
+ if (tr.receiver && tr.receiver.track) {
111675
+ track = {
111676
+ mid: tr.mid
111677
+ };
111678
+ track.type = tr.receiver.track.kind;
111679
+ track.id = tr.receiver.track.id;
111680
+ track.label = tr.receiver.track.label;
111681
+ }
111682
+ if (track) tracks.push(track);
111683
+ }
111684
+ return tracks;
111685
+ }
111686
+ function getVolume(handleId, mid, remote, result) {
111687
+ result = typeof result == "function" ? result : Janus.noop;
111688
+ let pluginHandle = pluginHandles.get(handleId);
111689
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111690
+ Janus.warn("Invalid handle");
111691
+ result(0);
111692
+ return;
111693
+ }
111694
+ let stream = remote ? "remote" : "local";
111695
+ let config = pluginHandle.webrtcStuff;
111696
+ if (!config.volume[stream]) config.volume[stream] = {
111697
+ value: 0
111698
+ };
111699
+ // Start getting the volume, if audioLevel in getStats is supported (apparently
111700
+ // they're only available in Chrome/Safari right now: https://webrtc-stats.callstats.io/)
111701
+ if (config.pc && config.pc.getStats && (Janus.webRTCAdapter.browserDetails.browser === "chrome" || Janus.webRTCAdapter.browserDetails.browser === "safari")) {
111702
+ // Are we interested in a mid in particular?
111703
+ let query = config.pc;
111704
+ if (mid) {
111705
+ let transceiver = config.pc.getTransceivers().find(t => t.mid === mid && t.receiver.track.kind === "audio");
111706
+ if (!transceiver) {
111707
+ Janus.warn("No audio transceiver with mid " + mid);
111708
+ result(0);
111709
+ return;
111710
+ }
111711
+ if (remote && !transceiver.receiver) {
111712
+ Janus.warn("Remote transceiver track unavailable");
111713
+ result(0);
111714
+ return;
111715
+ } else if (!remote && !transceiver.sender) {
111716
+ Janus.warn("Local transceiver track unavailable");
111717
+ result(0);
111718
+ return;
111719
+ }
111720
+ query = remote ? transceiver.receiver : transceiver.sender;
111721
+ }
111722
+ query.getStats().then(function (stats) {
111723
+ stats.forEach(function (res) {
111724
+ if (!res || res.kind !== "audio") return;
111725
+ if (remote && !res.remoteSource || !remote && res.type !== "media-source") return;
111726
+ result(res.audioLevel ? res.audioLevel : 0);
111727
+ });
111728
+ });
111729
+ return config.volume[stream].value;
111730
+ } else {
111731
+ // audioInputLevel and audioOutputLevel seem only available in Chrome? audioLevel
111732
+ // seems to be available on Chrome and Firefox, but they don't seem to work
111733
+ Janus.warn("Getting the " + stream + " volume unsupported by browser");
111734
+ result(0);
111735
+ return;
111736
+ }
111737
+ }
111738
+ function isMuted(handleId, mid, video) {
111739
+ let pluginHandle = pluginHandles.get(handleId);
111740
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111741
+ Janus.warn("Invalid handle");
111742
+ return true;
111743
+ }
111744
+ let config = pluginHandle.webrtcStuff;
111745
+ if (!config.pc) {
111746
+ Janus.warn("Invalid PeerConnection");
111747
+ return true;
111748
+ }
111749
+ if (!config.myStream) {
111750
+ Janus.warn("Invalid local MediaStream");
111751
+ return true;
111752
+ }
111753
+ if (video) {
111754
+ // Check video track
111755
+ if (!config.myStream.getVideoTracks() || config.myStream.getVideoTracks().length === 0) {
111756
+ Janus.warn("No video track");
111757
+ return true;
111758
+ }
111759
+ if (mid) {
111760
+ let transceiver = config.pc.getTransceivers().find(t => t.mid === mid && t.receiver.track.kind === "video");
111761
+ if (!transceiver) {
111762
+ Janus.warn("No video transceiver with mid " + mid);
111763
+ return true;
111764
+ }
111765
+ if (!transceiver.sender || !transceiver.sender.track) {
111766
+ Janus.warn("No video sender with mid " + mid);
111767
+ return true;
111768
+ }
111769
+ return !transceiver.sender.track.enabled;
111770
+ } else {
111771
+ return !config.myStream.getVideoTracks()[0].enabled;
111772
+ }
111773
+ } else {
111774
+ // Check audio track
111775
+ if (!config.myStream.getAudioTracks() || config.myStream.getAudioTracks().length === 0) {
111776
+ Janus.warn("No audio track");
111777
+ return true;
111778
+ }
111779
+ if (mid) {
111780
+ let transceiver = config.pc.getTransceivers().find(t => t.mid === mid && t.receiver.track.kind === "audio");
111781
+ if (!transceiver) {
111782
+ Janus.warn("No audio transceiver with mid " + mid);
111783
+ return true;
111784
+ }
111785
+ if (!transceiver.sender || !transceiver.sender.track) {
111786
+ Janus.warn("No audio sender with mid " + mid);
111787
+ return true;
111788
+ }
111789
+ return !transceiver.sender.track.enabled;
111790
+ } else {
111791
+ return !config.myStream.getAudioTracks()[0].enabled;
111792
+ }
111793
+ }
111794
+ }
111795
+ function mute(handleId, mid, video, mute) {
111796
+ let pluginHandle = pluginHandles.get(handleId);
111797
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111798
+ Janus.warn("Invalid handle");
111799
+ return false;
111800
+ }
111801
+ let config = pluginHandle.webrtcStuff;
111802
+ if (!config.pc) {
111803
+ Janus.warn("Invalid PeerConnection");
111804
+ return false;
111805
+ }
111806
+ if (!config.myStream) {
111807
+ Janus.warn("Invalid local MediaStream");
111808
+ return false;
111809
+ }
111810
+ if (video) {
111811
+ // Mute/unmute video track
111812
+ if (!config.myStream.getVideoTracks() || config.myStream.getVideoTracks().length === 0) {
111813
+ Janus.warn("No video track");
111814
+ return false;
111815
+ }
111816
+ if (mid) {
111817
+ let transceiver = config.pc.getTransceivers().find(t => t.mid === mid && t.receiver.track.kind === "video");
111818
+ if (!transceiver) {
111819
+ Janus.warn("No video transceiver with mid " + mid);
111820
+ return false;
111821
+ }
111822
+ if (!transceiver.sender || !transceiver.sender.track) {
111823
+ Janus.warn("No video sender with mid " + mid);
111824
+ return false;
111825
+ }
111826
+ transceiver.sender.track.enabled = mute ? false : true;
111827
+ } else {
111828
+ for (const videostream of config.myStream.getVideoTracks()) {
111829
+ videostream.enabled = !mute;
111830
+ }
111831
+ }
111832
+ } else {
111833
+ // Mute/unmute audio track
111834
+ if (!config.myStream.getAudioTracks() || config.myStream.getAudioTracks().length === 0) {
111835
+ Janus.warn("No audio track");
111836
+ return false;
111837
+ }
111838
+ if (mid) {
111839
+ let transceiver = config.pc.getTransceivers().find(t => t.mid === mid && t.receiver.track.kind === "audio");
111840
+ if (!transceiver) {
111841
+ Janus.warn("No audio transceiver with mid " + mid);
111842
+ return false;
111843
+ }
111844
+ if (!transceiver.sender || !transceiver.sender.track) {
111845
+ Janus.warn("No audio sender with mid " + mid);
111846
+ return false;
111847
+ }
111848
+ transceiver.sender.track.enabled = mute ? false : true;
111849
+ } else {
111850
+ for (const audiostream of config.myStream.getAudioTracks()) {
111851
+ audiostream.enabled = !mute;
111852
+ }
111853
+ }
111854
+ }
111855
+ return true;
111856
+ }
111857
+ function getBitrate(handleId, mid) {
111858
+ let pluginHandle = pluginHandles.get(handleId);
111859
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111860
+ Janus.warn("Invalid handle");
111861
+ return "Invalid handle";
111862
+ }
111863
+ let config = pluginHandle.webrtcStuff;
111864
+ if (!config.pc) return "Invalid PeerConnection";
111865
+ // Start getting the bitrate, if getStats is supported
111866
+ if (config.pc.getStats) {
111867
+ let query = config.pc;
111868
+ let target = mid ? mid : "default";
111869
+ if (mid) {
111870
+ let transceiver = config.pc.getTransceivers().find(t => t.mid === mid && t.receiver.track.kind === "video");
111871
+ if (!transceiver) {
111872
+ Janus.warn("No video transceiver with mid " + mid);
111873
+ return "No video transceiver with mid " + mid;
111874
+ }
111875
+ if (!transceiver.receiver) {
111876
+ Janus.warn("No video receiver with mid " + mid);
111877
+ return "No video receiver with mid " + mid;
111878
+ }
111879
+ query = transceiver.receiver;
111880
+ }
111881
+ if (!config.bitrate[target]) {
111882
+ config.bitrate[target] = {
111883
+ timer: null,
111884
+ bsnow: null,
111885
+ bsbefore: null,
111886
+ tsnow: null,
111887
+ tsbefore: null,
111888
+ value: "0 kbits/sec"
111889
+ };
111890
+ }
111891
+ if (!config.bitrate[target].timer) {
111892
+ Janus.log("Starting bitrate timer" + (mid ? " for mid " + mid : "") + " (via getStats)");
111893
+ config.bitrate[target].timer = setInterval(function () {
111894
+ query.getStats().then(function (stats) {
111895
+ stats.forEach(function (res) {
111896
+ if (!res) return;
111897
+ let inStats = false;
111898
+ // Check if these are statistics on incoming media
111899
+ if ((res.mediaType === "video" || res.kind === "video" || res.id.toLowerCase().indexOf("video") > -1) && res.type === "inbound-rtp" && res.id.indexOf("rtcp") < 0) {
111900
+ // New stats
111901
+ inStats = true;
111902
+ } else if (res.type == 'ssrc' && res.bytesReceived && (res.googCodecName === "VP8" || res.googCodecName === "")) {
111903
+ // Older Chromer versions
111904
+ inStats = true;
111905
+ }
111906
+ // Parse stats now
111907
+ if (inStats) {
111908
+ config.bitrate[target].bsnow = res.bytesReceived;
111909
+ config.bitrate[target].tsnow = res.timestamp;
111910
+ if (config.bitrate[target].bsbefore === null || config.bitrate[target].tsbefore === null) {
111911
+ // Skip this round
111912
+ config.bitrate[target].bsbefore = config.bitrate[target].bsnow;
111913
+ config.bitrate[target].tsbefore = config.bitrate[target].tsnow;
111914
+ } else {
111915
+ // Calculate bitrate
111916
+ let timePassed = config.bitrate[target].tsnow - config.bitrate[target].tsbefore;
111917
+ if (Janus.webRTCAdapter.browserDetails.browser === "safari") timePassed = timePassed / 1000; // Apparently the timestamp is in microseconds, in Safari
111918
+ let bitRate = Math.round((config.bitrate[target].bsnow - config.bitrate[target].bsbefore) * 8 / timePassed);
111919
+ if (Janus.webRTCAdapter.browserDetails.browser === "safari") bitRate = parseInt(bitRate / 1000);
111920
+ config.bitrate[target].value = bitRate + ' kbits/sec';
111921
+ //~ Janus.log("Estimated bitrate is " + config.bitrate.value);
111922
+ config.bitrate[target].bsbefore = config.bitrate[target].bsnow;
111923
+ config.bitrate[target].tsbefore = config.bitrate[target].tsnow;
111924
+ }
111925
+ }
111926
+ });
111927
+ });
111928
+ }, 1000);
111929
+ return "0 kbits/sec"; // We don't have a bitrate value yet
111930
+ }
111931
+ return config.bitrate[target].value;
111932
+ } else {
111933
+ Janus.warn("Getting the video bitrate unsupported by browser");
111934
+ return "Feature unsupported by browser";
111935
+ }
111936
+ }
111937
+ function setBitrate(handleId, mid, bitrate) {
111938
+ let pluginHandle = pluginHandles.get(handleId);
111939
+ if (!pluginHandle || !pluginHandle.webrtcStuff) {
111940
+ Janus.warn('Invalid handle');
111941
+ return;
111942
+ }
111943
+ let config = pluginHandle.webrtcStuff;
111944
+ if (!config.pc) {
111945
+ Janus.warn('Invalid PeerConnection');
111946
+ return;
111947
+ }
111948
+ let transceiver = config.pc.getTransceivers().find(t => t.mid === mid);
111949
+ if (!transceiver) {
111950
+ Janus.warn('No transceiver with mid', mid);
111951
+ return;
111952
+ }
111953
+ if (!transceiver.sender) {
111954
+ Janus.warn('No sender for transceiver with mid', mid);
111955
+ return;
111956
+ }
111957
+ let params = transceiver.sender.getParameters();
111958
+ if (!params || !params.encodings || params.encodings.length === 0) {
111959
+ Janus.warn('No parameters encodings');
111960
+ } else if (params.encodings.length > 1) {
111961
+ Janus.warn('Ignoring bitrate for simulcast track, use sendEncodings for that');
111962
+ } else if (isNaN(bitrate) || bitrate < 0) {
111963
+ Janus.warn('Invalid bitrate (must be a positive integer)');
111964
+ } else {
111965
+ params.encodings[0].maxBitrate = bitrate;
111966
+ transceiver.sender.setParameters(params);
111967
+ }
111968
+ }
111969
+ function webrtcError(error) {
111970
+ Janus.error("WebRTC error:", error);
111971
+ }
111972
+ function cleanupWebrtc(handleId, hangupRequest) {
111973
+ Janus.log("Cleaning WebRTC stuff");
111974
+ let pluginHandle = pluginHandles.get(handleId);
111975
+ if (!pluginHandle) {
111976
+ // Nothing to clean
111977
+ return;
111978
+ }
111979
+ let config = pluginHandle.webrtcStuff;
111980
+ if (config) {
111981
+ if (hangupRequest === true) {
111982
+ // Send a hangup request (we don't really care about the response)
111983
+ let request = {
111984
+ "janus": "hangup",
111985
+ "transaction": Janus.randomString(12)
111986
+ };
111987
+ if (pluginHandle.token) request["token"] = pluginHandle.token;
111988
+ if (apisecret) request["apisecret"] = apisecret;
111989
+ Janus.debug("Sending hangup request (handle=" + handleId + "):");
111990
+ Janus.debug(request);
111991
+ if (websockets) {
111992
+ request["session_id"] = sessionId;
111993
+ request["handle_id"] = handleId;
111994
+ ws.send(JSON.stringify(request));
111995
+ } else {
111996
+ Janus.httpAPICall(server + "/" + sessionId + "/" + handleId, {
111997
+ verb: 'POST',
111998
+ withCredentials: withCredentials,
111999
+ body: request
112000
+ });
112001
+ }
112002
+ }
112003
+ // Cleanup stack
112004
+ if (config.volume) {
112005
+ if (config.volume["local"] && config.volume["local"].timer) clearInterval(config.volume["local"].timer);
112006
+ if (config.volume["remote"] && config.volume["remote"].timer) clearInterval(config.volume["remote"].timer);
112007
+ }
112008
+ for (let i in config.bitrate) {
112009
+ if (config.bitrate[i].timer) clearInterval(config.bitrate[i].timer);
112010
+ }
112011
+ config.bitrate = {};
112012
+ if (!config.streamExternal && config.myStream) {
112013
+ Janus.log("Stopping local stream tracks");
112014
+ Janus.stopAllTracks(config.myStream);
112015
+ }
112016
+ config.streamExternal = false;
112017
+ config.myStream = null;
112018
+ // Close PeerConnection
112019
+ try {
112020
+ config.pc.close();
112021
+ // eslint-disable-next-line no-unused-vars
112022
+ } catch (e) {
112023
+ // Do nothing
112024
+ }
112025
+ config.pc = null;
112026
+ config.candidates = null;
112027
+ config.mySdp = null;
112028
+ config.remoteSdp = null;
112029
+ config.iceDone = false;
112030
+ config.dataChannel = {};
112031
+ config.dtmfSender = null;
112032
+ config.insertableStreams = false;
112033
+ config.externalEncryption = false;
112034
+ }
112035
+ pluginHandle.oncleanup();
112036
+ }
112037
+ function isTrickleEnabled(trickle) {
112038
+ Janus.debug("isTrickleEnabled:", trickle);
112039
+ return trickle === false ? false : true;
112040
+ }
112041
+ }
112042
+ return Janus;
112043
+ });
112044
+
108780
112045
  /*
108781
112046
  * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
108782
112047
  *
@@ -111801,7 +115066,7 @@ function adapterFactory({
111801
115066
  */
111802
115067
  /* eslint-env node */
111803
115068
 
111804
- const adapter = adapterFactory({
115069
+ const adapter$1 = adapterFactory({
111805
115070
  window: typeof window === 'undefined' ? undefined : window
111806
115071
  });
111807
115072
 
@@ -111915,7 +115180,6 @@ const copyToClipboard = text => {
111915
115180
  return successful;
111916
115181
  };
111917
115182
 
111918
- exports.Janus = Janus;
111919
115183
  exports.AADHAAR_REGEX = AADHAAR_REGEX;
111920
115184
  exports.ALPHABET_ONLY_REGEX = ALPHABET_ONLY_REGEX;
111921
115185
  exports.ALPHABET_WITH_SPACES_ONLY_REGEX = ALPHABET_WITH_SPACES_ONLY_REGEX;
@@ -112018,6 +115282,7 @@ exports.IconRadioGroup = IconRadioGroup;
112018
115282
  exports.Input = Input$1;
112019
115283
  exports.InputWithDropdown = InputWithDropdown;
112020
115284
  exports.JAVASCRIPT_FILE_TYPE_VALIDATION = JAVASCRIPT_FILE_TYPE_VALIDATION;
115285
+ exports.Janus = Janus;
112021
115286
  exports.LINKEDIN_PROFILE_REGEX = LINKEDIN_PROFILE_REGEX;
112022
115287
  exports.LINK_VALIDATION_REGEX = LINK_VALIDATION_REGEX;
112023
115288
  exports.LabelEditTextField = LabelEditTextField;
@@ -112115,7 +115380,7 @@ exports.VariableInput = VariableInput;
112115
115380
  exports.VariableSuggestionInputDropDown = VariableSuggestionInputDropDown;
112116
115381
  exports.WHITESPACE_REGEX = WHITESPACE_REGEX;
112117
115382
  exports.XML_FILE_TYPE_VALIDATION = XML_FILE_TYPE_VALIDATION;
112118
- exports.adapter = adapter;
115383
+ exports.adapter = adapter$1;
112119
115384
  exports.addPrePostStepGroup = addPrePostStepGroup;
112120
115385
  exports.addStepGroup = addStepGroup;
112121
115386
  exports.autoScrollToTableLastRow = autoScrollToTableLastRow;