-
-*/
-
-.hljs {
- display: block;
- overflow-x: auto;
- padding: 0.5em;
- color: #333;
- background: #f8f8f8;
- -webkit-text-size-adjust: none;
-}
-
-.hljs-comment,
-.diff .hljs-header,
-.hljs-javadoc {
- color: #998;
- font-style: italic;
-}
-
-.hljs-keyword,
-.css .rule .hljs-keyword,
-.hljs-winutils,
-.nginx .hljs-title,
-.hljs-subst,
-.hljs-request,
-.hljs-status {
- color: #1184CE;
-}
-
-.hljs-number,
-.hljs-hexcolor,
-.ruby .hljs-constant {
- color: #ed225d;
-}
-
-.hljs-string,
-.hljs-tag .hljs-value,
-.hljs-phpdoc,
-.hljs-dartdoc,
-.tex .hljs-formula {
- color: #ed225d;
-}
-
-.hljs-title,
-.hljs-id,
-.scss .hljs-preprocessor {
- color: #900;
- font-weight: bold;
-}
-
-.hljs-list .hljs-keyword,
-.hljs-subst {
- font-weight: normal;
-}
-
-.hljs-class .hljs-title,
-.hljs-type,
-.vhdl .hljs-literal,
-.tex .hljs-command {
- color: #458;
- font-weight: bold;
-}
-
-.hljs-tag,
-.hljs-tag .hljs-title,
-.hljs-rules .hljs-property,
-.django .hljs-tag .hljs-keyword {
- color: #000080;
- font-weight: normal;
-}
-
-.hljs-attribute,
-.hljs-variable,
-.lisp .hljs-body {
- color: #008080;
-}
-
-.hljs-regexp {
- color: #009926;
-}
-
-.hljs-symbol,
-.ruby .hljs-symbol .hljs-string,
-.lisp .hljs-keyword,
-.clojure .hljs-keyword,
-.scheme .hljs-keyword,
-.tex .hljs-special,
-.hljs-prompt {
- color: #990073;
-}
-
-.hljs-built_in {
- color: #0086b3;
-}
-
-.hljs-preprocessor,
-.hljs-pragma,
-.hljs-pi,
-.hljs-doctype,
-.hljs-shebang,
-.hljs-cdata {
- color: #999;
- font-weight: bold;
-}
-
-.hljs-deletion {
- background: #fdd;
-}
-
-.hljs-addition {
- background: #dfd;
-}
-
-.diff .hljs-change {
- background: #0086b3;
-}
-
-.hljs-chunk {
- color: #aaa;
-}
diff --git a/docs/api/assets/site.js b/docs/api/assets/site.js
deleted file mode 100644
index a624be7b2c6..00000000000
--- a/docs/api/assets/site.js
+++ /dev/null
@@ -1,168 +0,0 @@
-/* global anchors */
-
-// add anchor links to headers
-anchors.options.placement = 'left';
-anchors.add('h3');
-
-// Filter UI
-var tocElements = document.getElementById('toc').getElementsByTagName('li');
-
-document.getElementById('filter-input').addEventListener('keyup', function (e) {
- var i, element, children;
-
- // enter key
- if (e.keyCode === 13) {
- // go to the first displayed item in the toc
- for (i = 0; i < tocElements.length; i++) {
- element = tocElements[i];
- if (!element.classList.contains('display-none')) {
- location.replace(element.firstChild.href);
- return e.preventDefault();
- }
- }
- }
-
- var match = function () {
- return true;
- };
-
- var value = this.value.toLowerCase();
-
- if (!value.match(/^\s*$/)) {
- match = function (element) {
- var html = element.firstChild.innerHTML;
- return html && html.toLowerCase().indexOf(value) !== -1;
- };
- }
-
- for (i = 0; i < tocElements.length; i++) {
- element = tocElements[i];
- children = Array.from(element.getElementsByTagName('li'));
- if (match(element) || children.some(match)) {
- element.classList.remove('display-none');
- } else {
- element.classList.add('display-none');
- }
- }
-});
-
-var items = document.getElementsByClassName('toggle-sibling');
-for (var j = 0; j < items.length; j++) {
- items[j].addEventListener('click', toggleSibling);
-}
-
-function toggleSibling() {
- var stepSibling = this.parentNode.getElementsByClassName('toggle-target')[0];
- var icon = this.getElementsByClassName('icon')[0];
- var klass = 'display-none';
- if (stepSibling.classList.contains(klass)) {
- stepSibling.classList.remove(klass);
- icon.innerHTML = '▾';
- } else {
- stepSibling.classList.add(klass);
- icon.innerHTML = '▸';
- }
-}
-
-function showHashTarget(targetId) {
- if (targetId) {
- var hashTarget = document.getElementById(targetId);
- // new target is hidden
- if (
- hashTarget &&
- hashTarget.offsetHeight === 0 &&
- hashTarget.parentNode.parentNode.classList.contains('display-none')
- ) {
- hashTarget.parentNode.parentNode.classList.remove('display-none');
- }
- }
-}
-
-function scrollIntoView(targetId) {
- // Only scroll to element if we don't have a stored scroll position.
- if (targetId && !history.state) {
- var hashTarget = document.getElementById(targetId);
- if (hashTarget) {
- hashTarget.scrollIntoView();
- }
- }
-}
-
-function gotoCurrentTarget() {
- showHashTarget(location.hash.substring(1));
- scrollIntoView(location.hash.substring(1));
-}
-
-window.addEventListener('hashchange', gotoCurrentTarget);
-gotoCurrentTarget();
-
-var toclinks = document.getElementsByClassName('pre-open');
-for (var k = 0; k < toclinks.length; k++) {
- toclinks[k].addEventListener('mousedown', preOpen, false);
-}
-
-function preOpen() {
- showHashTarget(this.hash.substring(1));
-}
-
-var split_left = document.querySelector('#split-left');
-var split_right = document.querySelector('#split-right');
-var split_parent = split_left.parentNode;
-var cw_with_sb = split_left.clientWidth;
-split_left.style.overflow = 'hidden';
-var cw_without_sb = split_left.clientWidth;
-split_left.style.overflow = '';
-
-Split(['#split-left', '#split-right'], {
- elementStyle: function (dimension, size, gutterSize) {
- return {
- 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)'
- };
- },
- gutterStyle: function (dimension, gutterSize) {
- return {
- 'flex-basis': gutterSize + 'px'
- };
- },
- gutterSize: 20,
- sizes: [33, 67]
-});
-
-// Chrome doesn't remember scroll position properly so do it ourselves.
-// Also works on Firefox and Edge.
-
-function updateState() {
- history.replaceState(
- {
- left_top: split_left.scrollTop,
- right_top: split_right.scrollTop
- },
- document.title
- );
-}
-
-function loadState(ev) {
- if (ev) {
- // Edge doesn't replace change history.state on popstate.
- history.replaceState(ev.state, document.title);
- }
- if (history.state) {
- split_left.scrollTop = history.state.left_top;
- split_right.scrollTop = history.state.right_top;
- }
-}
-
-window.addEventListener('load', function () {
- // Restore after Firefox scrolls to hash.
- setTimeout(function () {
- loadState();
- // Update with initial scroll position.
- updateState();
- // Update scroll positions only after we've loaded because Firefox
- // emits an initial scroll event with 0.
- split_left.addEventListener('scroll', updateState);
- split_right.addEventListener('scroll', updateState);
- }, 1);
-});
-
-window.addEventListener('popstate', loadState);
diff --git a/docs/api/assets/split.css b/docs/api/assets/split.css
deleted file mode 100644
index 2d7779ee9b1..00000000000
--- a/docs/api/assets/split.css
+++ /dev/null
@@ -1,15 +0,0 @@
-.gutter {
- background-color: #f5f5f5;
- background-repeat: no-repeat;
- background-position: 50%;
-}
-
-.gutter.gutter-vertical {
- background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII=');
- cursor: ns-resize;
-}
-
-.gutter.gutter-horizontal {
- background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==');
- cursor: ew-resize;
-}
diff --git a/docs/api/assets/split.js b/docs/api/assets/split.js
deleted file mode 100644
index 71f9a60bd43..00000000000
--- a/docs/api/assets/split.js
+++ /dev/null
@@ -1,782 +0,0 @@
-/*! Split.js - v1.5.11 */
-
-(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- (global.Split = factory());
-}(this, (function () { 'use strict';
-
- // The programming goals of Split.js are to deliver readable, understandable and
- // maintainable code, while at the same time manually optimizing for tiny minified file size,
- // browser compatibility without additional requirements, graceful fallback (IE8 is supported)
- // and very few assumptions about the user's page layout.
- var global = window;
- var document = global.document;
-
- // Save a couple long function names that are used frequently.
- // This optimization saves around 400 bytes.
- var addEventListener = 'addEventListener';
- var removeEventListener = 'removeEventListener';
- var getBoundingClientRect = 'getBoundingClientRect';
- var gutterStartDragging = '_a';
- var aGutterSize = '_b';
- var bGutterSize = '_c';
- var HORIZONTAL = 'horizontal';
- var NOOP = function () { return false; };
-
- // Figure out if we're in IE8 or not. IE8 will still render correctly,
- // but will be static instead of draggable.
- var isIE8 = global.attachEvent && !global[addEventListener];
-
- // Helper function determines which prefixes of CSS calc we need.
- // We only need to do this once on startup, when this anonymous function is called.
- //
- // Tests -webkit, -moz and -o prefixes. Modified from StackOverflow:
- // http://stackoverflow.com/questions/16625140/js-feature-detection-to-detect-the-usage-of-webkit-calc-over-calc/16625167#16625167
- var calc = (['', '-webkit-', '-moz-', '-o-']
- .filter(function (prefix) {
- var el = document.createElement('div');
- el.style.cssText = "width:" + prefix + "calc(9px)";
-
- return !!el.style.length
- })
- .shift()) + "calc";
-
- // Helper function checks if its argument is a string-like type
- var isString = function (v) { return typeof v === 'string' || v instanceof String; };
-
- // Helper function allows elements and string selectors to be used
- // interchangeably. In either case an element is returned. This allows us to
- // do `Split([elem1, elem2])` as well as `Split(['#id1', '#id2'])`.
- var elementOrSelector = function (el) {
- if (isString(el)) {
- var ele = document.querySelector(el);
- if (!ele) {
- throw new Error(("Selector " + el + " did not match a DOM element"))
- }
- return ele
- }
-
- return el
- };
-
- // Helper function gets a property from the properties object, with a default fallback
- var getOption = function (options, propName, def) {
- var value = options[propName];
- if (value !== undefined) {
- return value
- }
- return def
- };
-
- var getGutterSize = function (gutterSize, isFirst, isLast, gutterAlign) {
- if (isFirst) {
- if (gutterAlign === 'end') {
- return 0
- }
- if (gutterAlign === 'center') {
- return gutterSize / 2
- }
- } else if (isLast) {
- if (gutterAlign === 'start') {
- return 0
- }
- if (gutterAlign === 'center') {
- return gutterSize / 2
- }
- }
-
- return gutterSize
- };
-
- // Default options
- var defaultGutterFn = function (i, gutterDirection) {
- var gut = document.createElement('div');
- gut.className = "gutter gutter-" + gutterDirection;
- return gut
- };
-
- var defaultElementStyleFn = function (dim, size, gutSize) {
- var style = {};
-
- if (!isString(size)) {
- if (!isIE8) {
- style[dim] = calc + "(" + size + "% - " + gutSize + "px)";
- } else {
- style[dim] = size + "%";
- }
- } else {
- style[dim] = size;
- }
-
- return style
- };
-
- var defaultGutterStyleFn = function (dim, gutSize) {
- var obj;
-
- return (( obj = {}, obj[dim] = (gutSize + "px"), obj ));
- };
-
- // The main function to initialize a split. Split.js thinks about each pair
- // of elements as an independant pair. Dragging the gutter between two elements
- // only changes the dimensions of elements in that pair. This is key to understanding
- // how the following functions operate, since each function is bound to a pair.
- //
- // A pair object is shaped like this:
- //
- // {
- // a: DOM element,
- // b: DOM element,
- // aMin: Number,
- // bMin: Number,
- // dragging: Boolean,
- // parent: DOM element,
- // direction: 'horizontal' | 'vertical'
- // }
- //
- // The basic sequence:
- //
- // 1. Set defaults to something sane. `options` doesn't have to be passed at all.
- // 2. Initialize a bunch of strings based on the direction we're splitting.
- // A lot of the behavior in the rest of the library is paramatized down to
- // rely on CSS strings and classes.
- // 3. Define the dragging helper functions, and a few helpers to go with them.
- // 4. Loop through the elements while pairing them off. Every pair gets an
- // `pair` object and a gutter.
- // 5. Actually size the pair elements, insert gutters and attach event listeners.
- var Split = function (idsOption, options) {
- if ( options === void 0 ) options = {};
-
- var ids = idsOption;
- var dimension;
- var clientAxis;
- var position;
- var positionEnd;
- var clientSize;
- var elements;
-
- // Allow HTMLCollection to be used as an argument when supported
- if (Array.from) {
- ids = Array.from(ids);
- }
-
- // All DOM elements in the split should have a common parent. We can grab
- // the first elements parent and hope users read the docs because the
- // behavior will be whacky otherwise.
- var firstElement = elementOrSelector(ids[0]);
- var parent = firstElement.parentNode;
- var parentStyle = getComputedStyle ? getComputedStyle(parent) : null;
- var parentFlexDirection = parentStyle ? parentStyle.flexDirection : null;
-
- // Set default options.sizes to equal percentages of the parent element.
- var sizes = getOption(options, 'sizes') || ids.map(function () { return 100 / ids.length; });
-
- // Standardize minSize to an array if it isn't already. This allows minSize
- // to be passed as a number.
- var minSize = getOption(options, 'minSize', 100);
- var minSizes = Array.isArray(minSize) ? minSize : ids.map(function () { return minSize; });
-
- // Get other options
- var expandToMin = getOption(options, 'expandToMin', false);
- var gutterSize = getOption(options, 'gutterSize', 10);
- var gutterAlign = getOption(options, 'gutterAlign', 'center');
- var snapOffset = getOption(options, 'snapOffset', 30);
- var dragInterval = getOption(options, 'dragInterval', 1);
- var direction = getOption(options, 'direction', HORIZONTAL);
- var cursor = getOption(
- options,
- 'cursor',
- direction === HORIZONTAL ? 'col-resize' : 'row-resize'
- );
- var gutter = getOption(options, 'gutter', defaultGutterFn);
- var elementStyle = getOption(
- options,
- 'elementStyle',
- defaultElementStyleFn
- );
- var gutterStyle = getOption(options, 'gutterStyle', defaultGutterStyleFn);
-
- // 2. Initialize a bunch of strings based on the direction we're splitting.
- // A lot of the behavior in the rest of the library is paramatized down to
- // rely on CSS strings and classes.
- if (direction === HORIZONTAL) {
- dimension = 'width';
- clientAxis = 'clientX';
- position = 'left';
- positionEnd = 'right';
- clientSize = 'clientWidth';
- } else if (direction === 'vertical') {
- dimension = 'height';
- clientAxis = 'clientY';
- position = 'top';
- positionEnd = 'bottom';
- clientSize = 'clientHeight';
- }
-
- // 3. Define the dragging helper functions, and a few helpers to go with them.
- // Each helper is bound to a pair object that contains its metadata. This
- // also makes it easy to store references to listeners that that will be
- // added and removed.
- //
- // Even though there are no other functions contained in them, aliasing
- // this to self saves 50 bytes or so since it's used so frequently.
- //
- // The pair object saves metadata like dragging state, position and
- // event listener references.
-
- function setElementSize(el, size, gutSize, i) {
- // Split.js allows setting sizes via numbers (ideally), or if you must,
- // by string, like '300px'. This is less than ideal, because it breaks
- // the fluid layout that `calc(% - px)` provides. You're on your own if you do that,
- // make sure you calculate the gutter size by hand.
- var style = elementStyle(dimension, size, gutSize, i);
-
- Object.keys(style).forEach(function (prop) {
- // eslint-disable-next-line no-param-reassign
- el.style[prop] = style[prop];
- });
- }
-
- function setGutterSize(gutterElement, gutSize, i) {
- var style = gutterStyle(dimension, gutSize, i);
-
- Object.keys(style).forEach(function (prop) {
- // eslint-disable-next-line no-param-reassign
- gutterElement.style[prop] = style[prop];
- });
- }
-
- function getSizes() {
- return elements.map(function (element) { return element.size; })
- }
-
- // Supports touch events, but not multitouch, so only the first
- // finger `touches[0]` is counted.
- function getMousePosition(e) {
- if ('touches' in e) { return e.touches[0][clientAxis] }
- return e[clientAxis]
- }
-
- // Actually adjust the size of elements `a` and `b` to `offset` while dragging.
- // calc is used to allow calc(percentage + gutterpx) on the whole split instance,
- // which allows the viewport to be resized without additional logic.
- // Element a's size is the same as offset. b's size is total size - a size.
- // Both sizes are calculated from the initial parent percentage,
- // then the gutter size is subtracted.
- function adjust(offset) {
- var a = elements[this.a];
- var b = elements[this.b];
- var percentage = a.size + b.size;
-
- a.size = (offset / this.size) * percentage;
- b.size = percentage - (offset / this.size) * percentage;
-
- setElementSize(a.element, a.size, this[aGutterSize], a.i);
- setElementSize(b.element, b.size, this[bGutterSize], b.i);
- }
-
- // drag, where all the magic happens. The logic is really quite simple:
- //
- // 1. Ignore if the pair is not dragging.
- // 2. Get the offset of the event.
- // 3. Snap offset to min if within snappable range (within min + snapOffset).
- // 4. Actually adjust each element in the pair to offset.
- //
- // ---------------------------------------------------------------------
- // | | <- a.minSize || b.minSize -> | |
- // | | | <- this.snapOffset || this.snapOffset -> | | |
- // | | | || | | |
- // | | | || | | |
- // ---------------------------------------------------------------------
- // | <- this.start this.size -> |
- function drag(e) {
- var offset;
- var a = elements[this.a];
- var b = elements[this.b];
-
- if (!this.dragging) { return }
-
- // Get the offset of the event from the first side of the
- // pair `this.start`. Then offset by the initial position of the
- // mouse compared to the gutter size.
- offset =
- getMousePosition(e) -
- this.start +
- (this[aGutterSize] - this.dragOffset);
-
- if (dragInterval > 1) {
- offset = Math.round(offset / dragInterval) * dragInterval;
- }
-
- // If within snapOffset of min or max, set offset to min or max.
- // snapOffset buffers a.minSize and b.minSize, so logic is opposite for both.
- // Include the appropriate gutter sizes to prevent overflows.
- if (offset <= a.minSize + snapOffset + this[aGutterSize]) {
- offset = a.minSize + this[aGutterSize];
- } else if (
- offset >=
- this.size - (b.minSize + snapOffset + this[bGutterSize])
- ) {
- offset = this.size - (b.minSize + this[bGutterSize]);
- }
-
- // Actually adjust the size.
- adjust.call(this, offset);
-
- // Call the drag callback continously. Don't do anything too intensive
- // in this callback.
- getOption(options, 'onDrag', NOOP)();
- }
-
- // Cache some important sizes when drag starts, so we don't have to do that
- // continously:
- //
- // `size`: The total size of the pair. First + second + first gutter + second gutter.
- // `start`: The leading side of the first element.
- //
- // ------------------------------------------------
- // | aGutterSize -> ||| |
- // | ||| |
- // | ||| |
- // | ||| <- bGutterSize |
- // ------------------------------------------------
- // | <- start size -> |
- function calculateSizes() {
- // Figure out the parent size minus padding.
- var a = elements[this.a].element;
- var b = elements[this.b].element;
-
- var aBounds = a[getBoundingClientRect]();
- var bBounds = b[getBoundingClientRect]();
-
- this.size =
- aBounds[dimension] +
- bBounds[dimension] +
- this[aGutterSize] +
- this[bGutterSize];
- this.start = aBounds[position];
- this.end = aBounds[positionEnd];
- }
-
- function innerSize(element) {
- // Return nothing if getComputedStyle is not supported (< IE9)
- // Or if parent element has no layout yet
- if (!getComputedStyle) { return null }
-
- var computedStyle = getComputedStyle(element);
-
- if (!computedStyle) { return null }
-
- var size = element[clientSize];
-
- if (size === 0) { return null }
-
- if (direction === HORIZONTAL) {
- size -=
- parseFloat(computedStyle.paddingLeft) +
- parseFloat(computedStyle.paddingRight);
- } else {
- size -=
- parseFloat(computedStyle.paddingTop) +
- parseFloat(computedStyle.paddingBottom);
- }
-
- return size
- }
-
- // When specifying percentage sizes that are less than the computed
- // size of the element minus the gutter, the lesser percentages must be increased
- // (and decreased from the other elements) to make space for the pixels
- // subtracted by the gutters.
- function trimToMin(sizesToTrim) {
- // Try to get inner size of parent element.
- // If it's no supported, return original sizes.
- var parentSize = innerSize(parent);
- if (parentSize === null) {
- return sizesToTrim
- }
-
- if (minSizes.reduce(function (a, b) { return a + b; }, 0) > parentSize) {
- return sizesToTrim
- }
-
- // Keep track of the excess pixels, the amount of pixels over the desired percentage
- // Also keep track of the elements with pixels to spare, to decrease after if needed
- var excessPixels = 0;
- var toSpare = [];
-
- var pixelSizes = sizesToTrim.map(function (size, i) {
- // Convert requested percentages to pixel sizes
- var pixelSize = (parentSize * size) / 100;
- var elementGutterSize = getGutterSize(
- gutterSize,
- i === 0,
- i === sizesToTrim.length - 1,
- gutterAlign
- );
- var elementMinSize = minSizes[i] + elementGutterSize;
-
- // If element is too smal, increase excess pixels by the difference
- // and mark that it has no pixels to spare
- if (pixelSize < elementMinSize) {
- excessPixels += elementMinSize - pixelSize;
- toSpare.push(0);
- return elementMinSize
- }
-
- // Otherwise, mark the pixels it has to spare and return it's original size
- toSpare.push(pixelSize - elementMinSize);
- return pixelSize
- });
-
- // If nothing was adjusted, return the original sizes
- if (excessPixels === 0) {
- return sizesToTrim
- }
-
- return pixelSizes.map(function (pixelSize, i) {
- var newPixelSize = pixelSize;
-
- // While there's still pixels to take, and there's enough pixels to spare,
- // take as many as possible up to the total excess pixels
- if (excessPixels > 0 && toSpare[i] - excessPixels > 0) {
- var takenPixels = Math.min(
- excessPixels,
- toSpare[i] - excessPixels
- );
-
- // Subtract the amount taken for the next iteration
- excessPixels -= takenPixels;
- newPixelSize = pixelSize - takenPixels;
- }
-
- // Return the pixel size adjusted as a percentage
- return (newPixelSize / parentSize) * 100
- })
- }
-
- // stopDragging is very similar to startDragging in reverse.
- function stopDragging() {
- var self = this;
- var a = elements[self.a].element;
- var b = elements[self.b].element;
-
- if (self.dragging) {
- getOption(options, 'onDragEnd', NOOP)(getSizes());
- }
-
- self.dragging = false;
-
- // Remove the stored event listeners. This is why we store them.
- global[removeEventListener]('mouseup', self.stop);
- global[removeEventListener]('touchend', self.stop);
- global[removeEventListener]('touchcancel', self.stop);
- global[removeEventListener]('mousemove', self.move);
- global[removeEventListener]('touchmove', self.move);
-
- // Clear bound function references
- self.stop = null;
- self.move = null;
-
- a[removeEventListener]('selectstart', NOOP);
- a[removeEventListener]('dragstart', NOOP);
- b[removeEventListener]('selectstart', NOOP);
- b[removeEventListener]('dragstart', NOOP);
-
- a.style.userSelect = '';
- a.style.webkitUserSelect = '';
- a.style.MozUserSelect = '';
- a.style.pointerEvents = '';
-
- b.style.userSelect = '';
- b.style.webkitUserSelect = '';
- b.style.MozUserSelect = '';
- b.style.pointerEvents = '';
-
- self.gutter.style.cursor = '';
- self.parent.style.cursor = '';
- document.body.style.cursor = '';
- }
-
- // startDragging calls `calculateSizes` to store the inital size in the pair object.
- // It also adds event listeners for mouse/touch events,
- // and prevents selection while dragging so avoid the selecting text.
- function startDragging(e) {
- // Right-clicking can't start dragging.
- if ('button' in e && e.button !== 0) {
- return
- }
-
- // Alias frequently used variables to save space. 200 bytes.
- var self = this;
- var a = elements[self.a].element;
- var b = elements[self.b].element;
-
- // Call the onDragStart callback.
- if (!self.dragging) {
- getOption(options, 'onDragStart', NOOP)(getSizes());
- }
-
- // Don't actually drag the element. We emulate that in the drag function.
- e.preventDefault();
-
- // Set the dragging property of the pair object.
- self.dragging = true;
-
- // Create two event listeners bound to the same pair object and store
- // them in the pair object.
- self.move = drag.bind(self);
- self.stop = stopDragging.bind(self);
-
- // All the binding. `window` gets the stop events in case we drag out of the elements.
- global[addEventListener]('mouseup', self.stop);
- global[addEventListener]('touchend', self.stop);
- global[addEventListener]('touchcancel', self.stop);
- global[addEventListener]('mousemove', self.move);
- global[addEventListener]('touchmove', self.move);
-
- // Disable selection. Disable!
- a[addEventListener]('selectstart', NOOP);
- a[addEventListener]('dragstart', NOOP);
- b[addEventListener]('selectstart', NOOP);
- b[addEventListener]('dragstart', NOOP);
-
- a.style.userSelect = 'none';
- a.style.webkitUserSelect = 'none';
- a.style.MozUserSelect = 'none';
- a.style.pointerEvents = 'none';
-
- b.style.userSelect = 'none';
- b.style.webkitUserSelect = 'none';
- b.style.MozUserSelect = 'none';
- b.style.pointerEvents = 'none';
-
- // Set the cursor at multiple levels
- self.gutter.style.cursor = cursor;
- self.parent.style.cursor = cursor;
- document.body.style.cursor = cursor;
-
- // Cache the initial sizes of the pair.
- calculateSizes.call(self);
-
- // Determine the position of the mouse compared to the gutter
- self.dragOffset = getMousePosition(e) - self.end;
- }
-
- // adjust sizes to ensure percentage is within min size and gutter.
- sizes = trimToMin(sizes);
-
- // 5. Create pair and element objects. Each pair has an index reference to
- // elements `a` and `b` of the pair (first and second elements).
- // Loop through the elements while pairing them off. Every pair gets a
- // `pair` object and a gutter.
- //
- // Basic logic:
- //
- // - Starting with the second element `i > 0`, create `pair` objects with
- // `a = i - 1` and `b = i`
- // - Set gutter sizes based on the _pair_ being first/last. The first and last
- // pair have gutterSize / 2, since they only have one half gutter, and not two.
- // - Create gutter elements and add event listeners.
- // - Set the size of the elements, minus the gutter sizes.
- //
- // -----------------------------------------------------------------------
- // | i=0 | i=1 | i=2 | i=3 |
- // | | | | |
- // | pair 0 pair 1 pair 2 |
- // | | | | |
- // -----------------------------------------------------------------------
- var pairs = [];
- elements = ids.map(function (id, i) {
- // Create the element object.
- var element = {
- element: elementOrSelector(id),
- size: sizes[i],
- minSize: minSizes[i],
- i: i,
- };
-
- var pair;
-
- if (i > 0) {
- // Create the pair object with its metadata.
- pair = {
- a: i - 1,
- b: i,
- dragging: false,
- direction: direction,
- parent: parent,
- };
-
- pair[aGutterSize] = getGutterSize(
- gutterSize,
- i - 1 === 0,
- false,
- gutterAlign
- );
- pair[bGutterSize] = getGutterSize(
- gutterSize,
- false,
- i === ids.length - 1,
- gutterAlign
- );
-
- // if the parent has a reverse flex-direction, switch the pair elements.
- if (
- parentFlexDirection === 'row-reverse' ||
- parentFlexDirection === 'column-reverse'
- ) {
- var temp = pair.a;
- pair.a = pair.b;
- pair.b = temp;
- }
- }
-
- // Determine the size of the current element. IE8 is supported by
- // staticly assigning sizes without draggable gutters. Assigns a string
- // to `size`.
- //
- // IE9 and above
- if (!isIE8) {
- // Create gutter elements for each pair.
- if (i > 0) {
- var gutterElement = gutter(i, direction, element.element);
- setGutterSize(gutterElement, gutterSize, i);
-
- // Save bound event listener for removal later
- pair[gutterStartDragging] = startDragging.bind(pair);
-
- // Attach bound event listener
- gutterElement[addEventListener](
- 'mousedown',
- pair[gutterStartDragging]
- );
- gutterElement[addEventListener](
- 'touchstart',
- pair[gutterStartDragging]
- );
-
- parent.insertBefore(gutterElement, element.element);
-
- pair.gutter = gutterElement;
- }
- }
-
- setElementSize(
- element.element,
- element.size,
- getGutterSize(
- gutterSize,
- i === 0,
- i === ids.length - 1,
- gutterAlign
- ),
- i
- );
-
- // After the first iteration, and we have a pair object, append it to the
- // list of pairs.
- if (i > 0) {
- pairs.push(pair);
- }
-
- return element
- });
-
- function adjustToMin(element) {
- var isLast = element.i === pairs.length;
- var pair = isLast ? pairs[element.i - 1] : pairs[element.i];
-
- calculateSizes.call(pair);
-
- var size = isLast
- ? pair.size - element.minSize - pair[bGutterSize]
- : element.minSize + pair[aGutterSize];
-
- adjust.call(pair, size);
- }
-
- elements.forEach(function (element) {
- var computedSize = element.element[getBoundingClientRect]()[dimension];
-
- if (computedSize < element.minSize) {
- if (expandToMin) {
- adjustToMin(element);
- } else {
- // eslint-disable-next-line no-param-reassign
- element.minSize = computedSize;
- }
- }
- });
-
- function setSizes(newSizes) {
- var trimmed = trimToMin(newSizes);
- trimmed.forEach(function (newSize, i) {
- if (i > 0) {
- var pair = pairs[i - 1];
-
- var a = elements[pair.a];
- var b = elements[pair.b];
-
- a.size = trimmed[i - 1];
- b.size = newSize;
-
- setElementSize(a.element, a.size, pair[aGutterSize], a.i);
- setElementSize(b.element, b.size, pair[bGutterSize], b.i);
- }
- });
- }
-
- function destroy(preserveStyles, preserveGutter) {
- pairs.forEach(function (pair) {
- if (preserveGutter !== true) {
- pair.parent.removeChild(pair.gutter);
- } else {
- pair.gutter[removeEventListener](
- 'mousedown',
- pair[gutterStartDragging]
- );
- pair.gutter[removeEventListener](
- 'touchstart',
- pair[gutterStartDragging]
- );
- }
-
- if (preserveStyles !== true) {
- var style = elementStyle(
- dimension,
- pair.a.size,
- pair[aGutterSize]
- );
-
- Object.keys(style).forEach(function (prop) {
- elements[pair.a].element.style[prop] = '';
- elements[pair.b].element.style[prop] = '';
- });
- }
- });
- }
-
- if (isIE8) {
- return {
- setSizes: setSizes,
- destroy: destroy,
- }
- }
-
- return {
- setSizes: setSizes,
- getSizes: getSizes,
- collapse: function collapse(i) {
- adjustToMin(elements[i]);
- },
- destroy: destroy,
- parent: parent,
- pairs: pairs,
- }
- };
-
- return Split;
-
-})));
diff --git a/docs/api/assets/style.css b/docs/api/assets/style.css
deleted file mode 100644
index 0618f4376f2..00000000000
--- a/docs/api/assets/style.css
+++ /dev/null
@@ -1,147 +0,0 @@
-.documentation {
- font-family: Helvetica, sans-serif;
- color: #666;
- line-height: 1.5;
- background: #f5f5f5;
-}
-
-.black {
- color: #666;
-}
-
-.bg-white {
- background-color: #fff;
-}
-
-h4 {
- margin: 20px 0 10px 0;
-}
-
-.documentation h3 {
- color: #000;
-}
-
-.border-bottom {
- border-color: #ddd;
-}
-
-a {
- color: #1184ce;
- text-decoration: none;
-}
-
-.documentation a[href]:hover {
- text-decoration: underline;
-}
-
-a:hover {
- cursor: pointer;
-}
-
-.py1-ul li {
- padding: 5px 0;
-}
-
-.max-height-100 {
- max-height: 100%;
-}
-
-.height-viewport-100 {
- height: 100vh;
-}
-
-section:target h3 {
- font-weight: 700;
-}
-
-.documentation td,
-.documentation th {
- padding: 0.25rem 0.25rem;
-}
-
-h1:hover .anchorjs-link,
-h2:hover .anchorjs-link,
-h3:hover .anchorjs-link,
-h4:hover .anchorjs-link {
- opacity: 1;
-}
-
-.fix-3 {
- width: 25%;
- max-width: 244px;
-}
-
-.fix-3 {
- width: 25%;
- max-width: 244px;
-}
-
-@media (min-width: 52em) {
- .fix-margin-3 {
- margin-left: 25%;
- }
-}
-
-.pre,
-pre,
-code,
-.code {
- font-family: Source Code Pro, Menlo, Consolas, Liberation Mono, monospace;
- font-size: 14px;
-}
-
-.fill-light {
- background: #f9f9f9;
-}
-
-.width2 {
- width: 1rem;
-}
-
-.input {
- font-family: inherit;
- display: block;
- width: 100%;
- height: 2rem;
- padding: 0.5rem;
- margin-bottom: 1rem;
- border: 1px solid #ccc;
- font-size: 0.875rem;
- border-radius: 3px;
- box-sizing: border-box;
-}
-
-table {
- border-collapse: collapse;
-}
-
-.prose table th,
-.prose table td {
- text-align: left;
- padding: 8px;
- border: 1px solid #ddd;
-}
-
-.prose table th:nth-child(1) {
- border-right: none;
-}
-.prose table th:nth-child(2) {
- border-left: none;
-}
-
-.prose table {
- border: 1px solid #ddd;
-}
-
-.prose-big {
- font-size: 18px;
- line-height: 30px;
-}
-
-.quiet {
- opacity: 0.7;
-}
-
-.minishadow {
- box-shadow: 2px 2px 10px #f3f3f3;
-}
diff --git a/docs/samples/calling/app.js b/docs/samples/calling/app.js
index c0df6e1cc4b..aa9fbae6a58 100644
--- a/docs/samples/calling/app.js
+++ b/docs/samples/calling/app.js
@@ -799,6 +799,8 @@ function definedTable(callHistoryResponse) {
EndTime |
SessionType |
CallbackAddress |
+ RedirectionReason |
+ Forwarded by |
`;
callHistoryHeader.innerHTML += callHistHeaderHtml;
@@ -813,6 +815,8 @@ function definedTable(callHistoryResponse) {
${callHistoryResponse.data.userSessions[i].endTime} |
${callHistoryResponse.data.userSessions[i].sessionType} |
${callHistoryResponse.data.userSessions[i].other.callbackAddress} |
+ ${callHistoryResponse.data.userSessions[i].callingSpecifics?.redirectionDetails?.reason === undefined ? 'NA' : callHistoryResponse.data.userSessions[i].callingSpecifics.redirectionDetails.reason} |
+ ${callHistoryResponse.data.userSessions[i].callingSpecifics?.redirectionDetails?.name === undefined ? 'NA' : callHistoryResponse.data.userSessions[i].callingSpecifics.redirectionDetails.name} |
`;
callHistoryTable.innerHTML += callHistoryRow;
diff --git a/packages/@webex/plugin-meetings/src/roap/index.ts b/packages/@webex/plugin-meetings/src/roap/index.ts
index 0fa3b987d26..013acf5ad8b 100644
--- a/packages/@webex/plugin-meetings/src/roap/index.ts
+++ b/packages/@webex/plugin-meetings/src/roap/index.ts
@@ -207,8 +207,6 @@ export default class Roap extends StatelessWebexPlugin {
preferTranscoding: !meeting.isMultistream,
locusMediaRequest: meeting.locusMediaRequest,
ipVersion: MeetingUtil.getIpVersion(meeting.webex),
- audioMuted: meeting.audio?.isLocallyMuted(),
- videoMuted: meeting.video?.isLocallyMuted(),
})
.then(({locus, mediaConnections}) => {
if (mediaConnections) {
diff --git a/packages/@webex/plugin-meetings/test/unit/spec/roap/index.ts b/packages/@webex/plugin-meetings/test/unit/spec/roap/index.ts
index e5de1789189..c349ccfdf95 100644
--- a/packages/@webex/plugin-meetings/test/unit/spec/roap/index.ts
+++ b/packages/@webex/plugin-meetings/test/unit/spec/roap/index.ts
@@ -113,8 +113,6 @@ describe('Roap', () => {
roapMessage: expectedRoapMessage,
locusSelfUrl: meeting.selfUrl,
mediaId: expectEmptyMediaId ? '' : meeting.mediaId,
- audioMuted: meeting.audio?.isLocallyMuted(),
- videoMuted: meeting.video?.isLocallyMuted(),
meetingId: meeting.id,
locusMediaRequest: meeting.locusMediaRequest,
}));
diff --git a/packages/@webex/webex-core/src/lib/services/services.js b/packages/@webex/webex-core/src/lib/services/services.js
index 3f66d749512..a5583709dac 100644
--- a/packages/@webex/webex-core/src/lib/services/services.js
+++ b/packages/@webex/webex-core/src/lib/services/services.js
@@ -58,6 +58,8 @@ const Services = WebexPlugin.extend({
_serviceUrls: null,
+ _hostCatalog: null,
+
/**
* Get the registry associated with this webex instance.
*
@@ -166,6 +168,15 @@ const Services = WebexPlugin.extend({
this._serviceUrls = {...this._serviceUrls, ...serviceUrls};
},
+ /**
+ * saves the host catalog
+ * @param {Object} hostCatalog
+ * @returns {void}
+ */
+ _updateHostCatalog(hostCatalog) {
+ this._hostCatalog = {...this._hostCatalog, ...hostCatalog};
+ },
+
/**
* Update a list of `serviceUrls` to the most current
* catalog via the defined `discoveryUrl` then returns the current
@@ -671,6 +682,7 @@ const Services = WebexPlugin.extend({
* @returns {object}
*/
_formatReceivedHostmap(serviceHostmap) {
+ this._updateHostCatalog(serviceHostmap.hostCatalog);
// map the host catalog items to a formatted hostmap
const formattedHostmap = Object.keys(serviceHostmap.hostCatalog).reduce((accumulator, key) => {
if (serviceHostmap.hostCatalog[key].length === 0) {
diff --git a/packages/@webex/webex-core/test/unit/spec/services/services.js b/packages/@webex/webex-core/test/unit/spec/services/services.js
index c1080544dac..65aba3ee9ea 100644
--- a/packages/@webex/webex-core/test/unit/spec/services/services.js
+++ b/packages/@webex/webex-core/test/unit/spec/services/services.js
@@ -298,6 +298,12 @@ describe('webex-core', () => {
formattedHM.map((item) => item.name)
);
});
+
+ it('has hostCatalog updated', () => {
+ services._formatReceivedHostmap(serviceHostmap);
+
+ assert.deepStrictEqual(services._hostCatalog, serviceHostmap.hostCatalog);
+ });
});
describe('#updateCredentialsConfig()', () => {
diff --git a/packages/calling/package.json b/packages/calling/package.json
index 78647f83a57..263aebf01a5 100644
--- a/packages/calling/package.json
+++ b/packages/calling/package.json
@@ -6,6 +6,7 @@
"contributors": [
"Bhargav Chinta (bchinta@cisco.com)",
"Dipanshu Sharma ",
+ "Karthik Raji (karaji@cisco.com)",
"Priya Kesari (pkesari@cisco.com)",
"Rajesh Kumar (rarajes2@cisco.com)",
"Shreyas Sharma (shreysh2@cisco.com)",
diff --git a/packages/calling/src/CallHistory/callHistoryFixtures.ts b/packages/calling/src/CallHistory/callHistoryFixtures.ts
index a61d44cf186..13755c4f176 100644
--- a/packages/calling/src/CallHistory/callHistoryFixtures.ts
+++ b/packages/calling/src/CallHistory/callHistoryFixtures.ts
@@ -311,7 +311,15 @@ const WEBEX_CALL_SESSION = {
'https://conv-a.wbx2.com/conversation/api/v1/conversations/c9252ff0-9de2-11ec-a582-59d00c02cca9',
callbackAddress: '123-456-7890',
},
-
+ callingSpecifics: {
+ redirectionDetails: {
+ phoneNumber: '+18308508011',
+ name: 'Test QA Call Center',
+ reason: 'CALLQUEUE',
+ userId: '604a966d-7518-4b74-8d78-6c05caf98239',
+ isPrivate: false,
+ },
+ },
isDeleted: false,
isPMR: false,
correlationIds: ['58ea6cd9-852b-4a77-957f-e704c8b0e63e'],
diff --git a/packages/calling/src/CallingClient/CallingClient.test.ts b/packages/calling/src/CallingClient/CallingClient.test.ts
index 442abbb5f14..6432f0f0642 100644
--- a/packages/calling/src/CallingClient/CallingClient.test.ts
+++ b/packages/calling/src/CallingClient/CallingClient.test.ts
@@ -17,10 +17,12 @@ import {getCallManager} from './calling/callManager';
import {
CALLING_CLIENT_FILE,
DISCOVERY_URL,
+ IP_ENDPOINT,
NETWORK_CHANGE_DETECTION_UTIL,
NETWORK_FLAP_TIMEOUT,
REGISTRATION_FILE,
SPARK_USER_AGENT,
+ URL_ENDPOINT,
} from './constants';
import {MOCK_MULTIPLE_SESSIONS_EVENT, MOCK_SESSION_EVENT} from './callRecordFixtures';
import {ILine} from './line/types';
@@ -31,19 +33,27 @@ import {
primaryUrl,
discoveryPayload,
registrationPayload,
- uri,
myIP,
+ mockEUServiceHosts,
+ mockIntServiceHosts,
+ mockEUIntServiceHosts,
+ mockCatalogEU,
+ mockCatalogUSInt,
+ mockCatalogUS,
+ mockCatalogEUInt,
} from './callingClientFixtures';
import Line from './line';
import {filterMobiusUris} from '../common/Utils';
import {URL} from './registration/registerFixtures';
import {ICall} from './calling/types';
+import {ServiceHost} from '../SDKConnector/types';
describe('CallingClient Tests', () => {
// Common initializers
const handleErrorSpy = jest.spyOn(utils, 'handleCallingClientErrors');
const webex = getTestUtilsWebex();
+ webex.internal.services['_hostCatalog'] = mockCatalogUS;
const defaultServiceIndicator = ServiceIndicator.CALLING;
const callManager = getCallManager(webex, defaultServiceIndicator);
@@ -226,7 +236,7 @@ describe('CallingClient Tests', () => {
expect(webex.request).nthCalledWith(1, {
method: 'GET',
...getMockRequestTemplate(),
- uri: `${uri}myip`,
+ uri: `${callingClient['mobiusHost']}${URL_ENDPOINT}${IP_ENDPOINT}`,
});
expect(webex.request).nthCalledWith(2, {
@@ -241,7 +251,7 @@ describe('CallingClient Tests', () => {
expect(webex.request).nthCalledWith(3, {
method: 'GET',
...getMockRequestTemplate(),
- uri: `${uri}?regionCode=${regionBody.clientRegion}&countryCode=${regionBody.countryCode}`,
+ uri: `${callingClient['mobiusHost']}${URL_ENDPOINT}?regionCode=${regionBody.clientRegion}&countryCode=${regionBody.countryCode}`,
});
});
@@ -254,27 +264,41 @@ describe('CallingClient Tests', () => {
callingClient = await createClient(webex, {logger: {level: LOGGER.INFO}});
- expect(handleErrorSpy).toBeCalledOnceWith(failurePayload, expect.anything(), {
- file: CALLING_CLIENT_FILE,
- method: 'getMobiusServers',
+ expect(webex.request).nthCalledWith(1, {
+ ...getMockRequestTemplate(),
+ uri: 'https://mobius-us-east-1.prod.infra.webex.com/api/v1/calling/web/myip',
+ method: 'GET',
});
- expect(webex.request).toBeCalledOnceWith({
+ expect(webex.request).nthCalledWith(2, {
...getMockRequestTemplate(),
- uri: `${uri}myip`,
+ uri: 'https://mobius-ca-central-1.prod.infra.webex.com/api/v1/calling/web/myip',
method: 'GET',
});
- expect(callingClient.primaryMobiusUris).toEqual([uri]);
-
- expect(webex.request).nthCalledWith(1, {
+ expect(webex.request).nthCalledWith(3, {
+ ...getMockRequestTemplate(),
+ uri: 'https://mobius-eu-central-1.prod.infra.webex.com/api/v1/calling/web/myip',
method: 'GET',
+ });
+
+ expect(webex.request).nthCalledWith(4, {
...getMockRequestTemplate(),
- uri: `${uri}myip`,
+ uri: 'https://mobius-ap-southeast-2.prod.infra.webex.com/api/v1/calling/web/myip',
+ method: 'GET',
+ });
+
+ expect(handleErrorSpy).toBeCalledWith(failurePayload, expect.anything(), {
+ file: CALLING_CLIENT_FILE,
+ method: 'getMobiusServers',
});
+ expect(callingClient.primaryMobiusUris).toEqual([
+ `${callingClient['mobiusHost']}${URL_ENDPOINT}`,
+ ]);
+
expect(warnSpy).toBeCalledWith(
- 'Error in finding Mobius Servers. Will use the default URL.',
+ `Couldn't resolve the region and country code. Defaulting to the catalog entries to discover mobius servers`,
''
);
});
@@ -297,12 +321,14 @@ describe('CallingClient Tests', () => {
});
expect(webex.request).toBeCalledTimes(3);
- expect(callingClient.primaryMobiusUris).toEqual([uri]);
+ expect(callingClient.primaryMobiusUris).toEqual([
+ `${callingClient['mobiusHost']}${URL_ENDPOINT}`,
+ ]);
expect(webex.request).nthCalledWith(1, {
method: 'GET',
...getMockRequestTemplate(),
- uri: `${uri}myip`,
+ uri: `${callingClient['mobiusHost']}${URL_ENDPOINT}${IP_ENDPOINT}`,
});
expect(webex.request).nthCalledWith(2, {
@@ -315,7 +341,7 @@ describe('CallingClient Tests', () => {
});
expect(warnSpy).toBeCalledWith(
- 'Error in finding Mobius Servers. Will use the default URL.',
+ `Couldn't resolve the region and country code. Defaulting to the catalog entries to discover mobius servers`,
''
);
});
@@ -342,13 +368,47 @@ describe('CallingClient Tests', () => {
});
expect(webex.request).toBeCalledOnceWith({
...getMockRequestTemplate(),
- uri: `${uri}?regionCode=${regionBody.clientRegion}&countryCode=${regionBody.countryCode}`,
+ uri: `${callingClient['mobiusHost']}${URL_ENDPOINT}?regionCode=${regionBody.clientRegion}&countryCode=${regionBody.countryCode}`,
method: 'GET',
});
expect(handleErrorSpy).not.toBeCalled();
});
});
+ describe('Testing each cluster present withing host catalog', () => {
+ const mobiusCluster = [
+ 'mobius-eu-central-1.prod.infra.webex.com',
+ 'mobius-us-east-1.int.infra.webex.com',
+ 'mobius-eu-central-1.int.infra.webex.com',
+ ];
+
+ const checkCluster = async (
+ mockServiceHosts: ServiceHost[],
+ mockCatalog: Record
+ ) => {
+ webex.internal.services._hostCatalog = mockCatalog;
+ const callingClient = await createClient(webex, {logger: {level: LOGGER.INFO}});
+
+ expect(callingClient['mobiusClusters']).toStrictEqual(mockServiceHosts);
+ };
+
+ it.each(mobiusCluster)('%s', async (clusterName) => {
+ switch (clusterName) {
+ case 'mobius-eu-central-1.prod.infra.webex.com':
+ checkCluster(mockEUServiceHosts, mockCatalogEU);
+ break;
+ case 'mobius-us-east-1.int.infra.webex.com':
+ checkCluster(mockIntServiceHosts, mockCatalogUSInt);
+ break;
+ case 'mobius-eu-central-1.int.infra.webex.com':
+ checkCluster(mockEUIntServiceHosts, mockCatalogEUInt);
+ break;
+ default:
+ break;
+ }
+ });
+ });
+
describe('Network activity detection tests', () => {
let callingClient;
let line;
diff --git a/packages/calling/src/CallingClient/CallingClient.ts b/packages/calling/src/CallingClient/CallingClient.ts
index cf4cf78dc82..5b7dba62f97 100644
--- a/packages/calling/src/CallingClient/CallingClient.ts
+++ b/packages/calling/src/CallingClient/CallingClient.ts
@@ -6,7 +6,7 @@ import {Mutex} from 'async-mutex';
import {filterMobiusUris, handleCallingClientErrors, validateServiceData} from '../common/Utils';
import {LOGGER, LogContext} from '../Logger/types';
import SDKConnector from '../SDKConnector';
-import {ClientRegionInfo, ISDKConnector, WebexSDK} from '../SDKConnector/types';
+import {ClientRegionInfo, ISDKConnector, ServiceHost, WebexSDK} from '../SDKConnector/types';
import {Eventing} from '../Events/impl';
import {
CallingClientEventTypes,
@@ -40,6 +40,11 @@ import {
SPARK_USER_AGENT,
URL_ENDPOINT,
NETWORK_FLAP_TIMEOUT,
+ API_V1,
+ MOBIUS_US_PROD,
+ MOBIUS_EU_PROD,
+ MOBIUS_US_INT,
+ MOBIUS_EU_INT,
} from './constants';
import Line from './line';
import {ILine} from './line/types';
@@ -74,6 +79,10 @@ export class CallingClient extends Eventing implements
private backupMobiusUris: string[];
+ private mobiusClusters: ServiceHost[];
+
+ private mobiusHost: string;
+
public mediaEngine: typeof Media;
private lineDict: Record = {};
@@ -106,6 +115,12 @@ export class CallingClient extends Eventing implements
this.primaryMobiusUris = [];
this.backupMobiusUris = [];
+ this.mobiusClusters =
+ this.webex.internal.services._hostCatalog[MOBIUS_US_PROD] ||
+ this.webex.internal.services._hostCatalog[MOBIUS_EU_PROD] ||
+ this.webex.internal.services._hostCatalog[MOBIUS_US_INT] ||
+ this.webex.internal.services._hostCatalog[MOBIUS_EU_INT];
+ this.mobiusHost = '';
this.registerSessionsListener();
@@ -178,48 +193,57 @@ export class CallingClient extends Eventing implements
private async getClientRegionInfo(): Promise {
const regionInfo = {} as RegionInfo;
- try {
- const temp = await this.webex.request({
- uri: `${this.webex.internal.services._serviceUrls.mobius}${URL_ENDPOINT}${IP_ENDPOINT}`,
- method: HTTP_METHODS.GET,
- headers: {
- [CISCO_DEVICE_URL]: this.webex.internal.device.url,
- [SPARK_USER_AGENT]: CALLING_USER_AGENT,
- },
- service: ALLOWED_SERVICES.MOBIUS,
- });
+ for (const mobius of this.mobiusClusters) {
+ this.mobiusHost = `https://${mobius.host}${API_V1}`;
- const myIP = (temp.body as IpInfo).ipv4;
- const response = await this.webex.request({
- uri: `${DISCOVERY_URL}/${myIP}`,
- method: HTTP_METHODS.GET,
- addAuthHeader: false,
- headers: {
- [SPARK_USER_AGENT]: null,
- },
- });
+ try {
+ // eslint-disable-next-line no-await-in-loop
+ const temp = await this.webex.request({
+ uri: `${this.mobiusHost}${URL_ENDPOINT}${IP_ENDPOINT}`,
+ method: HTTP_METHODS.GET,
+ headers: {
+ [CISCO_DEVICE_URL]: this.webex.internal.device.url,
+ [SPARK_USER_AGENT]: CALLING_USER_AGENT,
+ },
+ service: ALLOWED_SERVICES.MOBIUS,
+ });
- const clientRegionInfo = response.body as ClientRegionInfo;
-
- regionInfo.clientRegion = clientRegionInfo?.clientRegion ? clientRegionInfo.clientRegion : '';
-
- regionInfo.countryCode = clientRegionInfo?.countryCode ? clientRegionInfo.countryCode : '';
- } catch (err: unknown) {
- handleCallingClientErrors(
- err as WebexRequestPayload,
- (clientError) => {
- this.metricManager.submitRegistrationMetric(
- METRIC_EVENT.REGISTRATION_ERROR,
- REG_ACTION.REGISTER,
- METRIC_TYPE.BEHAVIORAL,
- clientError
- );
- this.emit(CALLING_CLIENT_EVENT_KEYS.ERROR, clientError);
- },
- {method: GET_MOBIUS_SERVERS_UTIL, file: CALLING_CLIENT_FILE}
- );
- regionInfo.clientRegion = '';
- regionInfo.countryCode = '';
+ const myIP = (temp.body as IpInfo).ipv4;
+ // eslint-disable-next-line no-await-in-loop
+ const response = await this.webex.request({
+ uri: `${DISCOVERY_URL}/${myIP}`,
+ method: HTTP_METHODS.GET,
+ addAuthHeader: false,
+ headers: {
+ [SPARK_USER_AGENT]: null,
+ },
+ });
+
+ const clientRegionInfo = response.body as ClientRegionInfo;
+
+ regionInfo.clientRegion = clientRegionInfo?.clientRegion
+ ? clientRegionInfo.clientRegion
+ : '';
+
+ regionInfo.countryCode = clientRegionInfo?.countryCode ? clientRegionInfo.countryCode : '';
+ break;
+ } catch (err: unknown) {
+ handleCallingClientErrors(
+ err as WebexRequestPayload,
+ (clientError) => {
+ this.metricManager.submitRegistrationMetric(
+ METRIC_EVENT.REGISTRATION_ERROR,
+ REG_ACTION.REGISTER,
+ METRIC_TYPE.BEHAVIORAL,
+ clientError
+ );
+ this.emit(CALLING_CLIENT_EVENT_KEYS.ERROR, clientError);
+ },
+ {method: GET_MOBIUS_SERVERS_UTIL, file: CALLING_CLIENT_FILE}
+ );
+ regionInfo.clientRegion = '';
+ regionInfo.countryCode = '';
+ }
}
return regionInfo;
@@ -250,6 +274,7 @@ export class CallingClient extends Eventing implements
});
clientRegion = this.sdkConfig?.discovery?.region;
countryCode = this.sdkConfig?.discovery?.country;
+ this.mobiusHost = this.webex.internal.services._serviceUrls.mobius;
} else {
log.info('Updating region and country through Region discovery', {
file: CALLING_CLIENT_FILE,
@@ -269,8 +294,9 @@ export class CallingClient extends Eventing implements
);
try {
+ // eslint-disable-next-line no-await-in-loop
const temp = await this.webex.request({
- uri: `${this.webex.internal.services._serviceUrls.mobius}${URL_ENDPOINT}?regionCode=${clientRegion}&countryCode=${countryCode}`,
+ uri: `${this.mobiusHost}${URL_ENDPOINT}?regionCode=${clientRegion}&countryCode=${countryCode}`,
method: HTTP_METHODS.GET,
headers: {
[CISCO_DEVICE_URL]: this.webex.internal.device.url,
@@ -283,10 +309,7 @@ export class CallingClient extends Eventing implements
const mobiusServers = temp.body as MobiusServers;
/* update arrays of Mobius Uris. */
- const mobiusUris = filterMobiusUris(
- mobiusServers,
- this.webex.internal.services._serviceUrls.mobius
- );
+ const mobiusUris = filterMobiusUris(mobiusServers, this.mobiusHost);
this.primaryMobiusUris = mobiusUris.primary;
this.backupMobiusUris = mobiusUris.backup;
log.info(
@@ -320,10 +343,12 @@ export class CallingClient extends Eventing implements
*/
if (useDefault) {
- log.warn('Error in finding Mobius Servers. Will use the default URL.', '' as LogContext);
- this.primaryMobiusUris = [
- `${this.webex.internal.services._serviceUrls.mobius}${URL_ENDPOINT}`,
- ];
+ log.warn(
+ `Couldn't resolve the region and country code. Defaulting to the catalog entries to discover mobius servers`,
+ '' as LogContext
+ );
+ this.mobiusHost = `https://${this.mobiusClusters[0].host}${API_V1}`;
+ this.primaryMobiusUris = [`${this.mobiusHost}${URL_ENDPOINT}`];
}
}
diff --git a/packages/calling/src/CallingClient/callingClientFixtures.ts b/packages/calling/src/CallingClient/callingClientFixtures.ts
index db3cd15d893..ec9c69cee43 100644
--- a/packages/calling/src/CallingClient/callingClientFixtures.ts
+++ b/packages/calling/src/CallingClient/callingClientFixtures.ts
@@ -47,6 +47,131 @@ const registrationPayload = ({
const uri = `${webex.internal.services._serviceUrls.mobius}${URL_ENDPOINT}`;
const myIP = mockIPReturnBody.ipv4;
+const mockUSServiceHosts = [
+ {
+ host: 'mobius-us-east-1.prod.infra.webex.com',
+ ttl: -1,
+ priority: 5,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+ {
+ host: 'mobius-ca-central-1.prod.infra.webex.com',
+ ttl: -1,
+ priority: 10,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+ {
+ host: 'mobius-eu-central-1.prod.infra.webex.com',
+ ttl: -1,
+ priority: 15,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+ {
+ host: 'mobius-ap-southeast-2.prod.infra.webex.com',
+ ttl: -1,
+ priority: 20,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+];
+
+const mockEUServiceHosts = [
+ {
+ host: 'mobius-eu-central-1.prod.infra.webex.com',
+ ttl: -1,
+ priority: 5,
+ id: 'urn:TEAM:eu-central-1_k:mobius',
+ },
+ {
+ host: 'mobius-us-east-1.prod.infra.webex.com',
+ ttl: -1,
+ priority: 15,
+ id: 'urn:TEAM:eu-central-1_k:mobius',
+ },
+ {
+ host: 'mobius-ca-central-1.prod.infra.webex.com',
+ ttl: -1,
+ priority: 10,
+ id: 'urn:TEAM:eu-central-1_k:mobius',
+ },
+ {
+ host: 'mobius-ap-southeast-2.prod.infra.webex.com',
+ ttl: -1,
+ priority: 20,
+ id: 'urn:TEAM:eu-central-1_k:mobius',
+ },
+];
+
+const mockEUIntServiceHosts = [
+ {
+ host: 'mobius-eu-central-1.int.infra.webex.com',
+ ttl: -1,
+ priority: 15,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+ {
+ host: 'mobius-us-east-1.int.infra.webex.com',
+ ttl: -1,
+ priority: 5,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+ {
+ host: 'mobius-ca-central-1.int.infra.webex.com',
+ ttl: -1,
+ priority: 10,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+ {
+ host: 'mobius-ap-southeast-2.int.infra.webex.com',
+ ttl: -1,
+ priority: 20,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+];
+
+const mockIntServiceHosts = [
+ {
+ host: 'mobius-us-east-1.int.infra.webex.com',
+ ttl: -1,
+ priority: 5,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+ {
+ host: 'mobius-ca-central-1.int.infra.webex.com',
+ ttl: -1,
+ priority: 10,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+ {
+ host: 'mobius-eu-central-1.int.infra.webex.com',
+ ttl: -1,
+ priority: 15,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+ {
+ host: 'mobius-ap-southeast-2.int.infra.webex.com',
+ ttl: -1,
+ priority: 20,
+ id: 'urn:TEAM:us-east-2_a:mobius',
+ },
+];
+
+const mockCatalogEU = {
+ 'mobius-eu-central-1.prod.infra.webex.com': mockEUServiceHosts,
+};
+const mockCatalogEUInt = {
+ 'mobius-eu-central-1.int.infra.webex.com': mockEUIntServiceHosts,
+};
+
+const mockCatalogUS = {
+ 'mobius-us-east-1.prod.infra.webex.com': mockUSServiceHosts,
+ 'mobius-eu-central-1.prod.infra.webex.com': mockEUServiceHosts,
+};
+
+const mockCatalogUSInt = {
+ 'mobius-us-east-1.int.infra.webex.com': mockIntServiceHosts,
+ 'mobius-eu-central-1.int.infra.webex.com': mockEUIntServiceHosts,
+};
+
export {
ipPayload,
regionBody,
@@ -56,4 +181,12 @@ export {
registrationPayload,
uri,
myIP,
+ mockEUServiceHosts,
+ mockEUIntServiceHosts,
+ mockIntServiceHosts,
+ mockUSServiceHosts,
+ mockCatalogEU,
+ mockCatalogEUInt,
+ mockCatalogUS,
+ mockCatalogUSInt,
};
diff --git a/packages/calling/src/CallingClient/constants.ts b/packages/calling/src/CallingClient/constants.ts
index bc3c61c791e..7c96c421fd2 100644
--- a/packages/calling/src/CallingClient/constants.ts
+++ b/packages/calling/src/CallingClient/constants.ts
@@ -58,6 +58,7 @@ export const SCIM_USER_FILTER = 'v1/Users?filter=';
export const SPARK_USER_AGENT = 'spark-user-agent';
export const REGISTER_RETRY_TIMEOUT = 10000;
export const SUPPLEMENTARY_SERVICES_TIMEOUT = 10000;
+export const API_V1 = '/api/v1';
export const URL_ENDPOINT = '/calling/web/';
export const VALID_PHONE = /[\d\s()*#+.-]+/;
export const WEB_AGENT = '(web)';
@@ -119,3 +120,7 @@ export const FAILOVER_UTIL = 'startFailoverTimer';
export const NETWORK_CHANGE_DETECTION_UTIL = 'detectNetworkChange';
export const CALLS_CLEARED_HANDLER_UTIL = 'callsClearedHandler';
export const RECONNECT_UTIL = 'reconnectOnFailure';
+export const MOBIUS_US_PROD = 'mobius-us-east-1.prod.infra.webex.com';
+export const MOBIUS_EU_PROD = 'mobius-eu-central-1.prod.infra.webex.com';
+export const MOBIUS_US_INT = 'mobius-us-east-1.int.infra.webex.com';
+export const MOBIUS_EU_INT = 'mobius-eu-central-1.int.infra.webex.com';
diff --git a/packages/calling/src/Events/types.ts b/packages/calling/src/Events/types.ts
index 6c0580369f2..6e3f5819dfd 100644
--- a/packages/calling/src/Events/types.ts
+++ b/packages/calling/src/Events/types.ts
@@ -92,6 +92,15 @@ export type CallRecordListOther = {
email?: string;
};
+export type RedirectionDetails = {
+ phoneNumber?: string;
+ sipUrl?: string;
+ name?: string;
+ reason: string;
+ userId?: string;
+ isPrivate: boolean;
+};
+
export enum SessionType {
SPARK = 'SPARK',
WEBEX_CALLING = 'WEBEXCALLING',
@@ -116,6 +125,9 @@ export type UserSession = {
other: CallRecordListOther;
sessionType: SessionType;
direction: string;
+ callingSpecifics?: {
+ redirectionDetails: RedirectionDetails;
+ };
};
export type CallingParty = {
diff --git a/packages/calling/src/SDKConnector/types.ts b/packages/calling/src/SDKConnector/types.ts
index d12f68abd97..6df1dfb67e5 100644
--- a/packages/calling/src/SDKConnector/types.ts
+++ b/packages/calling/src/SDKConnector/types.ts
@@ -16,7 +16,7 @@ export type ServiceHost = {
ttl: number;
priority: number;
id: string;
- homeCluster: boolean;
+ homeCluster?: boolean;
};
export type Model = {
@@ -97,6 +97,7 @@ export interface WebexSDK {
presence: unknown;
support: unknown;
services: {
+ _hostCatalog: Record;
_serviceUrls: {
mobius: string;
identity: string;
diff --git a/packages/calling/src/common/testUtil.ts b/packages/calling/src/common/testUtil.ts
index 3a0691f5a8d..a3ef45bb5f1 100644
--- a/packages/calling/src/common/testUtil.ts
+++ b/packages/calling/src/common/testUtil.ts
@@ -66,6 +66,7 @@ export function getTestUtilsWebex() {
presence: jest.fn(),
support: jest.fn(),
services: {
+ _hostCatalog: {},
_serviceUrls: {
mobius: 'https://mobius.aintgen-a-1.int.infra.webex.com/api/v1',
identity: 'https://identity-b-us.webex.com',
diff --git a/packages/webex/package.json b/packages/webex/package.json
index 889196adcfe..cb17d5297be 100644
--- a/packages/webex/package.json
+++ b/packages/webex/package.json
@@ -10,6 +10,7 @@
"Kesava Krishnan Madavan ",
"Priya Kesari ",
"Rajesh Kumar ",
+ "Shreyas Sharma ",
"Sreekanth Narayanan "
],
"main": "dist/index.js",
diff --git a/tooling/babel-plugin-inject-package-version.js b/tooling/babel-plugin-inject-package-version.js
index 69515e739b1..750da6a8dca 100644
--- a/tooling/babel-plugin-inject-package-version.js
+++ b/tooling/babel-plugin-inject-package-version.js
@@ -4,7 +4,7 @@
const t = require('@babel/types');
-const {version} = require('../package.json');
+const {version} = require('../packages/webex/package.json');
/**
* Simple babel transform for ensuring that every WebexPlugin (and WebexCore)