webpackJsonp([19],{
/***/ "/qCn":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react__ = __webpack_require__("GiK3");
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_react___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_react__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_rc_notification__ = __webpack_require__("Hx0i");
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__icon__ = __webpack_require__("FC3+");
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
var defaultDuration = 3;
var defaultTop;
var messageInstance;
var key = 1;
var prefixCls = 'ant-message';
var transitionName = 'move-up';
var getContainer;
var maxCount;
function getMessageInstance(callback) {
if (messageInstance) {
callback(messageInstance);
return;
}
__WEBPACK_IMPORTED_MODULE_1_rc_notification__["a" /* default */].newInstance({
prefixCls: prefixCls,
transitionName: transitionName,
style: {
top: defaultTop
},
getContainer: getContainer,
maxCount: maxCount
}, function (instance) {
if (messageInstance) {
callback(messageInstance);
return;
}
messageInstance = instance;
callback(instance);
});
}
function notice(args) {
var duration = args.duration !== undefined ? args.duration : defaultDuration;
var iconType = {
info: 'info-circle',
success: 'check-circle',
error: 'close-circle',
warning: 'exclamation-circle',
loading: 'loading'
}[args.type];
var target = args.key || key++;
var closePromise = new Promise(function (resolve) {
var callback = function callback() {
if (typeof args.onClose === 'function') {
args.onClose();
}
return resolve(true);
};
getMessageInstance(function (instance) {
var iconNode = /*#__PURE__*/__WEBPACK_IMPORTED_MODULE_0_react__["createElement"](__WEBPACK_IMPORTED_MODULE_2__icon__["default"], {
type: iconType,
theme: iconType === 'loading' ? 'outlined' : 'filled'
});
var switchIconNode = iconType ? iconNode : '';
instance.notice({
key: target,
duration: duration,
style: {},
content: /*#__PURE__*/__WEBPACK_IMPORTED_MODULE_0_react__["createElement"]("div", {
className: "".concat(prefixCls, "-custom-content").concat(args.type ? " ".concat(prefixCls, "-").concat(args.type) : '')
}, args.icon ? args.icon : switchIconNode, /*#__PURE__*/__WEBPACK_IMPORTED_MODULE_0_react__["createElement"]("span", null, args.content)),
onClose: callback
});
});
});
var result = function result() {
if (messageInstance) {
messageInstance.removeNotice(target);
}
};
result.then = function (filled, rejected) {
return closePromise.then(filled, rejected);
};
result.promise = closePromise;
return result;
}
function isArgsProps(content) {
return Object.prototype.toString.call(content) === '[object Object]' && !!content.content;
}
var api = {
open: notice,
config: function config(options) {
if (options.top !== undefined) {
defaultTop = options.top;
messageInstance = null; // delete messageInstance for new defaultTop
}
if (options.duration !== undefined) {
defaultDuration = options.duration;
}
if (options.prefixCls !== undefined) {
prefixCls = options.prefixCls;
}
if (options.getContainer !== undefined) {
getContainer = options.getContainer;
}
if (options.transitionName !== undefined) {
transitionName = options.transitionName;
messageInstance = null; // delete messageInstance for new transitionName
}
if (options.maxCount !== undefined) {
maxCount = options.maxCount;
messageInstance = null;
}
},
destroy: function destroy() {
if (messageInstance) {
messageInstance.destroy();
messageInstance = null;
}
}
};
['success', 'info', 'warning', 'error', 'loading'].forEach(function (type) {
api[type] = function (content, duration, onClose) {
if (isArgsProps(content)) {
return api.open(_extends(_extends({}, content), {
type: type
}));
}
if (typeof duration === 'function') {
onClose = duration;
duration = undefined;
}
return api.open({
content: content,
duration: duration,
type: type,
onClose: onClose
});
};
});
api.warn = api.warning;
/* harmony default export */ __webpack_exports__["default"] = (api);
/***/ }),
/***/ "06MX":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _interopRequireDefault = __webpack_require__("ouCL");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _classCallCheck2 = _interopRequireDefault(__webpack_require__("Q9dM"));
var _createClass2 = _interopRequireDefault(__webpack_require__("wm7F"));
var _possibleConstructorReturn2 = _interopRequireDefault(__webpack_require__("F6AD"));
var _getPrototypeOf2 = _interopRequireDefault(__webpack_require__("fghW"));
var _inherits2 = _interopRequireDefault(__webpack_require__("QwVp"));
var _react = _interopRequireWildcard(__webpack_require__("GiK3"));
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
var Mouse = exports.default = /*#__PURE__*/function (_Component) {
function Mouse(props) {
var _this;
(0, _classCallCheck2.default)(this, Mouse);
_this = _callSuper(this, Mouse, [props]);
_this.handleMouseDown = function (e) {
// console.log(e)
if (_this.state.isDown == false) {
return;
}
_this.setState({
clientX: e.clientX,
//获取x坐标和y坐标
clientY: e.clientY,
// isDown: true, //开关打开
cursor: 'move' //设置样式
});
};
_this.handleMouseMove = function (e) {
if (_this.state.isDown == false) {
return;
}
//获取x坐标和y坐标
var nx = e.clientX;
var ny = e.clientY;
// //计算移动后的左偏移量和顶部的偏移量
var nl = nx - (_this.state.clientX - _this.state.offsetLeft);
var nt = ny - (_this.state.clientY - _this.state.offsetTop);
_this.setState({
x: nl,
y: nt
});
};
_this.handleMouseUp = function (e) {
// console.log(e)
_this.setState({
isDown: false,
//开关关闭
cursor: 'default'
});
};
_this.state = {
clientX: 0,
clientY: 0,
x: 0,
y: 0,
isDown: props.isDown || false,
cursor: 'default',
offsetLeft: props.offsetLeft || 0,
offsetTop: props.offsetTop || 0
};
return _this;
}
(0, _inherits2.default)(Mouse, _Component);
return (0, _createClass2.default)(Mouse, [{
key: "componentDidMount",
value: function componentDidMount() {
window.addEventListener('mousedown', this.handleMouseDown);
window.addEventListener('mousemove', this.handleMouseMove);
window.addEventListener('mouseup', this.handleMouseUp);
}
}, {
key: "componentWillReceiveProps",
value: function componentWillReceiveProps(nextProps) {
// console.log(nextProps)
this.setState({
isDown: nextProps.isDown,
offsetLeft: nextProps.offsetLeft,
offsetTop: nextProps.offsetTop
});
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
// window.removeEventListener('mousemove', this.handleMouseMove)
}
}, {
key: "render",
value: function render() {
var _this$state = this.state,
x = _this$state.x,
y = _this$state.y,
cursor = _this$state.cursor; // console.log('Mouse', this.props.children);
return /*#__PURE__*/_react.default.createElement("div", null, this.props && this.props.children && this.props.children({
x: x,
y: y,
cursor: cursor
}));
}
}]);
}(_react.Component);
/***/ }),
/***/ "0vbU":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,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"
/***/ }),
/***/ "0wj8":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(global) {
/**!
* @fileOverview Kickass library to create and place poppers near their reference elements.
* @version 1.12.6
* @license
* Copyright (c) 2016 Federico Zivolo and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
(function (global, factory) {
true ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.Popper = factory();
})(void 0, function () {
'use strict';
var isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
var longerTimeoutBrowsers = ['Edge', 'Trident', 'Firefox'];
var timeoutDuration = 0;
for (var i = 0; i < longerTimeoutBrowsers.length; i += 1) {
if (isBrowser && navigator.userAgent.indexOf(longerTimeoutBrowsers[i]) >= 0) {
timeoutDuration = 1;
break;
}
}
function microtaskDebounce(fn) {
var called = false;
return function () {
if (called) {
return;
}
called = true;
Promise.resolve().then(function () {
called = false;
fn();
});
};
}
function taskDebounce(fn) {
var scheduled = false;
return function () {
if (!scheduled) {
scheduled = true;
setTimeout(function () {
scheduled = false;
fn();
}, timeoutDuration);
}
};
}
var supportsMicroTasks = isBrowser && window.Promise;
/**
* Create a debounced version of a method, that's asynchronously deferred
* but called in the minimum time possible.
*
* @method
* @memberof Popper.Utils
* @argument {Function} fn
* @returns {Function}
*/
var debounce = supportsMicroTasks ? microtaskDebounce : taskDebounce;
/**
* Check if the given variable is a function
* @method
* @memberof Popper.Utils
* @argument {Any} functionToCheck - variable to check
* @returns {Boolean} answer to: is a function?
*/
function isFunction(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
}
/**
* Get CSS computed property of the given element
* @method
* @memberof Popper.Utils
* @argument {Eement} element
* @argument {String} property
*/
function getStyleComputedProperty(element, property) {
if (element.nodeType !== 1) {
return [];
}
// NOTE: 1 DOM access here
var css = window.getComputedStyle(element, null);
return property ? css[property] : css;
}
/**
* Returns the parentNode or the host of the element
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @returns {Element} parent
*/
function getParentNode(element) {
if (element.nodeName === 'HTML') {
return element;
}
return element.parentNode || element.host;
}
/**
* Returns the scrolling parent of the given element
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @returns {Element} scroll parent
*/
function getScrollParent(element) {
// Return body, `getScroll` will take care to get the correct `scrollTop` from it
if (!element) {
return window.document.body;
}
switch (element.nodeName) {
case 'HTML':
case 'BODY':
return element.ownerDocument.body;
case '#document':
return element.body;
}
// Firefox want us to check `-x` and `-y` variations as well
var _getStyleComputedProp = getStyleComputedProperty(element),
overflow = _getStyleComputedProp.overflow,
overflowX = _getStyleComputedProp.overflowX,
overflowY = _getStyleComputedProp.overflowY;
if (/(auto|scroll)/.test(overflow + overflowY + overflowX)) {
return element;
}
return getScrollParent(getParentNode(element));
}
/**
* Returns the offset parent of the given element
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @returns {Element} offset parent
*/
function getOffsetParent(element) {
// NOTE: 1 DOM access here
var offsetParent = element && element.offsetParent;
var nodeName = offsetParent && offsetParent.nodeName;
if (!nodeName || nodeName === 'BODY' || nodeName === 'HTML') {
if (element) {
return element.ownerDocument.documentElement;
}
return window.document.documentElement;
}
// .offsetParent will return the closest TD or TABLE in case
// no offsetParent is present, I hate this job...
if (['TD', 'TABLE'].indexOf(offsetParent.nodeName) !== -1 && getStyleComputedProperty(offsetParent, 'position') === 'static') {
return getOffsetParent(offsetParent);
}
return offsetParent;
}
function isOffsetContainer(element) {
var nodeName = element.nodeName;
if (nodeName === 'BODY') {
return false;
}
return nodeName === 'HTML' || getOffsetParent(element.firstElementChild) === element;
}
/**
* Finds the root node (document, shadowDOM root) of the given element
* @method
* @memberof Popper.Utils
* @argument {Element} node
* @returns {Element} root node
*/
function getRoot(node) {
if (node.parentNode !== null) {
return getRoot(node.parentNode);
}
return node;
}
/**
* Finds the offset parent common to the two provided nodes
* @method
* @memberof Popper.Utils
* @argument {Element} element1
* @argument {Element} element2
* @returns {Element} common offset parent
*/
function findCommonOffsetParent(element1, element2) {
// This check is needed to avoid errors in case one of the elements isn't defined for any reason
if (!element1 || !element1.nodeType || !element2 || !element2.nodeType) {
return window.document.documentElement;
}
// Here we make sure to give as "start" the element that comes first in the DOM
var order = element1.compareDocumentPosition(element2) & Node.DOCUMENT_POSITION_FOLLOWING;
var start = order ? element1 : element2;
var end = order ? element2 : element1;
// Get common ancestor container
var range = document.createRange();
range.setStart(start, 0);
range.setEnd(end, 0);
var commonAncestorContainer = range.commonAncestorContainer;
// Both nodes are inside #document
if (element1 !== commonAncestorContainer && element2 !== commonAncestorContainer || start.contains(end)) {
if (isOffsetContainer(commonAncestorContainer)) {
return commonAncestorContainer;
}
return getOffsetParent(commonAncestorContainer);
}
// one of the nodes is inside shadowDOM, find which one
var element1root = getRoot(element1);
if (element1root.host) {
return findCommonOffsetParent(element1root.host, element2);
} else {
return findCommonOffsetParent(element1, getRoot(element2).host);
}
}
/**
* Gets the scroll value of the given element in the given side (top and left)
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @argument {String} side `top` or `left`
* @returns {number} amount of scrolled pixels
*/
function getScroll(element) {
var side = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'top';
var upperSide = side === 'top' ? 'scrollTop' : 'scrollLeft';
var nodeName = element.nodeName;
if (nodeName === 'BODY' || nodeName === 'HTML') {
var html = element.ownerDocument.documentElement;
var scrollingElement = element.ownerDocument.scrollingElement || html;
return scrollingElement[upperSide];
}
return element[upperSide];
}
/*
* Sum or subtract the element scroll values (left and top) from a given rect object
* @method
* @memberof Popper.Utils
* @param {Object} rect - Rect object you want to change
* @param {HTMLElement} element - The element from the function reads the scroll values
* @param {Boolean} subtract - set to true if you want to subtract the scroll values
* @return {Object} rect - The modifier rect object
*/
function includeScroll(rect, element) {
var subtract = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var scrollTop = getScroll(element, 'top');
var scrollLeft = getScroll(element, 'left');
var modifier = subtract ? -1 : 1;
rect.top += scrollTop * modifier;
rect.bottom += scrollTop * modifier;
rect.left += scrollLeft * modifier;
rect.right += scrollLeft * modifier;
return rect;
}
/*
* Helper to detect borders of a given element
* @method
* @memberof Popper.Utils
* @param {CSSStyleDeclaration} styles
* Result of `getStyleComputedProperty` on the given element
* @param {String} axis - `x` or `y`
* @return {number} borders - The borders size of the given axis
*/
function getBordersSize(styles, axis) {
var sideA = axis === 'x' ? 'Left' : 'Top';
var sideB = sideA === 'Left' ? 'Right' : 'Bottom';
return +styles['border' + sideA + 'Width'].split('px')[0] + +styles['border' + sideB + 'Width'].split('px')[0];
}
/**
* Tells if you are running Internet Explorer 10
* @method
* @memberof Popper.Utils
* @returns {Boolean} isIE10
*/
var isIE10 = undefined;
var isIE10$1 = function isIE10$1() {
if (isIE10 === undefined) {
isIE10 = navigator.appVersion.indexOf('MSIE 10') !== -1;
}
return isIE10;
};
function getSize(axis, body, html, computedStyle) {
return Math.max(body['offset' + axis], body['scroll' + axis], html['client' + axis], html['offset' + axis], html['scroll' + axis], isIE10$1() ? html['offset' + axis] + computedStyle['margin' + (axis === 'Height' ? 'Top' : 'Left')] + computedStyle['margin' + (axis === 'Height' ? 'Bottom' : 'Right')] : 0);
}
function getWindowSizes() {
var body = window.document.body;
var html = window.document.documentElement;
var computedStyle = isIE10$1() && window.getComputedStyle(html);
return {
height: getSize('Height', body, html, computedStyle),
width: getSize('Width', body, html, computedStyle)
};
}
var classCallCheck = function classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var defineProperty = function defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
};
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
/**
* Given element offsets, generate an output similar to getBoundingClientRect
* @method
* @memberof Popper.Utils
* @argument {Object} offsets
* @returns {Object} ClientRect like output
*/
function getClientRect(offsets) {
return _extends({}, offsets, {
right: offsets.left + offsets.width,
bottom: offsets.top + offsets.height
});
}
/**
* Get bounding client rect of given element
* @method
* @memberof Popper.Utils
* @param {HTMLElement} element
* @return {Object} client rect
*/
function getBoundingClientRect(element) {
var rect = {};
// IE10 10 FIX: Please, don't ask, the element isn't
// considered in DOM in some circumstances...
// This isn't reproducible in IE10 compatibility mode of IE11
if (isIE10$1()) {
try {
rect = element.getBoundingClientRect();
var scrollTop = getScroll(element, 'top');
var scrollLeft = getScroll(element, 'left');
rect.top += scrollTop;
rect.left += scrollLeft;
rect.bottom += scrollTop;
rect.right += scrollLeft;
} catch (err) {}
} else {
rect = element.getBoundingClientRect();
}
var result = {
left: rect.left,
top: rect.top,
width: rect.right - rect.left,
height: rect.bottom - rect.top
};
// subtract scrollbar size from sizes
var sizes = element.nodeName === 'HTML' ? getWindowSizes() : {};
var width = sizes.width || element.clientWidth || result.right - result.left;
var height = sizes.height || element.clientHeight || result.bottom - result.top;
var horizScrollbar = element.offsetWidth - width;
var vertScrollbar = element.offsetHeight - height;
// if an hypothetical scrollbar is detected, we must be sure it's not a `border`
// we make this check conditional for performance reasons
if (horizScrollbar || vertScrollbar) {
var styles = getStyleComputedProperty(element);
horizScrollbar -= getBordersSize(styles, 'x');
vertScrollbar -= getBordersSize(styles, 'y');
result.width -= horizScrollbar;
result.height -= vertScrollbar;
}
return getClientRect(result);
}
function getOffsetRectRelativeToArbitraryNode(children, parent) {
var isIE10 = isIE10$1();
var isHTML = parent.nodeName === 'HTML';
var childrenRect = getBoundingClientRect(children);
var parentRect = getBoundingClientRect(parent);
var scrollParent = getScrollParent(children);
var styles = getStyleComputedProperty(parent);
var borderTopWidth = +styles.borderTopWidth.split('px')[0];
var borderLeftWidth = +styles.borderLeftWidth.split('px')[0];
var offsets = getClientRect({
top: childrenRect.top - parentRect.top - borderTopWidth,
left: childrenRect.left - parentRect.left - borderLeftWidth,
width: childrenRect.width,
height: childrenRect.height
});
offsets.marginTop = 0;
offsets.marginLeft = 0;
// Subtract margins of documentElement in case it's being used as parent
// we do this only on HTML because it's the only element that behaves
// differently when margins are applied to it. The margins are included in
// the box of the documentElement, in the other cases not.
if (!isIE10 && isHTML) {
var marginTop = +styles.marginTop.split('px')[0];
var marginLeft = +styles.marginLeft.split('px')[0];
offsets.top -= borderTopWidth - marginTop;
offsets.bottom -= borderTopWidth - marginTop;
offsets.left -= borderLeftWidth - marginLeft;
offsets.right -= borderLeftWidth - marginLeft;
// Attach marginTop and marginLeft because in some circumstances we may need them
offsets.marginTop = marginTop;
offsets.marginLeft = marginLeft;
}
if (isIE10 ? parent.contains(scrollParent) : parent === scrollParent && scrollParent.nodeName !== 'BODY') {
offsets = includeScroll(offsets, parent);
}
return offsets;
}
function getViewportOffsetRectRelativeToArtbitraryNode(element) {
var html = element.ownerDocument.documentElement;
var relativeOffset = getOffsetRectRelativeToArbitraryNode(element, html);
var width = Math.max(html.clientWidth, window.innerWidth || 0);
var height = Math.max(html.clientHeight, window.innerHeight || 0);
var scrollTop = getScroll(html);
var scrollLeft = getScroll(html, 'left');
var offset = {
top: scrollTop - relativeOffset.top + relativeOffset.marginTop,
left: scrollLeft - relativeOffset.left + relativeOffset.marginLeft,
width: width,
height: height
};
return getClientRect(offset);
}
/**
* Check if the given element is fixed or is inside a fixed parent
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @argument {Element} customContainer
* @returns {Boolean} answer to "isFixed?"
*/
function isFixed(element) {
var nodeName = element.nodeName;
if (nodeName === 'BODY' || nodeName === 'HTML') {
return false;
}
if (getStyleComputedProperty(element, 'position') === 'fixed') {
return true;
}
return isFixed(getParentNode(element));
}
/**
* Computed the boundaries limits and return them
* @method
* @memberof Popper.Utils
* @param {HTMLElement} popper
* @param {HTMLElement} reference
* @param {number} padding
* @param {HTMLElement} boundariesElement - Element used to define the boundaries
* @returns {Object} Coordinates of the boundaries
*/
function getBoundaries(popper, reference, padding, boundariesElement) {
// NOTE: 1 DOM access here
var boundaries = {
top: 0,
left: 0
};
var offsetParent = findCommonOffsetParent(popper, reference);
// Handle viewport case
if (boundariesElement === 'viewport') {
boundaries = getViewportOffsetRectRelativeToArtbitraryNode(offsetParent);
} else {
// Handle other cases based on DOM element used as boundaries
var boundariesNode = void 0;
if (boundariesElement === 'scrollParent') {
boundariesNode = getScrollParent(getParentNode(popper));
if (boundariesNode.nodeName === 'BODY') {
boundariesNode = popper.ownerDocument.documentElement;
}
} else if (boundariesElement === 'window') {
boundariesNode = popper.ownerDocument.documentElement;
} else {
boundariesNode = boundariesElement;
}
var offsets = getOffsetRectRelativeToArbitraryNode(boundariesNode, offsetParent);
// In case of HTML, we need a different computation
if (boundariesNode.nodeName === 'HTML' && !isFixed(offsetParent)) {
var _getWindowSizes = getWindowSizes(),
height = _getWindowSizes.height,
width = _getWindowSizes.width;
boundaries.top += offsets.top - offsets.marginTop;
boundaries.bottom = height + offsets.top;
boundaries.left += offsets.left - offsets.marginLeft;
boundaries.right = width + offsets.left;
} else {
// for all the other DOM elements, this one is good
boundaries = offsets;
}
}
// Add paddings
boundaries.left += padding;
boundaries.top += padding;
boundaries.right -= padding;
boundaries.bottom -= padding;
return boundaries;
}
function getArea(_ref) {
var width = _ref.width,
height = _ref.height;
return width * height;
}
/**
* Utility used to transform the `auto` placement to the placement with more
* available space.
* @method
* @memberof Popper.Utils
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function computeAutoPlacement(placement, refRect, popper, reference, boundariesElement) {
var padding = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 0;
if (placement.indexOf('auto') === -1) {
return placement;
}
var boundaries = getBoundaries(popper, reference, padding, boundariesElement);
var rects = {
top: {
width: boundaries.width,
height: refRect.top - boundaries.top
},
right: {
width: boundaries.right - refRect.right,
height: boundaries.height
},
bottom: {
width: boundaries.width,
height: boundaries.bottom - refRect.bottom
},
left: {
width: refRect.left - boundaries.left,
height: boundaries.height
}
};
var sortedAreas = Object.keys(rects).map(function (key) {
return _extends({
key: key
}, rects[key], {
area: getArea(rects[key])
});
}).sort(function (a, b) {
return b.area - a.area;
});
var filteredAreas = sortedAreas.filter(function (_ref2) {
var width = _ref2.width,
height = _ref2.height;
return width >= popper.clientWidth && height >= popper.clientHeight;
});
var computedPlacement = filteredAreas.length > 0 ? filteredAreas[0].key : sortedAreas[0].key;
var variation = placement.split('-')[1];
return computedPlacement + (variation ? '-' + variation : '');
}
/**
* Get offsets to the reference element
* @method
* @memberof Popper.Utils
* @param {Object} state
* @param {Element} popper - the popper element
* @param {Element} reference - the reference element (the popper will be relative to this)
* @returns {Object} An object containing the offsets which will be applied to the popper
*/
function getReferenceOffsets(state, popper, reference) {
var commonOffsetParent = findCommonOffsetParent(popper, reference);
return getOffsetRectRelativeToArbitraryNode(reference, commonOffsetParent);
}
/**
* Get the outer sizes of the given element (offset size + margins)
* @method
* @memberof Popper.Utils
* @argument {Element} element
* @returns {Object} object containing width and height properties
*/
function getOuterSizes(element) {
var styles = window.getComputedStyle(element);
var x = parseFloat(styles.marginTop) + parseFloat(styles.marginBottom);
var y = parseFloat(styles.marginLeft) + parseFloat(styles.marginRight);
var result = {
width: element.offsetWidth + y,
height: element.offsetHeight + x
};
return result;
}
/**
* Get the opposite placement of the given one
* @method
* @memberof Popper.Utils
* @argument {String} placement
* @returns {String} flipped placement
*/
function getOppositePlacement(placement) {
var hash = {
left: 'right',
right: 'left',
bottom: 'top',
top: 'bottom'
};
return placement.replace(/left|right|bottom|top/g, function (matched) {
return hash[matched];
});
}
/**
* Get offsets to the popper
* @method
* @memberof Popper.Utils
* @param {Object} position - CSS position the Popper will get applied
* @param {HTMLElement} popper - the popper element
* @param {Object} referenceOffsets - the reference offsets (the popper will be relative to this)
* @param {String} placement - one of the valid placement options
* @returns {Object} popperOffsets - An object containing the offsets which will be applied to the popper
*/
function getPopperOffsets(popper, referenceOffsets, placement) {
placement = placement.split('-')[0];
// Get popper node sizes
var popperRect = getOuterSizes(popper);
// Add position, width and height to our offsets object
var popperOffsets = {
width: popperRect.width,
height: popperRect.height
};
// depending by the popper placement we have to compute its offsets slightly differently
var isHoriz = ['right', 'left'].indexOf(placement) !== -1;
var mainSide = isHoriz ? 'top' : 'left';
var secondarySide = isHoriz ? 'left' : 'top';
var measurement = isHoriz ? 'height' : 'width';
var secondaryMeasurement = !isHoriz ? 'height' : 'width';
popperOffsets[mainSide] = referenceOffsets[mainSide] + referenceOffsets[measurement] / 2 - popperRect[measurement] / 2;
if (placement === secondarySide) {
popperOffsets[secondarySide] = referenceOffsets[secondarySide] - popperRect[secondaryMeasurement];
} else {
popperOffsets[secondarySide] = referenceOffsets[getOppositePlacement(secondarySide)];
}
return popperOffsets;
}
/**
* Mimics the `find` method of Array
* @method
* @memberof Popper.Utils
* @argument {Array} arr
* @argument prop
* @argument value
* @returns index or -1
*/
function find(arr, check) {
// use native find if supported
if (Array.prototype.find) {
return arr.find(check);
}
// use `filter` to obtain the same behavior of `find`
return arr.filter(check)[0];
}
/**
* Return the index of the matching object
* @method
* @memberof Popper.Utils
* @argument {Array} arr
* @argument prop
* @argument value
* @returns index or -1
*/
function findIndex(arr, prop, value) {
// use native findIndex if supported
if (Array.prototype.findIndex) {
return arr.findIndex(function (cur) {
return cur[prop] === value;
});
}
// use `find` + `indexOf` if `findIndex` isn't supported
var match = find(arr, function (obj) {
return obj[prop] === value;
});
return arr.indexOf(match);
}
/**
* Loop trough the list of modifiers and run them in order,
* each of them will then edit the data object.
* @method
* @memberof Popper.Utils
* @param {dataObject} data
* @param {Array} modifiers
* @param {String} ends - Optional modifier name used as stopper
* @returns {dataObject}
*/
function runModifiers(modifiers, data, ends) {
var modifiersToRun = ends === undefined ? modifiers : modifiers.slice(0, findIndex(modifiers, 'name', ends));
modifiersToRun.forEach(function (modifier) {
if (modifier['function']) {
// eslint-disable-line dot-notation
console.warn('`modifier.function` is deprecated, use `modifier.fn`!');
}
var fn = modifier['function'] || modifier.fn; // eslint-disable-line dot-notation
if (modifier.enabled && isFunction(fn)) {
// Add properties to offsets to make them a complete clientRect object
// we do this before each modifier to make sure the previous one doesn't
// mess with these values
data.offsets.popper = getClientRect(data.offsets.popper);
data.offsets.reference = getClientRect(data.offsets.reference);
data = fn(data, modifier);
}
});
return data;
}
/**
* Updates the position of the popper, computing the new offsets and applying
* the new style.
* Prefer `scheduleUpdate` over `update` because of performance reasons.
* @method
* @memberof Popper
*/
function update() {
// if popper is destroyed, don't perform any further update
if (this.state.isDestroyed) {
return;
}
var data = {
instance: this,
styles: {},
arrowStyles: {},
attributes: {},
flipped: false,
offsets: {}
};
// compute reference element offsets
data.offsets.reference = getReferenceOffsets(this.state, this.popper, this.reference);
// compute auto placement, store placement inside the data object,
// modifiers will be able to edit `placement` if needed
// and refer to originalPlacement to know the original value
data.placement = computeAutoPlacement(this.options.placement, data.offsets.reference, this.popper, this.reference, this.options.modifiers.flip.boundariesElement, this.options.modifiers.flip.padding);
// store the computed placement inside `originalPlacement`
data.originalPlacement = data.placement;
// compute the popper offsets
data.offsets.popper = getPopperOffsets(this.popper, data.offsets.reference, data.placement);
data.offsets.popper.position = 'absolute';
// run the modifiers
data = runModifiers(this.modifiers, data);
// the first `update` will call `onCreate` callback
// the other ones will call `onUpdate` callback
if (!this.state.isCreated) {
this.state.isCreated = true;
this.options.onCreate(data);
} else {
this.options.onUpdate(data);
}
}
/**
* Helper used to know if the given modifier is enabled.
* @method
* @memberof Popper.Utils
* @returns {Boolean}
*/
function isModifierEnabled(modifiers, modifierName) {
return modifiers.some(function (_ref) {
var name = _ref.name,
enabled = _ref.enabled;
return enabled && name === modifierName;
});
}
/**
* Get the prefixed supported property name
* @method
* @memberof Popper.Utils
* @argument {String} property (camelCase)
* @returns {String} prefixed property (camelCase or PascalCase, depending on the vendor prefix)
*/
function getSupportedPropertyName(property) {
var prefixes = [false, 'ms', 'Webkit', 'Moz', 'O'];
var upperProp = property.charAt(0).toUpperCase() + property.slice(1);
for (var i = 0; i < prefixes.length - 1; i++) {
var prefix = prefixes[i];
var toCheck = prefix ? '' + prefix + upperProp : property;
if (typeof window.document.body.style[toCheck] !== 'undefined') {
return toCheck;
}
}
return null;
}
/**
* Destroy the popper
* @method
* @memberof Popper
*/
function destroy() {
this.state.isDestroyed = true;
// touch DOM only if `applyStyle` modifier is enabled
if (isModifierEnabled(this.modifiers, 'applyStyle')) {
this.popper.removeAttribute('x-placement');
this.popper.style.left = '';
this.popper.style.position = '';
this.popper.style.top = '';
this.popper.style[getSupportedPropertyName('transform')] = '';
}
this.disableEventListeners();
// remove the popper if user explicity asked for the deletion on destroy
// do not use `remove` because IE11 doesn't support it
if (this.options.removeOnDestroy) {
this.popper.parentNode.removeChild(this.popper);
}
return this;
}
/**
* Get the window associated with the element
* @argument {Element} element
* @returns {Window}
*/
function getWindow(element) {
var ownerDocument = element.ownerDocument;
return ownerDocument ? ownerDocument.defaultView : window;
}
function attachToScrollParents(scrollParent, event, callback, scrollParents) {
var isBody = scrollParent.nodeName === 'BODY';
var target = isBody ? scrollParent.ownerDocument.defaultView : scrollParent;
target.addEventListener(event, callback, {
passive: true
});
if (!isBody) {
attachToScrollParents(getScrollParent(target.parentNode), event, callback, scrollParents);
}
scrollParents.push(target);
}
/**
* Setup needed event listeners used to update the popper position
* @method
* @memberof Popper.Utils
* @private
*/
function setupEventListeners(reference, options, state, updateBound) {
// Resize event listener on window
state.updateBound = updateBound;
getWindow(reference).addEventListener('resize', state.updateBound, {
passive: true
});
// Scroll event listener on scroll parents
var scrollElement = getScrollParent(reference);
attachToScrollParents(scrollElement, 'scroll', state.updateBound, state.scrollParents);
state.scrollElement = scrollElement;
state.eventsEnabled = true;
return state;
}
/**
* It will add resize/scroll events and start recalculating
* position of the popper element when they are triggered.
* @method
* @memberof Popper
*/
function enableEventListeners() {
if (!this.state.eventsEnabled) {
this.state = setupEventListeners(this.reference, this.options, this.state, this.scheduleUpdate);
}
}
/**
* Remove event listeners used to update the popper position
* @method
* @memberof Popper.Utils
* @private
*/
function removeEventListeners(reference, state) {
// Remove resize event listener on window
getWindow(reference).removeEventListener('resize', state.updateBound);
// Remove scroll event listener on scroll parents
state.scrollParents.forEach(function (target) {
target.removeEventListener('scroll', state.updateBound);
});
// Reset state
state.updateBound = null;
state.scrollParents = [];
state.scrollElement = null;
state.eventsEnabled = false;
return state;
}
/**
* It will remove resize/scroll events and won't recalculate popper position
* when they are triggered. It also won't trigger onUpdate callback anymore,
* unless you call `update` method manually.
* @method
* @memberof Popper
*/
function disableEventListeners() {
if (this.state.eventsEnabled) {
window.cancelAnimationFrame(this.scheduleUpdate);
this.state = removeEventListeners(this.reference, this.state);
}
}
/**
* Tells if a given input is a number
* @method
* @memberof Popper.Utils
* @param {*} input to check
* @return {Boolean}
*/
function isNumeric(n) {
return n !== '' && !isNaN(parseFloat(n)) && isFinite(n);
}
/**
* Set the style to the given popper
* @method
* @memberof Popper.Utils
* @argument {Element} element - Element to apply the style to
* @argument {Object} styles
* Object with a list of properties and values which will be applied to the element
*/
function setStyles(element, styles) {
Object.keys(styles).forEach(function (prop) {
var unit = '';
// add unit if the value is numeric and is one of the following
if (['width', 'height', 'top', 'right', 'bottom', 'left'].indexOf(prop) !== -1 && isNumeric(styles[prop])) {
unit = 'px';
}
element.style[prop] = styles[prop] + unit;
});
}
/**
* Set the attributes to the given popper
* @method
* @memberof Popper.Utils
* @argument {Element} element - Element to apply the attributes to
* @argument {Object} styles
* Object with a list of properties and values which will be applied to the element
*/
function setAttributes(element, attributes) {
Object.keys(attributes).forEach(function (prop) {
var value = attributes[prop];
if (value !== false) {
element.setAttribute(prop, attributes[prop]);
} else {
element.removeAttribute(prop);
}
});
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by `update` method
* @argument {Object} data.styles - List of style properties - values to apply to popper element
* @argument {Object} data.attributes - List of attribute properties - values to apply to popper element
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The same data object
*/
function applyStyle(data) {
// any property present in `data.styles` will be applied to the popper,
// in this way we can make the 3rd party modifiers add custom styles to it
// Be aware, modifiers could override the properties defined in the previous
// lines of this modifier!
setStyles(data.instance.popper, data.styles);
// any property present in `data.attributes` will be applied to the popper,
// they will be set as HTML attributes of the element
setAttributes(data.instance.popper, data.attributes);
// if arrowElement is defined and arrowStyles has some properties
if (data.arrowElement && Object.keys(data.arrowStyles).length) {
setStyles(data.arrowElement, data.arrowStyles);
}
return data;
}
/**
* Set the x-placement attribute before everything else because it could be used
* to add margins to the popper margins needs to be calculated to get the
* correct popper offsets.
* @method
* @memberof Popper.modifiers
* @param {HTMLElement} reference - The reference element used to position the popper
* @param {HTMLElement} popper - The HTML element used as popper.
* @param {Object} options - Popper.js options
*/
function applyStyleOnLoad(reference, popper, options, modifierOptions, state) {
// compute reference element offsets
var referenceOffsets = getReferenceOffsets(state, popper, reference);
// compute auto placement, store placement inside the data object,
// modifiers will be able to edit `placement` if needed
// and refer to originalPlacement to know the original value
var placement = computeAutoPlacement(options.placement, referenceOffsets, popper, reference, options.modifiers.flip.boundariesElement, options.modifiers.flip.padding);
popper.setAttribute('x-placement', placement);
// Apply `position` to popper before anything else because
// without the position applied we can't guarantee correct computations
setStyles(popper, {
position: 'absolute'
});
return options;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by `update` method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function computeStyle(data, options) {
var x = options.x,
y = options.y;
var popper = data.offsets.popper;
// Remove this legacy support in Popper.js v2
var legacyGpuAccelerationOption = find(data.instance.modifiers, function (modifier) {
return modifier.name === 'applyStyle';
}).gpuAcceleration;
if (legacyGpuAccelerationOption !== undefined) {
console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');
}
var gpuAcceleration = legacyGpuAccelerationOption !== undefined ? legacyGpuAccelerationOption : options.gpuAcceleration;
var offsetParent = getOffsetParent(data.instance.popper);
var offsetParentRect = getBoundingClientRect(offsetParent);
// Styles
var styles = {
position: popper.position
};
// floor sides to avoid blurry text
var offsets = {
left: Math.floor(popper.left),
top: Math.floor(popper.top),
bottom: Math.floor(popper.bottom),
right: Math.floor(popper.right)
};
var sideA = x === 'bottom' ? 'top' : 'bottom';
var sideB = y === 'right' ? 'left' : 'right';
// if gpuAcceleration is set to `true` and transform is supported,
// we use `translate3d` to apply the position to the popper we
// automatically use the supported prefixed version if needed
var prefixedProperty = getSupportedPropertyName('transform');
// now, let's make a step back and look at this code closely (wtf?)
// If the content of the popper grows once it's been positioned, it
// may happen that the popper gets misplaced because of the new content
// overflowing its reference element
// To avoid this problem, we provide two options (x and y), which allow
// the consumer to define the offset origin.
// If we position a popper on top of a reference element, we can set
// `x` to `top` to make the popper grow towards its top instead of
// its bottom.
var left = void 0,
top = void 0;
if (sideA === 'bottom') {
top = -offsetParentRect.height + offsets.bottom;
} else {
top = offsets.top;
}
if (sideB === 'right') {
left = -offsetParentRect.width + offsets.right;
} else {
left = offsets.left;
}
if (gpuAcceleration && prefixedProperty) {
styles[prefixedProperty] = 'translate3d(' + left + 'px, ' + top + 'px, 0)';
styles[sideA] = 0;
styles[sideB] = 0;
styles.willChange = 'transform';
} else {
// othwerise, we use the standard `top`, `left`, `bottom` and `right` properties
var invertTop = sideA === 'bottom' ? -1 : 1;
var invertLeft = sideB === 'right' ? -1 : 1;
styles[sideA] = top * invertTop;
styles[sideB] = left * invertLeft;
styles.willChange = sideA + ', ' + sideB;
}
// Attributes
var attributes = {
'x-placement': data.placement
};
// Update `data` attributes, styles and arrowStyles
data.attributes = _extends({}, attributes, data.attributes);
data.styles = _extends({}, styles, data.styles);
data.arrowStyles = _extends({}, data.offsets.arrow, data.arrowStyles);
return data;
}
/**
* Helper used to know if the given modifier depends from another one.
* It checks if the needed modifier is listed and enabled.
* @method
* @memberof Popper.Utils
* @param {Array} modifiers - list of modifiers
* @param {String} requestingName - name of requesting modifier
* @param {String} requestedName - name of requested modifier
* @returns {Boolean}
*/
function isModifierRequired(modifiers, requestingName, requestedName) {
var requesting = find(modifiers, function (_ref) {
var name = _ref.name;
return name === requestingName;
});
var isRequired = !!requesting && modifiers.some(function (modifier) {
return modifier.name === requestedName && modifier.enabled && modifier.order < requesting.order;
});
if (!isRequired) {
var _requesting = '`' + requestingName + '`';
var requested = '`' + requestedName + '`';
console.warn(requested + ' modifier is required by ' + _requesting + ' modifier in order to work, be sure to include it before ' + _requesting + '!');
}
return isRequired;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function arrow(data, options) {
// arrow depends on keepTogether in order to work
if (!isModifierRequired(data.instance.modifiers, 'arrow', 'keepTogether')) {
return data;
}
var arrowElement = options.element;
// if arrowElement is a string, suppose it's a CSS selector
if (typeof arrowElement === 'string') {
arrowElement = data.instance.popper.querySelector(arrowElement);
// if arrowElement is not found, don't run the modifier
if (!arrowElement) {
return data;
}
} else {
// if the arrowElement isn't a query selector we must check that the
// provided DOM node is child of its popper node
if (!data.instance.popper.contains(arrowElement)) {
console.warn('WARNING: `arrow.element` must be child of its popper element!');
return data;
}
}
var placement = data.placement.split('-')[0];
var _data$offsets = data.offsets,
popper = _data$offsets.popper,
reference = _data$offsets.reference;
var isVertical = ['left', 'right'].indexOf(placement) !== -1;
var len = isVertical ? 'height' : 'width';
var sideCapitalized = isVertical ? 'Top' : 'Left';
var side = sideCapitalized.toLowerCase();
var altSide = isVertical ? 'left' : 'top';
var opSide = isVertical ? 'bottom' : 'right';
var arrowElementSize = getOuterSizes(arrowElement)[len];
//
// extends keepTogether behavior making sure the popper and its
// reference have enough pixels in conjuction
//
// top/left side
if (reference[opSide] - arrowElementSize < popper[side]) {
data.offsets.popper[side] -= popper[side] - (reference[opSide] - arrowElementSize);
}
// bottom/right side
if (reference[side] + arrowElementSize > popper[opSide]) {
data.offsets.popper[side] += reference[side] + arrowElementSize - popper[opSide];
}
// compute center of the popper
var center = reference[side] + reference[len] / 2 - arrowElementSize / 2;
// Compute the sideValue using the updated popper offsets
// take popper margin in account because we don't have this info available
var popperMarginSide = getStyleComputedProperty(data.instance.popper, 'margin' + sideCapitalized).replace('px', '');
var sideValue = center - getClientRect(data.offsets.popper)[side] - popperMarginSide;
// prevent arrowElement from being placed not contiguously to its popper
sideValue = Math.max(Math.min(popper[len] - arrowElementSize, sideValue), 0);
data.arrowElement = arrowElement;
data.offsets.arrow = {};
data.offsets.arrow[side] = Math.round(sideValue);
data.offsets.arrow[altSide] = ''; // make sure to unset any eventual altSide value from the DOM node
return data;
}
/**
* Get the opposite placement variation of the given one
* @method
* @memberof Popper.Utils
* @argument {String} placement variation
* @returns {String} flipped placement variation
*/
function getOppositeVariation(variation) {
if (variation === 'end') {
return 'start';
} else if (variation === 'start') {
return 'end';
}
return variation;
}
/**
* List of accepted placements to use as values of the `placement` option.
* Valid placements are:
* - `auto`
* - `top`
* - `right`
* - `bottom`
* - `left`
*
* Each placement can have a variation from this list:
* - `-start`
* - `-end`
*
* Variations are interpreted easily if you think of them as the left to right
* written languages. Horizontally (`top` and `bottom`), `start` is left and `end`
* is right.
* Vertically (`left` and `right`), `start` is top and `end` is bottom.
*
* Some valid examples are:
* - `top-end` (on top of reference, right aligned)
* - `right-start` (on right of reference, top aligned)
* - `bottom` (on bottom, centered)
* - `auto-right` (on the side with more space available, alignment depends by placement)
*
* @static
* @type {Array}
* @enum {String}
* @readonly
* @method placements
* @memberof Popper
*/
var placements = ['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start'];
// Get rid of `auto` `auto-start` and `auto-end`
var validPlacements = placements.slice(3);
/**
* Given an initial placement, returns all the subsequent placements
* clockwise (or counter-clockwise).
*
* @method
* @memberof Popper.Utils
* @argument {String} placement - A valid placement (it accepts variations)
* @argument {Boolean} counter - Set to true to walk the placements counterclockwise
* @returns {Array} placements including their variations
*/
function clockwise(placement) {
var counter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var index = validPlacements.indexOf(placement);
var arr = validPlacements.slice(index + 1).concat(validPlacements.slice(0, index));
return counter ? arr.reverse() : arr;
}
var BEHAVIORS = {
FLIP: 'flip',
CLOCKWISE: 'clockwise',
COUNTERCLOCKWISE: 'counterclockwise'
};
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function flip(data, options) {
// if `inner` modifier is enabled, we can't use the `flip` modifier
if (isModifierEnabled(data.instance.modifiers, 'inner')) {
return data;
}
if (data.flipped && data.placement === data.originalPlacement) {
// seems like flip is trying to loop, probably there's not enough space on any of the flippable sides
return data;
}
var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, options.boundariesElement);
var placement = data.placement.split('-')[0];
var placementOpposite = getOppositePlacement(placement);
var variation = data.placement.split('-')[1] || '';
var flipOrder = [];
switch (options.behavior) {
case BEHAVIORS.FLIP:
flipOrder = [placement, placementOpposite];
break;
case BEHAVIORS.CLOCKWISE:
flipOrder = clockwise(placement);
break;
case BEHAVIORS.COUNTERCLOCKWISE:
flipOrder = clockwise(placement, true);
break;
default:
flipOrder = options.behavior;
}
flipOrder.forEach(function (step, index) {
if (placement !== step || flipOrder.length === index + 1) {
return data;
}
placement = data.placement.split('-')[0];
placementOpposite = getOppositePlacement(placement);
var popperOffsets = data.offsets.popper;
var refOffsets = data.offsets.reference;
// using floor because the reference offsets may contain decimals we are not going to consider here
var floor = Math.floor;
var overlapsRef = placement === 'left' && floor(popperOffsets.right) > floor(refOffsets.left) || placement === 'right' && floor(popperOffsets.left) < floor(refOffsets.right) || placement === 'top' && floor(popperOffsets.bottom) > floor(refOffsets.top) || placement === 'bottom' && floor(popperOffsets.top) < floor(refOffsets.bottom);
var overflowsLeft = floor(popperOffsets.left) < floor(boundaries.left);
var overflowsRight = floor(popperOffsets.right) > floor(boundaries.right);
var overflowsTop = floor(popperOffsets.top) < floor(boundaries.top);
var overflowsBottom = floor(popperOffsets.bottom) > floor(boundaries.bottom);
var overflowsBoundaries = placement === 'left' && overflowsLeft || placement === 'right' && overflowsRight || placement === 'top' && overflowsTop || placement === 'bottom' && overflowsBottom;
// flip the variation if required
var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
var flippedVariation = !!options.flipVariations && (isVertical && variation === 'start' && overflowsLeft || isVertical && variation === 'end' && overflowsRight || !isVertical && variation === 'start' && overflowsTop || !isVertical && variation === 'end' && overflowsBottom);
if (overlapsRef || overflowsBoundaries || flippedVariation) {
// this boolean to detect any flip loop
data.flipped = true;
if (overlapsRef || overflowsBoundaries) {
placement = flipOrder[index + 1];
}
if (flippedVariation) {
variation = getOppositeVariation(variation);
}
data.placement = placement + (variation ? '-' + variation : '');
// this object contains `position`, we want to preserve it along with
// any additional property we may add in the future
data.offsets.popper = _extends({}, data.offsets.popper, getPopperOffsets(data.instance.popper, data.offsets.reference, data.placement));
data = runModifiers(data.instance.modifiers, data, 'flip');
}
});
return data;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function keepTogether(data) {
var _data$offsets = data.offsets,
popper = _data$offsets.popper,
reference = _data$offsets.reference;
var placement = data.placement.split('-')[0];
var floor = Math.floor;
var isVertical = ['top', 'bottom'].indexOf(placement) !== -1;
var side = isVertical ? 'right' : 'bottom';
var opSide = isVertical ? 'left' : 'top';
var measurement = isVertical ? 'width' : 'height';
if (popper[side] < floor(reference[opSide])) {
data.offsets.popper[opSide] = floor(reference[opSide]) - popper[measurement];
}
if (popper[opSide] > floor(reference[side])) {
data.offsets.popper[opSide] = floor(reference[side]);
}
return data;
}
/**
* Converts a string containing value + unit into a px value number
* @function
* @memberof {modifiers~offset}
* @private
* @argument {String} str - Value + unit string
* @argument {String} measurement - `height` or `width`
* @argument {Object} popperOffsets
* @argument {Object} referenceOffsets
* @returns {Number|String}
* Value in pixels, or original string if no values were extracted
*/
function toValue(str, measurement, popperOffsets, referenceOffsets) {
// separate value from unit
var split = str.match(/((?:\-|\+)?\d*\.?\d*)(.*)/);
var value = +split[1];
var unit = split[2];
// If it's not a number it's an operator, I guess
if (!value) {
return str;
}
if (unit.indexOf('%') === 0) {
var element = void 0;
switch (unit) {
case '%p':
element = popperOffsets;
break;
case '%':
case '%r':
default:
element = referenceOffsets;
}
var rect = getClientRect(element);
return rect[measurement] / 100 * value;
} else if (unit === 'vh' || unit === 'vw') {
// if is a vh or vw, we calculate the size based on the viewport
var size = void 0;
if (unit === 'vh') {
size = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
} else {
size = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
}
return size / 100 * value;
} else {
// if is an explicit pixel unit, we get rid of the unit and keep the value
// if is an implicit unit, it's px, and we return just the value
return value;
}
}
/**
* Parse an `offset` string to extrapolate `x` and `y` numeric offsets.
* @function
* @memberof {modifiers~offset}
* @private
* @argument {String} offset
* @argument {Object} popperOffsets
* @argument {Object} referenceOffsets
* @argument {String} basePlacement
* @returns {Array} a two cells array with x and y offsets in numbers
*/
function parseOffset(offset, popperOffsets, referenceOffsets, basePlacement) {
var offsets = [0, 0];
// Use height if placement is left or right and index is 0 otherwise use width
// in this way the first offset will use an axis and the second one
// will use the other one
var useHeight = ['right', 'left'].indexOf(basePlacement) !== -1;
// Split the offset string to obtain a list of values and operands
// The regex addresses values with the plus or minus sign in front (+10, -20, etc)
var fragments = offset.split(/(\+|\-)/).map(function (frag) {
return frag.trim();
});
// Detect if the offset string contains a pair of values or a single one
// they could be separated by comma or space
var divider = fragments.indexOf(find(fragments, function (frag) {
return frag.search(/,|\s/) !== -1;
}));
if (fragments[divider] && fragments[divider].indexOf(',') === -1) {
console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');
}
// If divider is found, we divide the list of values and operands to divide
// them by ofset X and Y.
var splitRegex = /\s*,\s*|\s+/;
var ops = divider !== -1 ? [fragments.slice(0, divider).concat([fragments[divider].split(splitRegex)[0]]), [fragments[divider].split(splitRegex)[1]].concat(fragments.slice(divider + 1))] : [fragments];
// Convert the values with units to absolute pixels to allow our computations
ops = ops.map(function (op, index) {
// Most of the units rely on the orientation of the popper
var measurement = (index === 1 ? !useHeight : useHeight) ? 'height' : 'width';
var mergeWithPrevious = false;
return op
// This aggregates any `+` or `-` sign that aren't considered operators
// e.g.: 10 + +5 => [10, +, +5]
.reduce(function (a, b) {
if (a[a.length - 1] === '' && ['+', '-'].indexOf(b) !== -1) {
a[a.length - 1] = b;
mergeWithPrevious = true;
return a;
} else if (mergeWithPrevious) {
a[a.length - 1] += b;
mergeWithPrevious = false;
return a;
} else {
return a.concat(b);
}
}, [])
// Here we convert the string values into number values (in px)
.map(function (str) {
return toValue(str, measurement, popperOffsets, referenceOffsets);
});
});
// Loop trough the offsets arrays and execute the operations
ops.forEach(function (op, index) {
op.forEach(function (frag, index2) {
if (isNumeric(frag)) {
offsets[index] += frag * (op[index2 - 1] === '-' ? -1 : 1);
}
});
});
return offsets;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @argument {Number|String} options.offset=0
* The offset value as described in the modifier description
* @returns {Object} The data object, properly modified
*/
function offset(data, _ref) {
var offset = _ref.offset;
var placement = data.placement,
_data$offsets = data.offsets,
popper = _data$offsets.popper,
reference = _data$offsets.reference;
var basePlacement = placement.split('-')[0];
var offsets = void 0;
if (isNumeric(+offset)) {
offsets = [+offset, 0];
} else {
offsets = parseOffset(offset, popper, reference, basePlacement);
}
if (basePlacement === 'left') {
popper.top += offsets[0];
popper.left -= offsets[1];
} else if (basePlacement === 'right') {
popper.top += offsets[0];
popper.left += offsets[1];
} else if (basePlacement === 'top') {
popper.left += offsets[0];
popper.top -= offsets[1];
} else if (basePlacement === 'bottom') {
popper.left += offsets[0];
popper.top += offsets[1];
}
data.popper = popper;
return data;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by `update` method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function preventOverflow(data, options) {
var boundariesElement = options.boundariesElement || getOffsetParent(data.instance.popper);
// If offsetParent is the reference element, we really want to
// go one step up and use the next offsetParent as reference to
// avoid to make this modifier completely useless and look like broken
if (data.instance.reference === boundariesElement) {
boundariesElement = getOffsetParent(boundariesElement);
}
var boundaries = getBoundaries(data.instance.popper, data.instance.reference, options.padding, boundariesElement);
options.boundaries = boundaries;
var order = options.priority;
var popper = data.offsets.popper;
var check = {
primary: function primary(placement) {
var value = popper[placement];
if (popper[placement] < boundaries[placement] && !options.escapeWithReference) {
value = Math.max(popper[placement], boundaries[placement]);
}
return defineProperty({}, placement, value);
},
secondary: function secondary(placement) {
var mainSide = placement === 'right' ? 'left' : 'top';
var value = popper[mainSide];
if (popper[placement] > boundaries[placement] && !options.escapeWithReference) {
value = Math.min(popper[mainSide], boundaries[placement] - (placement === 'right' ? popper.width : popper.height));
}
return defineProperty({}, mainSide, value);
}
};
order.forEach(function (placement) {
var side = ['left', 'top'].indexOf(placement) !== -1 ? 'primary' : 'secondary';
popper = _extends({}, popper, check[side](placement));
});
data.offsets.popper = popper;
return data;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by `update` method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function shift(data) {
var placement = data.placement;
var basePlacement = placement.split('-')[0];
var shiftvariation = placement.split('-')[1];
// if shift shiftvariation is specified, run the modifier
if (shiftvariation) {
var _data$offsets = data.offsets,
reference = _data$offsets.reference,
popper = _data$offsets.popper;
var isVertical = ['bottom', 'top'].indexOf(basePlacement) !== -1;
var side = isVertical ? 'left' : 'top';
var measurement = isVertical ? 'width' : 'height';
var shiftOffsets = {
start: defineProperty({}, side, reference[side]),
end: defineProperty({}, side, reference[side] + reference[measurement] - popper[measurement])
};
data.offsets.popper = _extends({}, popper, shiftOffsets[shiftvariation]);
}
return data;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by update method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function hide(data) {
if (!isModifierRequired(data.instance.modifiers, 'hide', 'preventOverflow')) {
return data;
}
var refRect = data.offsets.reference;
var bound = find(data.instance.modifiers, function (modifier) {
return modifier.name === 'preventOverflow';
}).boundaries;
if (refRect.bottom < bound.top || refRect.left > bound.right || refRect.top > bound.bottom || refRect.right < bound.left) {
// Avoid unnecessary DOM access if visibility hasn't changed
if (data.hide === true) {
return data;
}
data.hide = true;
data.attributes['x-out-of-boundaries'] = '';
} else {
// Avoid unnecessary DOM access if visibility hasn't changed
if (data.hide === false) {
return data;
}
data.hide = false;
data.attributes['x-out-of-boundaries'] = false;
}
return data;
}
/**
* @function
* @memberof Modifiers
* @argument {Object} data - The data object generated by `update` method
* @argument {Object} options - Modifiers configuration and options
* @returns {Object} The data object, properly modified
*/
function inner(data) {
var placement = data.placement;
var basePlacement = placement.split('-')[0];
var _data$offsets = data.offsets,
popper = _data$offsets.popper,
reference = _data$offsets.reference;
var isHoriz = ['left', 'right'].indexOf(basePlacement) !== -1;
var subtractLength = ['top', 'left'].indexOf(basePlacement) === -1;
popper[isHoriz ? 'left' : 'top'] = reference[basePlacement] - (subtractLength ? popper[isHoriz ? 'width' : 'height'] : 0);
data.placement = getOppositePlacement(placement);
data.offsets.popper = getClientRect(popper);
return data;
}
/**
* Modifier function, each modifier can have a function of this type assigned
* to its `fn` property.
* These functions will be called on each update, this means that you must
* make sure they are performant enough to avoid performance bottlenecks.
*
* @function ModifierFn
* @argument {dataObject} data - The data object generated by `update` method
* @argument {Object} options - Modifiers configuration and options
* @returns {dataObject} The data object, properly modified
*/
/**
* Modifiers are plugins used to alter the behavior of your poppers.
* Popper.js uses a set of 9 modifiers to provide all the basic functionalities
* needed by the library.
*
* Usually you don't want to override the `order`, `fn` and `onLoad` props.
* All the other properties are configurations that could be tweaked.
* @namespace modifiers
*/
var modifiers = {
/**
* Modifier used to shift the popper on the start or end of its reference
* element.
* It will read the variation of the `placement` property.
* It can be one either `-end` or `-start`.
* @memberof modifiers
* @inner
*/
shift: {
/** @prop {number} order=100 - Index used to define the order of execution */
order: 100,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: shift
},
/**
* The `offset` modifier can shift your popper on both its axis.
*
* It accepts the following units:
* - `px` or unitless, interpreted as pixels
* - `%` or `%r`, percentage relative to the length of the reference element
* - `%p`, percentage relative to the length of the popper element
* - `vw`, CSS viewport width unit
* - `vh`, CSS viewport height unit
*
* For length is intended the main axis relative to the placement of the popper.
* This means that if the placement is `top` or `bottom`, the length will be the
* `width`. In case of `left` or `right`, it will be the height.
*
* You can provide a single value (as `Number` or `String`), or a pair of values
* as `String` divided by a comma or one (or more) white spaces.
* The latter is a deprecated method because it leads to confusion and will be
* removed in v2.
* Additionally, it accepts additions and subtractions between different units.
* Note that multiplications and divisions aren't supported.
*
* Valid examples are:
* ```
* 10
* '10%'
* '10, 10'
* '10%, 10'
* '10 + 10%'
* '10 - 5vh + 3%'
* '-10px + 5vh, 5px - 6%'
* ```
* > **NB**: If you desire to apply offsets to your poppers in a way that may make them overlap
* > with their reference element, unfortunately, you will have to disable the `flip` modifier.
* > More on this [reading this issue](https://github.com/FezVrasta/popper.js/issues/373)
*
* @memberof modifiers
* @inner
*/
offset: {
/** @prop {number} order=200 - Index used to define the order of execution */
order: 200,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: offset,
/** @prop {Number|String} offset=0
* The offset value as described in the modifier description
*/
offset: 0
},
/**
* Modifier used to prevent the popper from being positioned outside the boundary.
*
* An scenario exists where the reference itself is not within the boundaries.
* We can say it has "escaped the boundaries" — or just "escaped".
* In this case we need to decide whether the popper should either:
*
* - detach from the reference and remain "trapped" in the boundaries, or
* - if it should ignore the boundary and "escape with its reference"
*
* When `escapeWithReference` is set to`true` and reference is completely
* outside its boundaries, the popper will overflow (or completely leave)
* the boundaries in order to remain attached to the edge of the reference.
*
* @memberof modifiers
* @inner
*/
preventOverflow: {
/** @prop {number} order=300 - Index used to define the order of execution */
order: 300,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: preventOverflow,
/**
* @prop {Array} [priority=['left','right','top','bottom']]
* Popper will try to prevent overflow following these priorities by default,
* then, it could overflow on the left and on top of the `boundariesElement`
*/
priority: ['left', 'right', 'top', 'bottom'],
/**
* @prop {number} padding=5
* Amount of pixel used to define a minimum distance between the boundaries
* and the popper this makes sure the popper has always a little padding
* between the edges of its container
*/
padding: 5,
/**
* @prop {String|HTMLElement} boundariesElement='scrollParent'
* Boundaries used by the modifier, can be `scrollParent`, `window`,
* `viewport` or any DOM element.
*/
boundariesElement: 'scrollParent'
},
/**
* Modifier used to make sure the reference and its popper stay near eachothers
* without leaving any gap between the two. Expecially useful when the arrow is
* enabled and you want to assure it to point to its reference element.
* It cares only about the first axis, you can still have poppers with margin
* between the popper and its reference element.
* @memberof modifiers
* @inner
*/
keepTogether: {
/** @prop {number} order=400 - Index used to define the order of execution */
order: 400,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: keepTogether
},
/**
* This modifier is used to move the `arrowElement` of the popper to make
* sure it is positioned between the reference element and its popper element.
* It will read the outer size of the `arrowElement` node to detect how many
* pixels of conjuction are needed.
*
* It has no effect if no `arrowElement` is provided.
* @memberof modifiers
* @inner
*/
arrow: {
/** @prop {number} order=500 - Index used to define the order of execution */
order: 500,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: arrow,
/** @prop {String|HTMLElement} element='[x-arrow]' - Selector or node used as arrow */
element: '[x-arrow]'
},
/**
* Modifier used to flip the popper's placement when it starts to overlap its
* reference element.
*
* Requires the `preventOverflow` modifier before it in order to work.
*
* **NOTE:** this modifier will interrupt the current update cycle and will
* restart it if it detects the need to flip the placement.
* @memberof modifiers
* @inner
*/
flip: {
/** @prop {number} order=600 - Index used to define the order of execution */
order: 600,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: flip,
/**
* @prop {String|Array} behavior='flip'
* The behavior used to change the popper's placement. It can be one of
* `flip`, `clockwise`, `counterclockwise` or an array with a list of valid
* placements (with optional variations).
*/
behavior: 'flip',
/**
* @prop {number} padding=5
* The popper will flip if it hits the edges of the `boundariesElement`
*/
padding: 5,
/**
* @prop {String|HTMLElement} boundariesElement='viewport'
* The element which will define the boundaries of the popper position,
* the popper will never be placed outside of the defined boundaries
* (except if keepTogether is enabled)
*/
boundariesElement: 'viewport'
},
/**
* Modifier used to make the popper flow toward the inner of the reference element.
* By default, when this modifier is disabled, the popper will be placed outside
* the reference element.
* @memberof modifiers
* @inner
*/
inner: {
/** @prop {number} order=700 - Index used to define the order of execution */
order: 700,
/** @prop {Boolean} enabled=false - Whether the modifier is enabled or not */
enabled: false,
/** @prop {ModifierFn} */
fn: inner
},
/**
* Modifier used to hide the popper when its reference element is outside of the
* popper boundaries. It will set a `x-out-of-boundaries` attribute which can
* be used to hide with a CSS selector the popper when its reference is
* out of boundaries.
*
* Requires the `preventOverflow` modifier before it in order to work.
* @memberof modifiers
* @inner
*/
hide: {
/** @prop {number} order=800 - Index used to define the order of execution */
order: 800,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: hide
},
/**
* Computes the style that will be applied to the popper element to gets
* properly positioned.
*
* Note that this modifier will not touch the DOM, it just prepares the styles
* so that `applyStyle` modifier can apply it. This separation is useful
* in case you need to replace `applyStyle` with a custom implementation.
*
* This modifier has `850` as `order` value to maintain backward compatibility
* with previous versions of Popper.js. Expect the modifiers ordering method
* to change in future major versions of the library.
*
* @memberof modifiers
* @inner
*/
computeStyle: {
/** @prop {number} order=850 - Index used to define the order of execution */
order: 850,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: computeStyle,
/**
* @prop {Boolean} gpuAcceleration=true
* If true, it uses the CSS 3d transformation to position the popper.
* Otherwise, it will use the `top` and `left` properties.
*/
gpuAcceleration: true,
/**
* @prop {string} [x='bottom']
* Where to anchor the X axis (`bottom` or `top`). AKA X offset origin.
* Change this if your popper should grow in a direction different from `bottom`
*/
x: 'bottom',
/**
* @prop {string} [x='left']
* Where to anchor the Y axis (`left` or `right`). AKA Y offset origin.
* Change this if your popper should grow in a direction different from `right`
*/
y: 'right'
},
/**
* Applies the computed styles to the popper element.
*
* All the DOM manipulations are limited to this modifier. This is useful in case
* you want to integrate Popper.js inside a framework or view library and you
* want to delegate all the DOM manipulations to it.
*
* Note that if you disable this modifier, you must make sure the popper element
* has its position set to `absolute` before Popper.js can do its work!
*
* Just disable this modifier and define you own to achieve the desired effect.
*
* @memberof modifiers
* @inner
*/
applyStyle: {
/** @prop {number} order=900 - Index used to define the order of execution */
order: 900,
/** @prop {Boolean} enabled=true - Whether the modifier is enabled or not */
enabled: true,
/** @prop {ModifierFn} */
fn: applyStyle,
/** @prop {Function} */
onLoad: applyStyleOnLoad,
/**
* @deprecated since version 1.10.0, the property moved to `computeStyle` modifier
* @prop {Boolean} gpuAcceleration=true
* If true, it uses the CSS 3d transformation to position the popper.
* Otherwise, it will use the `top` and `left` properties.
*/
gpuAcceleration: undefined
}
};
/**
* The `dataObject` is an object containing all the informations used by Popper.js
* this object get passed to modifiers and to the `onCreate` and `onUpdate` callbacks.
* @name dataObject
* @property {Object} data.instance The Popper.js instance
* @property {String} data.placement Placement applied to popper
* @property {String} data.originalPlacement Placement originally defined on init
* @property {Boolean} data.flipped True if popper has been flipped by flip modifier
* @property {Boolean} data.hide True if the reference element is out of boundaries, useful to know when to hide the popper.
* @property {HTMLElement} data.arrowElement Node used as arrow by arrow modifier
* @property {Object} data.styles Any CSS property defined here will be applied to the popper, it expects the JavaScript nomenclature (eg. `marginBottom`)
* @property {Object} data.arrowStyles Any CSS property defined here will be applied to the popper arrow, it expects the JavaScript nomenclature (eg. `marginBottom`)
* @property {Object} data.boundaries Offsets of the popper boundaries
* @property {Object} data.offsets The measurements of popper, reference and arrow elements.
* @property {Object} data.offsets.popper `top`, `left`, `width`, `height` values
* @property {Object} data.offsets.reference `top`, `left`, `width`, `height` values
* @property {Object} data.offsets.arrow] `top` and `left` offsets, only one of them will be different from 0
*/
/**
* Default options provided to Popper.js constructor.
* These can be overriden using the `options` argument of Popper.js.
* To override an option, simply pass as 3rd argument an object with the same
* structure of this object, example:
* ```
* new Popper(ref, pop, {
* modifiers: {
* preventOverflow: { enabled: false }
* }
* })
* ```
* @type {Object}
* @static
* @memberof Popper
*/
var Defaults = {
/**
* Popper's placement
* @prop {Popper.placements} placement='bottom'
*/
placement: 'bottom',
/**
* Whether events (resize, scroll) are initially enabled
* @prop {Boolean} eventsEnabled=true
*/
eventsEnabled: true,
/**
* Set to true if you want to automatically remove the popper when
* you call the `destroy` method.
* @prop {Boolean} removeOnDestroy=false
*/
removeOnDestroy: false,
/**
* Callback called when the popper is created.
* By default, is set to no-op.
* Access Popper.js instance with `data.instance`.
* @prop {onCreate}
*/
onCreate: function onCreate() {},
/**
* Callback called when the popper is updated, this callback is not called
* on the initialization/creation of the popper, but only on subsequent
* updates.
* By default, is set to no-op.
* Access Popper.js instance with `data.instance`.
* @prop {onUpdate}
*/
onUpdate: function onUpdate() {},
/**
* List of modifiers used to modify the offsets before they are applied to the popper.
* They provide most of the functionalities of Popper.js
* @prop {modifiers}
*/
modifiers: modifiers
};
/**
* @callback onCreate
* @param {dataObject} data
*/
/**
* @callback onUpdate
* @param {dataObject} data
*/
// Utils
// Methods
var Popper = function () {
/**
* Create a new Popper.js instance
* @class Popper
* @param {HTMLElement|referenceObject} reference - The reference element used to position the popper
* @param {HTMLElement} popper - The HTML element used as popper.
* @param {Object} options - Your custom options to override the ones defined in [Defaults](#defaults)
* @return {Object} instance - The generated Popper.js instance
*/
function Popper(reference, popper) {
var _this = this;
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
classCallCheck(this, Popper);
this.scheduleUpdate = function () {
return requestAnimationFrame(_this.update);
};
// make update() debounced, so that it only runs at most once-per-tick
this.update = debounce(this.update.bind(this));
// with {} we create a new object with the options inside it
this.options = _extends({}, Popper.Defaults, options);
// init state
this.state = {
isDestroyed: false,
isCreated: false,
scrollParents: []
};
// get reference and popper elements (allow jQuery wrappers)
this.reference = reference && reference.jquery ? reference[0] : reference;
this.popper = popper && popper.jquery ? popper[0] : popper;
// Deep merge modifiers options
this.options.modifiers = {};
Object.keys(_extends({}, Popper.Defaults.modifiers, options.modifiers)).forEach(function (name) {
_this.options.modifiers[name] = _extends({}, Popper.Defaults.modifiers[name] || {}, options.modifiers ? options.modifiers[name] : {});
});
// Refactoring modifiers' list (Object => Array)
this.modifiers = Object.keys(this.options.modifiers).map(function (name) {
return _extends({
name: name
}, _this.options.modifiers[name]);
})
// sort the modifiers by order
.sort(function (a, b) {
return a.order - b.order;
});
// modifiers have the ability to execute arbitrary code when Popper.js get inited
// such code is executed in the same order of its modifier
// they could add new properties to their options configuration
// BE AWARE: don't add options to `options.modifiers.name` but to `modifierOptions`!
this.modifiers.forEach(function (modifierOptions) {
if (modifierOptions.enabled && isFunction(modifierOptions.onLoad)) {
modifierOptions.onLoad(_this.reference, _this.popper, _this.options, modifierOptions, _this.state);
}
});
// fire the first update to position the popper in the right place
this.update();
var eventsEnabled = this.options.eventsEnabled;
if (eventsEnabled) {
// setup event listeners, they will take care of update the position in specific situations
this.enableEventListeners();
}
this.state.eventsEnabled = eventsEnabled;
}
// We can't use class properties because they don't get listed in the
// class prototype and break stuff like Sinon stubs
createClass(Popper, [{
key: 'update',
value: function update$$1() {
return update.call(this);
}
}, {
key: 'destroy',
value: function destroy$$1() {
return destroy.call(this);
}
}, {
key: 'enableEventListeners',
value: function enableEventListeners$$1() {
return enableEventListeners.call(this);
}
}, {
key: 'disableEventListeners',
value: function disableEventListeners$$1() {
return disableEventListeners.call(this);
}
/**
* Schedule an update, it will run on the next UI update available
* @method scheduleUpdate
* @memberof Popper
*/
/**
* Collection of utilities useful when writing custom modifiers.
* Starting from version 1.7, this method is available only if you
* include `popper-utils.js` before `popper.js`.
*
* **DEPRECATION**: This way to access PopperUtils is deprecated
* and will be removed in v2! Use the PopperUtils module directly instead.
* Due to the high instability of the methods contained in Utils, we can't
* guarantee them to follow semver. Use them at your own risk!
* @static
* @private
* @type {Object}
* @deprecated since version 1.8
* @member Utils
* @memberof Popper
*/
}]);
return Popper;
}();
/**
* The `referenceObject` is an object that provides an interface compatible with Popper.js
* and lets you use it as replacement of a real DOM node.
* You can use this method to position a popper relatively to a set of coordinates
* in case you don't have a DOM node to use as reference.
*
* ```
* new Popper(referenceObject, popperNode);
* ```
*
* NB: This feature isn't supported in Internet Explorer 10
* @name referenceObject
* @property {Function} data.getBoundingClientRect
* A function that returns a set of coordinates compatible with the native `getBoundingClientRect` method.
* @property {number} data.clientWidth
* An ES6 getter that will return the width of the virtual reference element.
* @property {number} data.clientHeight
* An ES6 getter that will return the height of the virtual reference element.
*/
Popper.Utils = (typeof window !== 'undefined' ? window : global).PopperUtils;
Popper.placements = placements;
Popper.Defaults = Defaults;
return Popper;
});
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__("DuR2")))
/***/ }),
/***/ "1+y7":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAeCAYAAAFIwk4JAAAAAXNSR0IArs4c6QAAA5ZJREFUSA2lVk1IVFEUPvelBpWEEEYRLQqkhVAg5KKsXGVQQgSRUGBQuBAi8y+KFkLRPCeiEKI2LdoUUVEUSUKFEfS3EIl+LCISRHERQVZCOafvvJn77nkzb8a/C2/ud77zd8+95903RHb43GMhUYLZCp4FmkxzPq8MldAOpQXri9kEjCW0kNYIE9FCPm4yOlFopWBmz3n6/IyYaoKwGS9DPg+A3BCQeX+6+RBCj2t9zFKh9mg7dZg+twce7QhXmKJHEsEpmb7qkIKLQoLpI3JacZMAV4quM9wAayuzNgg8swht68IKe4GX0ySNaYMQe3QU5V20snPM3kUTlDqMnT1sjYM53HbL6mVlF6J1HlUg82eXMcFPEWObjYNdPYVs3/BcCzkBORmF7OadlKL7AnOGoXfUaSot7zJaRmafJ5BpcUh10gIyJhXK04IkV+ezyc14icvoJ11Fxq3ogS942rAZ/dkBoo4+P4dD0DcRQ0NTqM/1GZSuI31+GOskERg1JnhKB3OOjD53I4Ulng2W6jiPkrzfiumlJrgJxGVL2rMKZJ97kbEuo5uArlSwzbg+o5DptsICdcstsbq0o0ePLYHl1YdYAFOHkicsdruq+9HQOBwaEeQA5gZrDHkPdveOyM7R5+sw2hcaZQMDbaexpYU1EkgXOdtJ5HJapOkwQkBWU7FWKtxAB82kklVGYWvNP7hGr0RDD3AEN7ST4GhGYVrNICpvFogxjBJ2peFMfxPcVcjUZF9thYxDnaFGdxwhq8AVLqYfaACmdrBLlSYOvsKr0ELt5kWc0nLxCRMswV/iWWcNZzUbLLLTJON8chP6XI+K7sUZz5L7RGVUSU3mr/aLnn43ry2QbBSONcEbLjeyPGtoITrlvA6ocAWO466SAxitMMFnwJ7INkLQIWxR/u3t5jrc/r05fkJ4tAJX65jVRSsk3MJxgym8LeLUSJZfX4RWUiOasIRuKZ2GVfgEvaFzvEyTAU7ybsz9OXya6KNjZkTrolsqGp9rcY5PtNEc8SDOuIr2msg3IzehRE9yObbpLRKXzzFZD5rqSJxvfEJrmWDpwBYrTjsb+oUT20ht5n0+28IJxSv9H+s10Op8QQLeUBKdrD9LsebTJ7Ru8seH8V7Jtzw6BqiUtlCzyd+pyn7mCa2Tz6eR9CTeze+gNqOqD1Y1k9nQTS6hEVo1E+N52vyhFjMqn5kurLZunsGmd2f6jVun9T+WFAYXIRdFDAAAAABJRU5ErkJggg=="
/***/ }),
/***/ "1Bek":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAgCAYAAAB3j6rJAAADtklEQVRYR82XWahWVRTHf/9GsbDIEFILRYoGxAejwadMg8CH0BIxirBySKIJJIVC0Eqh8kWQ1IQQBSGHIIuCoqAEI7E5J5CiHJAyUys1dcn/ss/1fMdzvu/cc1/ugu/lO2uv/dtrr2mLPiLqIxzUAomIy4HxwAPASGAE4P/+A/YBXwOfAe9JOtnkcBeARMQYYK+kgxHRD3gamAdcU2ODP4HlwKuS/q2h363SAhIRzwKvAx9ImhgRjwHv9MRg0v0VeFLSJ3XXdoNExEzgrdzCh4F1wKfA2LoGc3qngemSah2kCyQi7gU+Bi7JGfoDuA24EvgB6N8AxksekbS201qlONgDDC1RXi9pckQ8DyzpZKzi+3Hgdkm72q03yAvAm22UJgMbgS3AXQ1hvgFGS4qq9QZZA0wFLqpQOpSuaBBgg5c1hJkg6cNKkBQjwxzlwDRgcInyOklTI+IlYGFDkM8lVQZ9MX0drBOAGcD9BS9NdFqn4jWqBzC7gXf9k/RdJ4/cnFJ3BbDB1TEirgeeSD8H8sF0RfbeV4UMK9p3YGabf5+8PkDS0U4gLwKLk5Kr42pgpaQdEXFx8o69dFjStIhYBMwtGN0BrE8nd7q7LNwCPAg8BJyUdGcnEGeFXV+ULwB7yWl8IiIcP64vDuxvgTO5k/+UNve1eWMDGCST/4Grq0p/VtB+LiwqAh3Oecm6Pm3/zGhE3JE29uZuiNsBZ+MxwB51zzrr/yT9VeaVDOS3ioJWtubL5KVfgEkJwPFkcTf2NV8HPGUPFAz8CMyX5BtokQzkd2BIDzKhTNUQjwMvA7d2sOUOPVuSvdQlGYizwO7tjTznJpcyK29nE+CszMeLvy+W5PGiBcRZ8mgvKFwf3gdc8IoyMAWvvZAXe2OUJF9Xt0eazh2Z4TkpNq4tAbkKcL96u+Tbckmz8iCmdpw4upuIa4zTvEzagXgSdJadn1kjYlUKtiYgPlV+qMrbaAdySpJn3xYQl/GdwBUNSGYDyxp45Kgkg7ZO8RHhQXlpAxBny8qKdZ6D76mo3Fsl3X0BiP9oeEX2hvvI6BKYrcANFePFHElvVIG4JNuwA7Cu/A140nOcFcU1xFlTnGMOADdK+qcUJLOSnhav9WBo3pAeW88USI6kATw/mJ8Cxklyu+iSti+9iBgOLACmAJfWcM8rgJ8RLmz5jfNL99teHqIjSM47bv8eE/zs9BPDfclgntD3AtuAzcBHkk5HxE3uJcB9gBviiZSRbnYrykaBWm/fGp7otUqfATkH0sAoMAH+9p4AAAAASUVORK5CYII="
/***/ }),
/***/ "2mw7":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = login;
var _config = __webpack_require__("bzuE");
var _api = __webpack_require__("H/Zg");
// preset before starting RTC
// class Presetting {
// // init() {
// // // populate userId/roomId
// // $('#userId').val('user_' + parseInt(Math.random() * 100000000));
// // $('#roomId').val(parseInt(Math.random() * 100000));
// // // const roomId = this.query('roomId');
// // const roomId = '83999';
// // const userId = this.query('userId');
// // if (roomId) {
// // $('#roomId').val(roomId);
// // }
// // if (userId) {
// // $('#userId').val(userId);
// // }
// // $('#main-video-btns').hide();
// // $('.mask').hide();
// // setBtnClickFuc();
// // }
// // query(name) {
// // const match = window.location.search.match(new RegExp('(\\?|&)' + name + '=([^&]*)(&|$)'));
// // return !match ? '' : decodeURIComponent(match[2]);
// // }
// login(share, callback) {
// let userId = $('#userId').val();
// if (share) {
// userId = 'share_' + userId;
// }
// const config = genTestUserSig(userId);
// const sdkAppId = config.sdkAppId;
// const userSig = config.userSig;
// const roomId = $('#roomId').val();
// callback({
// // sdkAppId,
// // userId,
// // userSig,
// // roomId
// sdkAppId: 1400246643,
// userId: "user_43707228",
// userSig:
// "eJwtzMEKgzAQBNB-ydUia4y6Cr0Ueih4UqG9lWoSWaJVkrQUSv*9oh7nzTBf1pR1*FaWFYyHwA5rJqmenjSt-HLK3kWcQcY57gMnzWOeSbIiEgBcpKmIt8bTqBZNEFFEOeCm6jOTXTwFgQD7B-XLe95IDzc7TH2gz21QXU5j1155OdLQmUrWueZZMiBOxh3Z7w8UZDLR",
// roomId: "83999",
// });
// }
// }
function login(share, roomId, mobilePhone, cid, name, callback) {
console.log(roomId);
console.log(cid + name);
// let userId = 'user_43707228';
// if (share) {
// userId = "share_" + userId;
// }
// callback({
// // sdkAppId,
// // userId,
// // userSig,
// // roomId
// sdkAppId: 1400246643,
// userId,
// userSig:
// "eJwtzV0LgjAYBeD-slvD3tycU-Ai6ULQkqiuZeDM4UdjM-ui-56pl*c5B84HndOTPQiNAuTYgFZTloXoelnKiU3FtcjvRujcdx2CMXOXmSlqrpQsULAhAA6hlOC56WUrRnUZ831MMMwqnkrq0SkQBosZef1-WF2yi2Ku1tkhebf6xbw0avcGLnHdlVVzs*jRawb*yLYh*v4Asdg1aw__" roomId: "83999",
// });
if (share) {
(0, _api.getTencentYspSig)({
cid: mobilePhone,
name: name,
isShare: "1"
}).then(function (res) {
callback({
sdkAppId: _config.imKey.SDKAppID,
userId: "share_" + mobilePhone,
userSig: res.data,
roomId: roomId
});
});
} else {
(0, _api.getTencentYspSig)({
cid: mobilePhone,
name: name
}).then(function (res) {
callback({
sdkAppId: _config.imKey.SDKAppID,
userId: mobilePhone,
userSig: res.data,
roomId: roomId
});
});
// callback({
// sdkAppId: imKey.sdkAppId,
// userId: "user_95243385",
// userSig:
// "eJwtzEsLgkAUBeD-MuuI68wdGYV2vcAsSkNqE8KMdgnFV1MR-fdEXZ7vHM6Xxbtobk3DfMbnwGZDJm3KjjIa*Nma5uZJjkIoOQ1a-UirijTzHQTg6LooxqajwvQqlfI8gQJGNe*Kmt5dQAWTtZT37-szrWUY4yXRdgW5PZxksbkGd-sKeJR*nFLVWZ2E23h5XLDfHxB2Msg_",
// roomId,
// });
}
}
/***/ }),
/***/ "2pqD":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _interopRequireDefault = __webpack_require__("ouCL");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
__webpack_require__("rpBe");
var _checkbox = _interopRequireDefault(__webpack_require__("hK1P"));
var _classCallCheck2 = _interopRequireDefault(__webpack_require__("Q9dM"));
var _createClass2 = _interopRequireDefault(__webpack_require__("wm7F"));
var _possibleConstructorReturn2 = _interopRequireDefault(__webpack_require__("F6AD"));
var _getPrototypeOf2 = _interopRequireDefault(__webpack_require__("fghW"));
var _inherits2 = _interopRequireDefault(__webpack_require__("QwVp"));
__webpack_require__("sRCI");
var _modal = _interopRequireDefault(__webpack_require__("vnWH"));
var _react = _interopRequireWildcard(__webpack_require__("GiK3"));
var _dva = __webpack_require__("S6G3");
__webpack_require__("bPsJ");
var _RecordVideo = _interopRequireDefault(__webpack_require__("F1Lc"));
var _recordRTC = __webpack_require__("NH7q");
var _recording = _interopRequireDefault(__webpack_require__("RMDC"));
var _dec, _class;
/**
* 视频通话页面
*/
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
var confirm = _modal.default.confirm,
warning = _modal.default.warning,
success = _modal.default.success,
error = _modal.default.error;
var _TOOLS = [{
type: 'mute',
icon: __webpack_require__("NCSo"),
checkIcon: __webpack_require__("CE2x"),
name: '静音'
}, {
type: 'video',
icon: __webpack_require__("Z1a+"),
checkIcon: __webpack_require__("5uwJ"),
name: '视频'
}, {
type: 'add',
icon: __webpack_require__("zVWh"),
checkIcon: __webpack_require__("WO9G"),
name: '添加'
}, {
type: 'record',
icon: __webpack_require__("7Mgp"),
checkIcon: _recording.default,
name: '录制'
}, {
type: 'close',
icon: __webpack_require__("MRHW"),
checkIcon: __webpack_require__("SECW"),
name: '结束'
}];
var _GENRE_TOOLS = [{
type: 'mute',
icon: __webpack_require__("NCSo"),
checkIcon: __webpack_require__("CE2x"),
name: '静音'
}, {
type: 'record',
icon: __webpack_require__("7Mgp"),
checkIcon: _recording.default,
name: '录制'
}, {
type: 'close',
icon: __webpack_require__("MRHW"),
checkIcon: __webpack_require__("SECW"),
name: '结束'
}];
var BoxTool = exports.default = (_dec = (0, _dva.connect)(function (state) {
return {
ownCid: state.im.v5Data.ownCid,
videoType: state.command.videoType,
//0: 九宫格 1: 右侧列表 2: 顶部列表 3: 十六宫格 4: 二十五宫格
recordStatus: state.command.recordStatus,
// 0:未录制 1:录制中
videoGenre: state.command.videoGenre // 功能类型 0: 视频调度(一对多) 1: 视频上拉(一对一)
};
}), _dec(_class = /*#__PURE__*/function (_Component) {
function BoxTool(props) {
var _this;
(0, _classCallCheck2.default)(this, BoxTool);
_this = _callSuper(this, BoxTool, [props]);
_this.onClickTool = function (type) {
var _this$props = _this.props,
canMic = _this$props.canMic,
common = _this$props.common,
switchVideoTool = _this$props.switchVideoTool,
canCamera = _this$props.canCamera,
addMember = _this$props.addMember,
dissolutionRoomModal = _this$props.dissolutionRoomModal;
switch (type) {
case "mute":
{
if (canMic) {
switchVideoTool(type);
if (common.isMicOn) {
common.isMicOn = false;
common.muteAudio();
} else {
common.isMicOn = true;
common.unmuteAudio();
}
}
break;
}
case "video":
{
if (canCamera) {
switchVideoTool(type);
if (common.isCamOn) {
common.isCamOn = false;
common.muteVideo();
} else {
common.isCamOn = true;
common.unmuteVideo();
}
}
break;
}
case "share":
{
common.screenShareClick();
break;
}
case "add":
{
addMember();
break;
}
case "record":
{
_this.onRecordVideo('start');
break;
}
case "close":
{
dissolutionRoomModal();
}
}
};
_this.onRecordVideo = function (status) {
// 0:未录制 1:录制中
var that = _this;
if (status == 'start') {
warning({
title: '录制功能为浏览器录制,录制过程中,请勿关闭当前录制的浏览器页面及注意当前网络!',
okText: '我知道了',
onOk: function onOk() {
_modal.default.destroyAll();
(0, _recordRTC.startRecord)(function (success) {
that.props.dispatch({
type: 'command/fetchRecordStatus',
payload: 1
});
}, function (err) {
error({
title: '浏览器无法获取录制权限,启动失败',
okText: '好的'
});
});
}
});
} else if (status == 'end') {
confirm({
title: '是否确认结束视频调度录屏?',
okText: '确定',
cancelText: '取消',
onOk: function onOk() {
_modal.default.destroyAll();
(0, _recordRTC.stopRecord)();
that.props.dispatch({
type: 'command/fetchRecordStatus',
payload: 0
});
var hasE = (0, _recordRTC.hasError)();
if (hasE) {
error({
title: '录制文件生成失败',
okText: '好的'
});
} else {
success({
content: '录制结束,录制文件已下载完成!',
okText: '我知道了',
onOk: function onOk() {
_modal.default.destroyAll();
(0, _recordRTC.downloadRecord)();
}
});
}
},
onCancel: function onCancel() {
console.log('Cancel');
}
});
}
_this.props.onRecordVideo && _this.props.onRecordVideo();
};
_this.state = {
isShowChange: false // 切换布局弹框是否显示
};
return _this;
}
(0, _inherits2.default)(BoxTool, _Component);
return (0, _createClass2.default)(BoxTool, [{
key: "componentDidMount",
value: function componentDidMount() {}
}, {
key: "onChangeVideoType",
value: function onChangeVideoType(type) {
console.log('onChangeVideoType', type);
this.props.dispatch({
type: 'command/fetchVideoType',
payload: type
});
}
}, {
key: "onShowChange",
value: function onShowChange() {
this.setState({
isShowChange: this.state.isShowChange ? false : true
});
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _this$state = this.state,
currentTime = _this$state.currentTime,
isShowChange = _this$state.isShowChange;
var _this$props2 = this.props,
toolStatus = _this$props2.toolStatus,
recordStatus = _this$props2.recordStatus,
addMember = _this$props2.addMember,
boxToolStyle = _this$props2.boxToolStyle,
dissolutionRoomModal = _this$props2.dissolutionRoomModal,
videoType = _this$props2.videoType,
videoGenre = _this$props2.videoGenre;
var hasVideoScheduling = videoGenre == 0 ? true : false;
var tools = hasVideoScheduling ? _TOOLS : _GENRE_TOOLS;
return /*#__PURE__*/_react.default.createElement("div", {
className: boxToolStyle
}, tools.map(function (tool, index) {
if (recordStatus != 0 && tool.type == 'record') {
return /*#__PURE__*/_react.default.createElement("div", {
className: "tool-item",
onClick: _this2.onRecordVideo.bind(_this2, 'end')
}, /*#__PURE__*/_react.default.createElement("img", {
src: _recording.default,
alt: ""
}), /*#__PURE__*/_react.default.createElement("div", {
className: "name"
}, "\u7ED3\u675F\u5F55\u5236"));
}
return /*#__PURE__*/_react.default.createElement("div", {
className: "tool-item",
onClick: _this2.onClickTool.bind(_this2, tool.type)
}, /*#__PURE__*/_react.default.createElement("img", {
src: toolStatus[tool.type] ? tool.checkIcon : tool.icon,
alt: ""
}), /*#__PURE__*/_react.default.createElement("div", {
className: "name"
}, tool.name));
}), hasVideoScheduling ? /*#__PURE__*/_react.default.createElement("div", {
className: "tool-item change-abs",
onClick: this.onShowChange.bind(this)
}, /*#__PURE__*/_react.default.createElement("img", {
src: toolStatus.close ? __webpack_require__("dToI") : __webpack_require__("dToI"),
alt: ""
}), /*#__PURE__*/_react.default.createElement("div", {
className: "name"
}, "\u5207\u6362\u5E03\u5C40"), isShowChange ? /*#__PURE__*/_react.default.createElement("div", {
className: "change-content"
}, /*#__PURE__*/_react.default.createElement("div", {
className: "change-item",
onChange: this.onChangeVideoType.bind(this, '0')
}, /*#__PURE__*/_react.default.createElement("img", {
className: "change-img",
src: __webpack_require__("AS0P")
}), /*#__PURE__*/_react.default.createElement(_checkbox.default, {
checked: videoType == "0" ? true : false,
key: "0"
}, "\u4E5D\u5BAB\u683C")), /*#__PURE__*/_react.default.createElement("div", {
className: "change-item",
onChange: this.onChangeVideoType.bind(this, '3')
}, /*#__PURE__*/_react.default.createElement("img", {
className: "change-img",
src: __webpack_require__("AS0P")
}), /*#__PURE__*/_react.default.createElement(_checkbox.default, {
checked: videoType == "3" ? true : false,
key: "3"
}, "\u5341\u516D\u5BAB\u683C")), /*#__PURE__*/_react.default.createElement("div", {
className: "change-item",
onChange: this.onChangeVideoType.bind(this, '4')
}, /*#__PURE__*/_react.default.createElement("img", {
className: "change-img",
src: __webpack_require__("AS0P")
}), /*#__PURE__*/_react.default.createElement(_checkbox.default, {
checked: videoType == "4" ? true : false,
key: "4"
}, "\u4E8C\u5341\u4E94\u5BAB\u683C")), /*#__PURE__*/_react.default.createElement("div", {
className: "change-item",
onChange: this.onChangeVideoType.bind(this, '1')
}, /*#__PURE__*/_react.default.createElement("img", {
className: "change-img",
src: __webpack_require__("6Jhx")
}), /*#__PURE__*/_react.default.createElement(_checkbox.default, {
checked: videoType == "1" ? true : false,
key: "1"
}, "\u53F3\u4FA7\u5217\u8868")), /*#__PURE__*/_react.default.createElement("div", {
className: "change-item",
onChange: this.onChangeVideoType.bind(this, '2')
}, /*#__PURE__*/_react.default.createElement("img", {
className: "change-img",
src: __webpack_require__("K7vd")
}), /*#__PURE__*/_react.default.createElement(_checkbox.default, {
checked: videoType == "2" ? true : false,
key: "2"
}, "\u9876\u90E8\u5217\u8868"))) : null) : null, /*#__PURE__*/_react.default.createElement(_RecordVideo.default, null));
}
}]);
}(_react.Component)) || _class);
/***/ }),
/***/ "5uwJ":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAAAXNSR0IArs4c6QAAA4lJREFUWEftl01oHGUYx3//2dUQojub1CoePJQaKfFkTQ5+0YJ46UFFBQ8q9ZNEatLsxtaj40lBs7uKXaFpIVVQ8OChCCJ4CIJVGtsiogc/DkrVQm2ys1htTHYemTG7zq4N3W22mwjOcXif//t7n/d5nvmPWGeP1hkP/x0gzzPnq4HmgG/8GvM8Be3Idl2G3Kzdj/E0sNWM3lY2kJgHjsuhWJrUe63ExtdGQBs9u2LB53WMnRcr1BA33ZVm9LSn31rVi4DccTto8HhdsAgwrClBIQyn7qTioJ/Xk03FxxbJnbCbLeDosqAhXutKsm9XD983Wxdhve07y+bFCqNBwGikLwIZQ35Bx1uBUkN2pssFPdaKQOPaVMamq1evC2Rpu2fJMH7G01JVR6lxOwLcEh0qwaA/qWOrAXIzNmjG7LLGp+WCbm3US3uWDnwKwIMYCeCdlBg5mdcfcjM2V+2opIs794LKqwHqe95SSz5+dEAx7+fVF9fbMGFbFiscBvobau4jP6+7wgzVCrdcUFsG5UqavVnbERhvm+HGYML9o327LuOGjgG5Gdtj8FK1GyXOInaasRvjjhDIEdsvOVBfmu45nymMh2uFK36QuKeU0xduxmbM2NYxIMGswVANBj7u7uaBUy/qdDQDOw3UULj7r9/CM8eGtVh9vzZAIpwzu8t5FRs7uONAEl8qwVjpFc2cb5x0HAjxDQ5j5Ul9uD6A/qaoOA57Sznl1vzK6gDEoas3MfzdmBbWrqjFJxi31cDEZ5cb9/1a0C9r0vbXpek66VM044nYYPwJuNfP6/OOF3X1+5jO2lhgTGJElgNxDiM0cCPA7dGkTrDtkn864h9sN2t3YrxrRp0DiF1nf9vtR+9z5lYWKK1kP9w9ttmWOIwx0NBxH5QL2qFUxsKii0yUxFB4r6vxQ6kJG6LC0eVrOVLO65+CXha+aq9dufgnLwMPmUgK3tq4idGw+8IMHagVnDhUzuvRVQFl7E2MR6IDOhzwc3pqJT0zk6S6H4nQU281MRvzKcUEFK91+XZggEozcCdOkTzzI/1BhV1mUZFGJt8Rg6WcTjSjUevAaBZkbcqCqOLb9kjs9/MablUwso7XPGs95yq8Gp8VrQrF10tM9biM/+zp91Z16jx0Omt3BwEjiJswNoSjoUnBAHEm/JVOiDfmc3q/ybh/LWuLqb/Yzc8X9z/QhbK57jL0F0MRw3k9o7x7AAAAAElFTkSuQmCC"
/***/ }),
/***/ "6Jhx":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJgAAABoCAYAAAANUDk4AAAAAXNSR0IArs4c6QAAAmxJREFUeF7t3LFNBFEQBFE2WYIi2cNFCJOR6lpvAxh1d5XxrX1er9fnhy+zwPM8Xz/DvDuf590LZMz4pyAE+6chnfl7AYIx43QBgp3O6zjBOHC6AMFO53WcYBw4XYBgp/M6TjAOnC5AsNN5HScYB04XINjpvI4TjAOnCxDsdF7HCcaB0wUIdjqv4wTjwOkCBDud13GCceB0AYKdzus4wThwugDBTud1nGAcOF2AYKfzOk4wDpwuQLDTeR0nGAdOFyDY6byOE4wDpwsQ7HRexwnGgdMFCHY6r+O/BXv3RfwfLEaQYDEga3EItkY01odgMSBrcTzy14jG+hAsBmQtDsHWiMb6ECwGZC0OwdaIxvoQLAZkLQ7B1ojG+hAsBmQtDsHWiMb6ECwGZC0OwdaIxvoQLAZkLQ7B1ojG+hAsBmQtDsHWiMb6ECwGZC0OwdaIxvoQLAZkLQ7B1ojG+hAsBmQtDsHWiMb6ECwGZC0OwdaIxvoQLAZkLQ7B1ojG+hAsBmQtDsHWiMb6ECwGZC0OwdaIxvoQLAZkLQ7B1ojG+hAsBmQtzm/B3r2ff7TGCBIsBmQtDsHWiMb6ECwGZC2OR/4a0VgfgsWArMUh2BrRWB+CxYCsxSHYGtFYH4LFgKzFIdga0VgfgsWArMUh2BrRWB+CxYCsxSHYGtFYH4LFgKzFIdga0VgfgsWArMUh2BrRWB+CxYCsxSHYGtFYH4LFgKzFIdga0VgfgsWArMUh2BrRWB+CxYCsxSHYGtFYH4LFgKzFIdga0VgfgsWArMUh2BrRWB+CxYCsxSHYGtFYH4LFgKzFIdga0VifNcG+AeKO5ad/GBYzAAAAAElFTkSuQmCC"
/***/ }),
/***/ "6Mj6":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,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"
/***/ }),
/***/ "6VvU":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
module.exports = isMobile
module.exports.isMobile = isMobile
module.exports.default = isMobile
var mobileRE = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i
var tabletRE = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series[46]0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino|android|ipad|playbook|silk/i
function isMobile (opts) {
if (!opts) opts = {}
var ua = opts.ua
if (!ua && typeof navigator !== 'undefined') ua = navigator.userAgent
if (ua && ua.headers && typeof ua.headers['user-agent'] === 'string') {
ua = ua.headers['user-agent']
}
if (typeof ua !== 'string') return false
var result = opts.tablet ? tabletRE.test(ua) : mobileRE.test(ua)
if (
!result &&
opts.tablet &&
opts.featureDetect &&
navigator &&
navigator.maxTouchPoints > 1 &&
ua.indexOf('Macintosh') !== -1 &&
ua.indexOf('Safari') !== -1
) {
result = true
}
return result
}
/***/ }),
/***/ "6upA":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAABCCAYAAAAsejQrAAAAAXNSR0IArs4c6QAACvBJREFUaEPNWntwVNUZ/33n3Lub94uNo0VR21pH66vTmdp2LEMC8hq18ggImkSopuhIRRzxWUmR1krxUbEqKGhJxAKjtIJWKEod7Uyddqb1RRXsaJWAkmxCkkU22b3n69y7uZvde+/u3ZvgjPef3bnnnO/7ne/8vsc55xJG8XBLa0lXsvtCQF3EwIVgHguglokiBI4D6GRQJzH2keA3SNIbkXWPvD8KlaCRDD78kxvHkzKuVaxmE6iIwQHE0EdEtJ4IT9VueORggIFW10CAD199/VRm/g2Ac6zBRGBm69d8mM13qd9U+/A7JzAiSjKrZ0jIO4IALwjwkZbF30gMGg8z8/RMxSYuU0Au+9ptthKvCYEpBkH31p5Wu4paW5N+FvcF3Nl8w2WAsZEZlebSEwg2Bez/5m96vexZWCZ3zsj5YnhKRHi9mGhO2dOPfpYPdF7AnU3XrwCruzgXIpfJMkB6UiSbQi4OAYeEJmdEnvrdm7lA5wTc2bjoPgYvC7bofgua28L2SAL1AXJibdsj//SS5gn4cON1d4HVPX7qnU7lNPiIx4OiGjChpu3xd13O6nzR1bRoBit+3kuZk8NOjjqjhlOGHVHMX68wkumkRPQxVennj1mzpi9TTpaFO69qOYlA7yjmMbmWI9OnnHHLCcgVJRw+6NcOoo0ntK9tzg34ypaXAEzLGUhda+5QWUgcs2UUEkWIQEyzIpvWplc8beHO+S11AL+aFWd9EkGQROFJsaGkY1PEm1K0P3L5pLNozhwjK9N1zr/mNTDG53UUl5cFXeOAqWZoxUiK5kj72o1pwD3zfzo+ycZrXh6ZlQccFndnBh9WOibsy+H0/Ghf7R+eODMNOHrFNWsZaHFFAccMnJkts78cexKK6sdDP/dsyNox1lyMrigSb+/FwJ7XYXQccic+x5t88kmK70WeeeIfxFu2yK7ndx0E8wl+cdWzXZMouWoOiibXgYTwZBQrhfiuPTjatgUwLCp6RbV0IZXm9FAZkEq0YlVk8xO3kkkHw0hadHCFpTzVlqVRaqi4fQn0c87yyxFWe+Kdvej79W8Bw65xskmRXz99GNm8/gyKzl24lBn3e2r0IVnpwitRNKW+ILB2p/jOVxHb0J6ycJYFMxw4Xd5lO7Usqaim6JyFGxhYYEkIkFvlyV9D5eoVOWmQaxYmPXpvWQ7jwEGPAjq7eBquY1IzEKCLKNqw8O8MvtD2wLzVYQaKkqvnoXj6xYGsa3c+9uIufPH0s/krfA8DkhAt1NWwYD+Yv+mlOZ/XVj6wEtop5hYu+JP8tAO9S+9yDXTvYFK7GdsJBYnbKTq7+X8AjfN2Okf9mqGieuNjoOKi4GjNVT8WR0/TdR4MzJ9YpBDLKTqr2dwInjQUaLJIlC8u17Q9PirA3Y2L0tsr322PbRYSK6lrVvM+Yj6jEMCZFKl86JeQI6SEYVJiyR1uDvsUJ0R8G0VnNu8CeMh7fJNlmgIlC+aj6JIpI6JEfMdOfLFhkzMI+Mc1yCuoe0bTOgauTWkuvDgxrVvx4MoRhbW+pXfC+KSjAIDZ9tCE9n3qmdm0mBkPezmdi2SO+ZRc24Si6ZMCWTn+0m4ce9IqvHw31ZlRg4BkVUnlGOprWHhmIjFoHR+5wphPvcpSoPzuZdDPO7sg0Im330P/PatByVQ94U7FjoOZjGMFIvytZlv7RZaD9lx+1cfMONV3T+tV/WgaihfMQ3jqJJDMUfwYCgMv77Z4S4aR++Al/0nR8uptbStSgH/c+ACDb/I1k8OLMycoThmLoskToF1wLmRtxFpw43AXkm+9i/iuv0J92uE62vLTl7kCJMW5Ndva3rUAH2loPJ0H+UNm9jaRn+QvuZ2IXqn+U7vlLOmY3XPZlc8zMCOdflIkyzrd80udgXH7FeBpgXRpzfZndmQDvnz+BTBgnrZI+9jALxX5RW2/dtcEPZ2I3qze3v4DIrIOM7KyYs8l81YzcHM6EzqOU51xyG9LFdTirl0zc1IX+nfLXtj4dhpTplBubCw90mO8x8yneivLNoFbgc8u2iHU75iAgFVVO569NXOYq+7ouazpOzCSrwNcGnhX7JqlYwvkd1ybsYQE2lM5tnwKrVuXyAvYbOy7ZN6livFHBou0DA8ndBUDgUnrmKF9DiHofTD9sOrFTT1OG+Ss7I5Mv2IRgx/LBOyXCV13BH5RxmFxS5dAB4nQj6q2t33kRcu8pWjvtLk3MLAmyJVLfkfLn0uJ0EGaPqHyhfYPc8nxrZ17p81drJit4si1PK5LmewtjV+UyC5u6CCkmFC5Y9P+fON8AZuDe6fPvZEVP+S+ssifWJyKcxU7gugQCZpQ8dLmfb6T9Otgt/dMm7uEFD9ox17rvcPJRnQ+TDgkgbqKl7d+UAiWgixsC+qb2rBUmYcuNqmdUcFl0vxxmQifka7VVWx/tuDb0UCArZA3ZfbNirG6EGt4sD5dthPoc6FTXfmLm/8TRFZgwBboqQ23sOJVfpnKfZCU8gICPhchGRiszcIgE0z37bt49jLFfF/WYL/EQRbtDwtN1JX/eevekSgekYXTnJ7UcBtD3VuoYiLqJMF15Tufe6/QMa5QOtKBadCTZ9+uDPUrj6CRVdyZYIXQ6st3bXbdvQXBMCoL24r6J826UzGvzKWYgC5Bsr5s99Z3goDz6ntcAJuC+yfO/LliXuFaQkJUQNSXvfJcuqYdDejjBjgFesbdivEL+56PBEWFwsSyPdveGg3IzLHHFbApuLd+ZitYLQdRt5TaxLK/bP338QI7qrCWD0Rf/YzbpBA7S3c/96/jCfZLAczMhDlbBb7dwNRK6isBeAhUUawsVqbxQCklQ+Ewi1CCWIcysmmmkeKEGmQpBhRzvFjXYjjttBi11vl+LjPqKMGLHw6jV48MGqKG2AjltJ6SGoRigIcv5Rydlab6wyiP4vdN3fYWvpDVKMjpuGGLRHXPWCQQSWRakHRdsaog5mIoDgOalOY+0HxMMpj/WCVBIplMclxCHGUYMWsyunlxB3BIDoaKjQP02PWu/duILMwNraHBksgZBCN9oUEiGSaEa5GgMkgATjuyIpBw76xSfVVSV90imYhCyHSfpE6flzz5swN+Vva1cLzp/m8J4nJbEEkUKyXGSR76WG24QScDVQAXD9kWyvo2zYgJUr2uCRCOKWPwk0zQxzTxUeX6pd35QOcFzC1r9cRA/3mZAphwqmSYoIYfQcTM44RzEkM9DOaYIHQ5gbDgQ6yo136va6KX1i/NuQH1DWvc2qolDoTPzwIsxCmSVWmWclbEoJMF2Fx012OQ6hPQXJZjRgcD/fYAJeSRonXL/jtiC5sD+Zp7Tk+AazIoEVKGGCfJ0LJBk1RCVRBUMUzgRGwokRCkjgqWMSeIJES/YDYvOtKPXqz205rWrI+SnON8OWx+noBXPvj6FwZXDDk2yIDGksawblRJ08GGSKsMQGQ6of1fDgcNQ8lBQ4loGNxrnkGZMnUhORamT8vX3NE5aqezrMxMR5fceUJIyRMTSQxbVikSQpQqxaUQCGnM0mBoiqBBQHGCDUgyWCIZYjqmBB3VDJif6SKRAHTdnAjHwqWVB2j1LUf9wPpy2OUkDVtk/7i91aE4agwVL0/Zxz6rs/+bvxakIftlneVZLbqmJw2ljhSxHqVHW110GRWHcw02k0n3iXtLiwYGS4VQ4QSpMImwFiKIBJkpYxA6SWOQWekGDyrIAUOqeJlWG8MDN8WDZLdMDP8Ha96wbRjI3DAAAAAASUVORK5CYII="
/***/ }),
/***/ "7Mgp":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkCAYAAADhAJiYAAAAAXNSR0IArs4c6QAAAudJREFUWEfNmE2ITlEYx39/LCwQm6lRLEmWFsQoM7EQC1+RMrPxsfOVicISZRDDzsfCUCKDBVmQmTImFpaSWVKUDZlZWODRk+fquN73vXfGW+6pu7n3PP/nd59zznPOecQ4m5nNAtYAq4C5wOx4XOl9PG+BR8ADSZ/G40JlO5vZemA3sByYUtLuG/AUuCDpbhmbQiAzWwb0AEvLCDboMwwclPSskU5dIDObDJwB9uYEfgAufg94mQyTd8uGbxGwLn5iUs6+Fzgg6XstsJpAZjYTuBXzJLP7Cpx3SEkfy0TLzFrcObAHmJrY+PzaLOlzXucvoIDxCCxIOvcD+yW9KwOS72Nmc4CzwMbk22uPYB7qD6AYpodJZAw4KunEREBqgB0GjgGZX4/U6nT48kDnkjnjMFsl3WwGTKZhZluAGwlUr6R92fffQLGahhLnR5oVmTqROp68b8tWXwrkyzFb2v2SNjUzMjWgbidzaliSp5dfYxlJ704Y+WqaN9EJXPYnYqKPJKtvgyfPDOgJ0B5iPZIOlRX+l35mdtKTZWgMSOpQ7E2eV3w78KTXWibPmJn/zA5gO7AwRF8BV4DLknxRNGyRpz4Anjx9m2lxoE6gLyyHJPleVSTUClwHOup09Ihvk+TOirR8r2uLTl0O5DAO5a1bkm8XdVtE5nEDmMzWoVYWRcrMPJOfDqNrDjQArIgX7ZIGC4B2AheL/jy+75J0qUDPfTuDt0EHeuOrKl7Ml+Qzv1GEngOLSwK9kLSkQM99O4O3EQcaBabFi+mSxgoE0v5FXGOSphfouW/X9DbmQF+AzGiGpOxjTZ3cDzQDyH07g7fRSg5Z5Sb1VaArQvY/ln03cCr891UtMXZWb+vwUJlZdTbXAPI7V3WOHwFVnQNaAPmJrTpH2ICqziE/gPy2Wp1rUED5rbUaF8Vsp6zUVTqBqk6xIT1TVKYckz/oVKZgVQPMS3proyDhVY1aJT2vkngh4f54S3o/AdIWsUP2RbRSAAAAAElFTkSuQmCC"
/***/ }),
/***/ "8VXG":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _interopRequireDefault = __webpack_require__("ouCL");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = CheckRTC;
__webpack_require__("UQ5M");
var _message2 = _interopRequireDefault(__webpack_require__("/qCn"));
/**
* 检查设备是否支持
*/
// import Common from './common';
// const presetting = new Presetting();
// presetting.init();
function CheckRTC() {
// 检查浏览器是否支持TRTC视频功能(check if browser is compatible with TRTC)
TRTC.checkSystemRequirements().then(function (result) {
console.log("检测浏览器");
if (!result) {
alert('您的浏览器不兼容视频应用!建议下载最新版Chrome浏览器');
window.location.href = 'https://www.google.cn/chrome/';
}
});
// setup logging stuffs
TRTC.Logger.setLogLevel(TRTC.Logger.LogLevel.DEBUG);
TRTC.Logger.enableUploadLog();
TRTC.getDevices().then(function (devices) {
devices.forEach(function (item) {
console.log('device: ' + item.kind + ' ' + item.label + ' ' + item.deviceId);
});
}).catch(function (error) {
_message2.default.error("获取设备失败");
console.error('getDevices error observed ' + error);
});
// populate camera options
// TRTC.getCameras().then(devices => {
// devices.forEach(device => {
// console.log(device)
// if (!Common.cameraId) {
// Common.cameraId = device.deviceId;
// }
// // let div = $('
"recorder.getBlob()"
method. Usage of "getBlob" is recommended, though.
* @property {Blob} blob - Recorded Blob can be accessed using this property.
* @memberof RecordRTC
* @instance
* @readonly
* @example
* recorder.stopRecording(function() {
* var blob = this.blob;
*
* // below one is recommended
* var blob = this.getBlob();
* });
*/
blob: null,
/**
* This works only with {recorderType:StereoAudioRecorder}. Use this property on "stopRecording" to verify the encoder's sample-rates.
* @property {number} bufferSize - Buffer-size used to encode the WAV container
* @memberof RecordRTC
* @instance
* @readonly
* @example
* recorder.stopRecording(function() {
* alert('Recorder used this buffer-size: ' + this.bufferSize);
* });
*/
bufferSize: 0,
/**
* This works only with {recorderType:StereoAudioRecorder}. Use this property on "stopRecording" to verify the encoder's sample-rates.
* @property {number} sampleRate - Sample-rates used to encode the WAV container
* @memberof RecordRTC
* @instance
* @readonly
* @example
* recorder.stopRecording(function() {
* alert('Recorder used these sample-rates: ' + this.sampleRate);
* });
*/
sampleRate: 0,
/**
* {recorderType:StereoAudioRecorder} returns ArrayBuffer object.
* @property {ArrayBuffer} buffer - Audio ArrayBuffer, supported only in Chrome.
* @memberof RecordRTC
* @instance
* @readonly
* @example
* recorder.stopRecording(function() {
* var arrayBuffer = this.buffer;
* alert(arrayBuffer.byteLength);
* });
*/
buffer: null,
/**
* This method resets the recorder. So that you can reuse single recorder instance many times.
* @method
* @memberof RecordRTC
* @instance
* @example
* recorder.reset();
* recorder.startRecording();
*/
reset: function() {
if (self.state === 'recording' && !config.disableLogs) {
console.warn('Stop an active recorder.');
}
if (mediaRecorder && typeof mediaRecorder.clearRecordedData === 'function') {
mediaRecorder.clearRecordedData();
}
mediaRecorder = null;
setState('inactive');
self.blob = null;
},
/**
* This method is called whenever recorder's state changes. Use this as an "event".
* @property {String} state - A recorder's state can be: recording, paused, stopped or inactive.
* @method
* @memberof RecordRTC
* @instance
* @example
* recorder.onStateChanged = function(state) {
* console.log('Recorder state: ', state);
* };
*/
onStateChanged: function(state) {
if (!config.disableLogs) {
console.log('Recorder state changed:', state);
}
},
/**
* A recorder can have inactive, recording, paused or stopped states.
* @property {String} state - A recorder's state can be: recording, paused, stopped or inactive.
* @memberof RecordRTC
* @static
* @readonly
* @example
* // this looper function will keep you updated about the recorder's states.
* (function looper() {
* document.querySelector('h1').innerHTML = 'Recorder\'s state is: ' + recorder.state;
* if(recorder.state === 'stopped') return; // ignore+stop
* setTimeout(looper, 1000); // update after every 3-seconds
* })();
* recorder.startRecording();
*/
state: 'inactive',
/**
* Get recorder's readonly state.
* @method
* @memberof RecordRTC
* @example
* var state = recorder.getState();
* @returns {String} Returns recording state.
*/
getState: function() {
return self.state;
},
/**
* Destroy RecordRTC instance. Clear all recorders and objects.
* @method
* @memberof RecordRTC
* @example
* recorder.destroy();
*/
destroy: function() {
var disableLogsCache = config.disableLogs;
config = {
disableLogs: true
};
self.reset();
setState('destroyed');
returnObject = self = null;
if (Storage.AudioContextConstructor) {
Storage.AudioContextConstructor.close();
Storage.AudioContextConstructor = null;
}
config.disableLogs = disableLogsCache;
if (!config.disableLogs) {
console.log('RecordRTC is destroyed.');
}
},
/**
* RecordRTC version number
* @property {String} version - Release version number.
* @memberof RecordRTC
* @static
* @readonly
* @example
* alert(recorder.version);
*/
version: '5.6.2'
};
if (!this) {
self = returnObject;
return returnObject;
}
// if someone wants to use RecordRTC with the "new" keyword.
for (var prop in returnObject) {
this[prop] = returnObject[prop];
}
self = this;
return returnObject;
}
RecordRTC.version = '5.6.2';
if (true /* && !!module.exports*/ ) {
module.exports = RecordRTC;
}
if (true) {
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function() {
return RecordRTC;
}).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
}
RecordRTC.getFromDisk = function(type, callback) {
if (!callback) {
throw 'callback is mandatory.';
}
console.log('Getting recorded ' + (type === 'all' ? 'blobs' : type + ' blob ') + ' from disk!');
DiskStorage.Fetch(function(dataURL, _type) {
if (type !== 'all' && _type === type + 'Blob' && callback) {
callback(dataURL);
}
if (type === 'all' && callback) {
callback(dataURL, _type.replace('Blob', ''));
}
});
};
/**
* This method can be used to store recorded blobs into IndexedDB storage.
* @param {object} options - {audio: Blob, video: Blob, gif: Blob}
* @method
* @memberof RecordRTC
* @example
* RecordRTC.writeToDisk({
* audio: audioBlob,
* video: videoBlob,
* gif : gifBlob
* });
*/
RecordRTC.writeToDisk = function(options) {
console.log('Writing recorded blob(s) to disk!');
options = options || {};
if (options.audio && options.video && options.gif) {
options.audio.getDataURL(function(audioDataURL) {
options.video.getDataURL(function(videoDataURL) {
options.gif.getDataURL(function(gifDataURL) {
DiskStorage.Store({
audioBlob: audioDataURL,
videoBlob: videoDataURL,
gifBlob: gifDataURL
});
});
});
});
} else if (options.audio && options.video) {
options.audio.getDataURL(function(audioDataURL) {
options.video.getDataURL(function(videoDataURL) {
DiskStorage.Store({
audioBlob: audioDataURL,
videoBlob: videoDataURL
});
});
});
} else if (options.audio && options.gif) {
options.audio.getDataURL(function(audioDataURL) {
options.gif.getDataURL(function(gifDataURL) {
DiskStorage.Store({
audioBlob: audioDataURL,
gifBlob: gifDataURL
});
});
});
} else if (options.video && options.gif) {
options.video.getDataURL(function(videoDataURL) {
options.gif.getDataURL(function(gifDataURL) {
DiskStorage.Store({
videoBlob: videoDataURL,
gifBlob: gifDataURL
});
});
});
} else if (options.audio) {
options.audio.getDataURL(function(audioDataURL) {
DiskStorage.Store({
audioBlob: audioDataURL
});
});
} else if (options.video) {
options.video.getDataURL(function(videoDataURL) {
DiskStorage.Store({
videoBlob: videoDataURL
});
});
} else if (options.gif) {
options.gif.getDataURL(function(gifDataURL) {
DiskStorage.Store({
gifBlob: gifDataURL
});
});
}
};
// __________________________
// RecordRTC-Configuration.js
/**
* {@link RecordRTCConfiguration} is an inner/private helper for {@link RecordRTC}.
* @summary It configures the 2nd parameter passed over {@link RecordRTC} and returns a valid "config" object.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef RecordRTCConfiguration
* @class
* @example
* var options = RecordRTCConfiguration(mediaStream, options);
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.
* @param {object} config - {type:"video", disableLogs: true, numberOfAudioChannels: 1, bufferSize: 0, sampleRate: 0, video: HTMLVideoElement, getNativeBlob:true, etc.}
*/
function RecordRTCConfiguration(mediaStream, config) {
if (!config.recorderType && !config.type) {
if (!!config.audio && !!config.video) {
config.type = 'video';
} else if (!!config.audio && !config.video) {
config.type = 'audio';
}
}
if (config.recorderType && !config.type) {
if (config.recorderType === WhammyRecorder || config.recorderType === CanvasRecorder || (typeof WebAssemblyRecorder !== 'undefined' && config.recorderType === WebAssemblyRecorder)) {
config.type = 'video';
} else if (config.recorderType === GifRecorder) {
config.type = 'gif';
} else if (config.recorderType === StereoAudioRecorder) {
config.type = 'audio';
} else if (config.recorderType === MediaStreamRecorder) {
if (getTracks(mediaStream, 'audio').length && getTracks(mediaStream, 'video').length) {
config.type = 'video';
} else if (!getTracks(mediaStream, 'audio').length && getTracks(mediaStream, 'video').length) {
config.type = 'video';
} else if (getTracks(mediaStream, 'audio').length && !getTracks(mediaStream, 'video').length) {
config.type = 'audio';
} else {
// config.type = 'UnKnown';
}
}
}
if (typeof MediaStreamRecorder !== 'undefined' && typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype) {
if (!config.mimeType) {
config.mimeType = 'video/webm';
}
if (!config.type) {
config.type = config.mimeType.split('/')[0];
}
if (!config.bitsPerSecond) {
// config.bitsPerSecond = 128000;
}
}
// consider default type=audio
if (!config.type) {
if (config.mimeType) {
config.type = config.mimeType.split('/')[0];
}
if (!config.type) {
config.type = 'audio';
}
}
return config;
}
// __________________
// GetRecorderType.js
/**
* {@link GetRecorderType} is an inner/private helper for {@link RecordRTC}.
* @summary It returns best recorder-type available for your browser.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef GetRecorderType
* @class
* @example
* var RecorderType = GetRecorderType(options);
* var recorder = new RecorderType(options);
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.
* @param {object} config - {type:"video", disableLogs: true, numberOfAudioChannels: 1, bufferSize: 0, sampleRate: 0, video: HTMLVideoElement, etc.}
*/
function GetRecorderType(mediaStream, config) {
var recorder;
// StereoAudioRecorder can work with all three: Edge, Firefox and Chrome
// todo: detect if it is Edge, then auto use: StereoAudioRecorder
if (isChrome || isEdge || isOpera) {
// Media Stream Recording API has not been implemented in chrome yet;
// That's why using WebAudio API to record stereo audio in WAV format
recorder = StereoAudioRecorder;
}
if (typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype && !isChrome) {
recorder = MediaStreamRecorder;
}
// video recorder (in WebM format)
if (config.type === 'video' && (isChrome || isOpera)) {
recorder = WhammyRecorder;
if (typeof WebAssemblyRecorder !== 'undefined' && typeof ReadableStream !== 'undefined') {
recorder = WebAssemblyRecorder;
}
}
// video recorder (in Gif format)
if (config.type === 'gif') {
recorder = GifRecorder;
}
// html2canvas recording!
if (config.type === 'canvas') {
recorder = CanvasRecorder;
}
if (isMediaRecorderCompatible() && recorder !== CanvasRecorder && recorder !== GifRecorder && typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype) {
if (getTracks(mediaStream, 'video').length || getTracks(mediaStream, 'audio').length) {
// audio-only recording
if (config.type === 'audio') {
if (typeof MediaRecorder.isTypeSupported === 'function' && MediaRecorder.isTypeSupported('audio/webm')) {
recorder = MediaStreamRecorder;
}
// else recorder = StereoAudioRecorder;
} else {
// video or screen tracks
if (typeof MediaRecorder.isTypeSupported === 'function' && MediaRecorder.isTypeSupported('video/webm')) {
recorder = MediaStreamRecorder;
}
}
}
}
if (mediaStream instanceof Array && mediaStream.length) {
recorder = MultiStreamRecorder;
}
if (config.recorderType) {
recorder = config.recorderType;
}
if (!config.disableLogs && !!recorder && !!recorder.name) {
console.log('Using recorderType:', recorder.name || recorder.constructor.name);
}
if (!recorder && isSafari) {
recorder = MediaStreamRecorder;
}
return recorder;
}
// _____________
// MRecordRTC.js
/**
* MRecordRTC runs on top of {@link RecordRTC} to bring multiple recordings in a single place, by providing simple API.
* @summary MRecordRTC stands for "Multiple-RecordRTC".
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef MRecordRTC
* @class
* @example
* var recorder = new MRecordRTC();
* recorder.addStream(MediaStream);
* recorder.mediaType = {
* audio: true, // or StereoAudioRecorder or MediaStreamRecorder
* video: true, // or WhammyRecorder or MediaStreamRecorder or WebAssemblyRecorder or CanvasRecorder
* gif: true // or GifRecorder
* };
* // mimeType is optional and should be set only in advance cases.
* recorder.mimeType = {
* audio: 'audio/wav',
* video: 'video/webm',
* gif: 'image/gif'
* };
* recorder.startRecording();
* @see For further information:
* @see {@link https://github.com/muaz-khan/RecordRTC/tree/master/MRecordRTC|MRecordRTC Source Code}
* @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.
* @requires {@link RecordRTC}
*/
function MRecordRTC(mediaStream) {
/**
* This method attaches MediaStream object to {@link MRecordRTC}.
* @param {MediaStream} mediaStream - A MediaStream object, either fetched using getUserMedia API, or generated using captureStreamUntilEnded or WebAudio API.
* @method
* @memberof MRecordRTC
* @example
* recorder.addStream(MediaStream);
*/
this.addStream = function(_mediaStream) {
if (_mediaStream) {
mediaStream = _mediaStream;
}
};
/**
* This property can be used to set the recording type e.g. audio, or video, or gif, or canvas.
* @property {object} mediaType - {audio: true, video: true, gif: true}
* @memberof MRecordRTC
* @example
* var recorder = new MRecordRTC();
* recorder.mediaType = {
* audio: true, // TRUE or StereoAudioRecorder or MediaStreamRecorder
* video: true, // TRUE or WhammyRecorder or MediaStreamRecorder or WebAssemblyRecorder or CanvasRecorder
* gif : true // TRUE or GifRecorder
* };
*/
this.mediaType = {
audio: true,
video: true
};
/**
* This method starts recording.
* @method
* @memberof MRecordRTC
* @example
* recorder.startRecording();
*/
this.startRecording = function() {
var mediaType = this.mediaType;
var recorderType;
var mimeType = this.mimeType || {
audio: null,
video: null,
gif: null
};
if (typeof mediaType.audio !== 'function' && isMediaRecorderCompatible() && !getTracks(mediaStream, 'audio').length) {
mediaType.audio = false;
}
if (typeof mediaType.video !== 'function' && isMediaRecorderCompatible() && !getTracks(mediaStream, 'video').length) {
mediaType.video = false;
}
if (typeof mediaType.gif !== 'function' && isMediaRecorderCompatible() && !getTracks(mediaStream, 'video').length) {
mediaType.gif = false;
}
if (!mediaType.audio && !mediaType.video && !mediaType.gif) {
throw 'MediaStream must have either audio or video tracks.';
}
if (!!mediaType.audio) {
recorderType = null;
if (typeof mediaType.audio === 'function') {
recorderType = mediaType.audio;
}
this.audioRecorder = new RecordRTC(mediaStream, {
type: 'audio',
bufferSize: this.bufferSize,
sampleRate: this.sampleRate,
numberOfAudioChannels: this.numberOfAudioChannels || 2,
disableLogs: this.disableLogs,
recorderType: recorderType,
mimeType: mimeType.audio,
timeSlice: this.timeSlice,
onTimeStamp: this.onTimeStamp
});
if (!mediaType.video) {
this.audioRecorder.startRecording();
}
}
if (!!mediaType.video) {
recorderType = null;
if (typeof mediaType.video === 'function') {
recorderType = mediaType.video;
}
var newStream = mediaStream;
if (isMediaRecorderCompatible() && !!mediaType.audio && typeof mediaType.audio === 'function') {
var videoTrack = getTracks(mediaStream, 'video')[0];
if (isFirefox) {
newStream = new MediaStream();
newStream.addTrack(videoTrack);
if (recorderType && recorderType === WhammyRecorder) {
// Firefox does NOT supports webp-encoding yet
// But Firefox do supports WebAssemblyRecorder
recorderType = MediaStreamRecorder;
}
} else {
newStream = new MediaStream();
newStream.addTrack(videoTrack);
}
}
this.videoRecorder = new RecordRTC(newStream, {
type: 'video',
video: this.video,
canvas: this.canvas,
frameInterval: this.frameInterval || 10,
disableLogs: this.disableLogs,
recorderType: recorderType,
mimeType: mimeType.video,
timeSlice: this.timeSlice,
onTimeStamp: this.onTimeStamp,
workerPath: this.workerPath,
webAssemblyPath: this.webAssemblyPath,
frameRate: this.frameRate, // used by WebAssemblyRecorder; values: usually 30; accepts any.
bitrate: this.bitrate // used by WebAssemblyRecorder; values: 0 to 1000+
});
if (!mediaType.audio) {
this.videoRecorder.startRecording();
}
}
if (!!mediaType.audio && !!mediaType.video) {
var self = this;
var isSingleRecorder = isMediaRecorderCompatible() === true;
if (mediaType.audio instanceof StereoAudioRecorder && !!mediaType.video) {
isSingleRecorder = false;
} else if (mediaType.audio !== true && mediaType.video !== true && mediaType.audio !== mediaType.video) {
isSingleRecorder = false;
}
if (isSingleRecorder === true) {
self.audioRecorder = null;
self.videoRecorder.startRecording();
} else {
self.videoRecorder.initRecorder(function() {
self.audioRecorder.initRecorder(function() {
// Both recorders are ready to record things accurately
self.videoRecorder.startRecording();
self.audioRecorder.startRecording();
});
});
}
}
if (!!mediaType.gif) {
recorderType = null;
if (typeof mediaType.gif === 'function') {
recorderType = mediaType.gif;
}
this.gifRecorder = new RecordRTC(mediaStream, {
type: 'gif',
frameRate: this.frameRate || 200,
quality: this.quality || 10,
disableLogs: this.disableLogs,
recorderType: recorderType,
mimeType: mimeType.gif
});
this.gifRecorder.startRecording();
}
};
/**
* This method stops recording.
* @param {function} callback - Callback function is invoked when all encoders finished their jobs.
* @method
* @memberof MRecordRTC
* @example
* recorder.stopRecording(function(recording){
* var audioBlob = recording.audio;
* var videoBlob = recording.video;
* var gifBlob = recording.gif;
* });
*/
this.stopRecording = function(callback) {
callback = callback || function() {};
if (this.audioRecorder) {
this.audioRecorder.stopRecording(function(blobURL) {
callback(blobURL, 'audio');
});
}
if (this.videoRecorder) {
this.videoRecorder.stopRecording(function(blobURL) {
callback(blobURL, 'video');
});
}
if (this.gifRecorder) {
this.gifRecorder.stopRecording(function(blobURL) {
callback(blobURL, 'gif');
});
}
};
/**
* This method pauses recording.
* @method
* @memberof MRecordRTC
* @example
* recorder.pauseRecording();
*/
this.pauseRecording = function() {
if (this.audioRecorder) {
this.audioRecorder.pauseRecording();
}
if (this.videoRecorder) {
this.videoRecorder.pauseRecording();
}
if (this.gifRecorder) {
this.gifRecorder.pauseRecording();
}
};
/**
* This method resumes recording.
* @method
* @memberof MRecordRTC
* @example
* recorder.resumeRecording();
*/
this.resumeRecording = function() {
if (this.audioRecorder) {
this.audioRecorder.resumeRecording();
}
if (this.videoRecorder) {
this.videoRecorder.resumeRecording();
}
if (this.gifRecorder) {
this.gifRecorder.resumeRecording();
}
};
/**
* This method can be used to manually get all recorded blobs.
* @param {function} callback - All recorded blobs are passed back to the "callback" function.
* @method
* @memberof MRecordRTC
* @example
* recorder.getBlob(function(recording){
* var audioBlob = recording.audio;
* var videoBlob = recording.video;
* var gifBlob = recording.gif;
* });
* // or
* var audioBlob = recorder.getBlob().audio;
* var videoBlob = recorder.getBlob().video;
*/
this.getBlob = function(callback) {
var output = {};
if (this.audioRecorder) {
output.audio = this.audioRecorder.getBlob();
}
if (this.videoRecorder) {
output.video = this.videoRecorder.getBlob();
}
if (this.gifRecorder) {
output.gif = this.gifRecorder.getBlob();
}
if (callback) {
callback(output);
}
return output;
};
/**
* Destroy all recorder instances.
* @method
* @memberof MRecordRTC
* @example
* recorder.destroy();
*/
this.destroy = function() {
if (this.audioRecorder) {
this.audioRecorder.destroy();
this.audioRecorder = null;
}
if (this.videoRecorder) {
this.videoRecorder.destroy();
this.videoRecorder = null;
}
if (this.gifRecorder) {
this.gifRecorder.destroy();
this.gifRecorder = null;
}
};
/**
* This method can be used to manually get all recorded blobs' DataURLs.
* @param {function} callback - All recorded blobs' DataURLs are passed back to the "callback" function.
* @method
* @memberof MRecordRTC
* @example
* recorder.getDataURL(function(recording){
* var audioDataURL = recording.audio;
* var videoDataURL = recording.video;
* var gifDataURL = recording.gif;
* });
*/
this.getDataURL = function(callback) {
this.getBlob(function(blob) {
if (blob.audio && blob.video) {
getDataURL(blob.audio, function(_audioDataURL) {
getDataURL(blob.video, function(_videoDataURL) {
callback({
audio: _audioDataURL,
video: _videoDataURL
});
});
});
} else if (blob.audio) {
getDataURL(blob.audio, function(_audioDataURL) {
callback({
audio: _audioDataURL
});
});
} else if (blob.video) {
getDataURL(blob.video, function(_videoDataURL) {
callback({
video: _videoDataURL
});
});
}
});
function getDataURL(blob, callback00) {
if (typeof Worker !== 'undefined') {
var webWorker = processInWebWorker(function readFile(_blob) {
postMessage(new FileReaderSync().readAsDataURL(_blob));
});
webWorker.onmessage = function(event) {
callback00(event.data);
};
webWorker.postMessage(blob);
} else {
var reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = function(event) {
callback00(event.target.result);
};
}
}
function processInWebWorker(_function) {
var blob = URL.createObjectURL(new Blob([_function.toString(),
'this.onmessage = function (eee) {' + _function.name + '(eee.data);}'
], {
type: 'application/javascript'
}));
var worker = new Worker(blob);
var url;
if (typeof URL !== 'undefined') {
url = URL;
} else if (typeof webkitURL !== 'undefined') {
url = webkitURL;
} else {
throw 'Neither URL nor webkitURL detected.';
}
url.revokeObjectURL(blob);
return worker;
}
};
/**
* This method can be used to ask {@link MRecordRTC} to write all recorded blobs into IndexedDB storage.
* @method
* @memberof MRecordRTC
* @example
* recorder.writeToDisk();
*/
this.writeToDisk = function() {
RecordRTC.writeToDisk({
audio: this.audioRecorder,
video: this.videoRecorder,
gif: this.gifRecorder
});
};
/**
* This method can be used to invoke a save-as dialog for all recorded blobs.
* @param {object} args - {audio: 'audio-name', video: 'video-name', gif: 'gif-name'}
* @method
* @memberof MRecordRTC
* @example
* recorder.save({
* audio: 'audio-file-name',
* video: 'video-file-name',
* gif : 'gif-file-name'
* });
*/
this.save = function(args) {
args = args || {
audio: true,
video: true,
gif: true
};
if (!!args.audio && this.audioRecorder) {
this.audioRecorder.save(typeof args.audio === 'string' ? args.audio : '');
}
if (!!args.video && this.videoRecorder) {
this.videoRecorder.save(typeof args.video === 'string' ? args.video : '');
}
if (!!args.gif && this.gifRecorder) {
this.gifRecorder.save(typeof args.gif === 'string' ? args.gif : '');
}
};
}
/**
* This method can be used to get all recorded blobs from IndexedDB storage.
* @param {string} type - 'all' or 'audio' or 'video' or 'gif'
* @param {function} callback - Callback function to get all stored blobs.
* @method
* @memberof MRecordRTC
* @example
* MRecordRTC.getFromDisk('all', function(dataURL, type){
* if(type === 'audio') { }
* if(type === 'video') { }
* if(type === 'gif') { }
* });
*/
MRecordRTC.getFromDisk = RecordRTC.getFromDisk;
/**
* This method can be used to store recorded blobs into IndexedDB storage.
* @param {object} options - {audio: Blob, video: Blob, gif: Blob}
* @method
* @memberof MRecordRTC
* @example
* MRecordRTC.writeToDisk({
* audio: audioBlob,
* video: videoBlob,
* gif : gifBlob
* });
*/
MRecordRTC.writeToDisk = RecordRTC.writeToDisk;
if (typeof RecordRTC !== 'undefined') {
RecordRTC.MRecordRTC = MRecordRTC;
}
var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45';
(function(that) {
if (!that) {
return;
}
if (typeof window !== 'undefined') {
return;
}
if (typeof global === 'undefined') {
return;
}
global.navigator = {
userAgent: browserFakeUserAgent,
getUserMedia: function() {}
};
if (!global.console) {
global.console = {};
}
if (typeof global.console.log === 'undefined' || typeof global.console.error === 'undefined') {
global.console.error = global.console.log = global.console.log || function() {
console.log(arguments);
};
}
if (typeof document === 'undefined') {
/*global document:true */
that.document = {
documentElement: {
appendChild: function() {
return '';
}
}
};
document.createElement = document.captureStream = document.mozCaptureStream = function() {
var obj = {
getContext: function() {
return obj;
},
play: function() {},
pause: function() {},
drawImage: function() {},
toDataURL: function() {
return '';
},
style: {}
};
return obj;
};
that.HTMLVideoElement = function() {};
}
if (typeof location === 'undefined') {
/*global location:true */
that.location = {
protocol: 'file:',
href: '',
hash: ''
};
}
if (typeof screen === 'undefined') {
/*global screen:true */
that.screen = {
width: 0,
height: 0
};
}
if (typeof URL === 'undefined') {
/*global screen:true */
that.URL = {
createObjectURL: function() {
return '';
},
revokeObjectURL: function() {
return '';
}
};
}
/*global window:true */
that.window = global;
})(typeof global !== 'undefined' ? global : null);
// _____________________________
// Cross-Browser-Declarations.js
// animation-frame used in WebM recording
/*jshint -W079 */
var requestAnimationFrame = window.requestAnimationFrame;
if (typeof requestAnimationFrame === 'undefined') {
if (typeof webkitRequestAnimationFrame !== 'undefined') {
/*global requestAnimationFrame:true */
requestAnimationFrame = webkitRequestAnimationFrame;
} else if (typeof mozRequestAnimationFrame !== 'undefined') {
/*global requestAnimationFrame:true */
requestAnimationFrame = mozRequestAnimationFrame;
} else if (typeof msRequestAnimationFrame !== 'undefined') {
/*global requestAnimationFrame:true */
requestAnimationFrame = msRequestAnimationFrame;
} else if (typeof requestAnimationFrame === 'undefined') {
// via: https://gist.github.com/paulirish/1579671
var lastTime = 0;
/*global requestAnimationFrame:true */
requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
}
/*jshint -W079 */
var cancelAnimationFrame = window.cancelAnimationFrame;
if (typeof cancelAnimationFrame === 'undefined') {
if (typeof webkitCancelAnimationFrame !== 'undefined') {
/*global cancelAnimationFrame:true */
cancelAnimationFrame = webkitCancelAnimationFrame;
} else if (typeof mozCancelAnimationFrame !== 'undefined') {
/*global cancelAnimationFrame:true */
cancelAnimationFrame = mozCancelAnimationFrame;
} else if (typeof msCancelAnimationFrame !== 'undefined') {
/*global cancelAnimationFrame:true */
cancelAnimationFrame = msCancelAnimationFrame;
} else if (typeof cancelAnimationFrame === 'undefined') {
/*global cancelAnimationFrame:true */
cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}
}
// WebAudio API representer
var AudioContext = window.AudioContext;
if (typeof AudioContext === 'undefined') {
if (typeof webkitAudioContext !== 'undefined') {
/*global AudioContext:true */
AudioContext = webkitAudioContext;
}
if (typeof mozAudioContext !== 'undefined') {
/*global AudioContext:true */
AudioContext = mozAudioContext;
}
}
/*jshint -W079 */
var URL = window.URL;
if (typeof URL === 'undefined' && typeof webkitURL !== 'undefined') {
/*global URL:true */
URL = webkitURL;
}
if (typeof navigator !== 'undefined' && typeof navigator.getUserMedia === 'undefined') { // maybe window.navigator?
if (typeof navigator.webkitGetUserMedia !== 'undefined') {
navigator.getUserMedia = navigator.webkitGetUserMedia;
}
if (typeof navigator.mozGetUserMedia !== 'undefined') {
navigator.getUserMedia = navigator.mozGetUserMedia;
}
}
var isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveBlob || !!navigator.msSaveOrOpenBlob);
var isOpera = !!window.opera || navigator.userAgent.indexOf('OPR/') !== -1;
var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1 && ('netscape' in window) && / rv:/.test(navigator.userAgent);
var isChrome = (!isOpera && !isEdge && !!navigator.webkitGetUserMedia) || isElectron() || navigator.userAgent.toLowerCase().indexOf('chrome/') !== -1;
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if (isSafari && !isChrome && navigator.userAgent.indexOf('CriOS') !== -1) {
isSafari = false;
isChrome = true;
}
var MediaStream = window.MediaStream;
if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') {
MediaStream = webkitMediaStream;
}
/*global MediaStream:true */
if (typeof MediaStream !== 'undefined') {
// override "stop" method for all browsers
if (typeof MediaStream.prototype.stop === 'undefined') {
MediaStream.prototype.stop = function() {
this.getTracks().forEach(function(track) {
track.stop();
});
};
}
}
// below function via: http://goo.gl/B3ae8c
/**
* Return human-readable file size.
* @param {number} bytes - Pass bytes and get formatted string.
* @returns {string} - formatted string
* @example
* bytesToSize(1024*1024*5) === '5 GB'
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
*/
function bytesToSize(bytes) {
var k = 1000;
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes === 0) {
return '0 Bytes';
}
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(k)), 10);
return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];
}
/**
* @param {Blob} file - File or Blob object. This parameter is required.
* @param {string} fileName - Optional file name e.g. "Recorded-Video.webm"
* @example
* invokeSaveAsDialog(blob or file, [optional] fileName);
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
*/
function invokeSaveAsDialog(file, fileName) {
if (!file) {
throw 'Blob object is required.';
}
if (!file.type) {
try {
file.type = 'video/webm';
} catch (e) {}
}
var fileExtension = (file.type || 'video/webm').split('/')[1];
if (fileExtension.indexOf(';') !== -1) {
// extended mimetype, e.g. 'video/webm;codecs=vp8,opus'
fileExtension = fileExtension.split(';')[0];
}
if (fileName && fileName.indexOf('.') !== -1) {
var splitted = fileName.split('.');
fileName = splitted[0];
fileExtension = splitted[1];
}
var fileFullName = (fileName || (Math.round(Math.random() * 9999999999) + 888888888)) + '.' + fileExtension;
if (typeof navigator.msSaveOrOpenBlob !== 'undefined') {
return navigator.msSaveOrOpenBlob(file, fileFullName);
} else if (typeof navigator.msSaveBlob !== 'undefined') {
return navigator.msSaveBlob(file, fileFullName);
}
var hyperlink = document.createElement('a');
hyperlink.href = URL.createObjectURL(file);
hyperlink.download = fileFullName;
hyperlink.style = 'display:none;opacity:0;color:transparent;';
(document.body || document.documentElement).appendChild(hyperlink);
if (typeof hyperlink.click === 'function') {
hyperlink.click();
} else {
hyperlink.target = '_blank';
hyperlink.dispatchEvent(new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
}));
}
URL.revokeObjectURL(hyperlink.href);
}
/**
* from: https://github.com/cheton/is-electron/blob/master/index.js
**/
function isElectron() {
// Renderer process
if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {
return true;
}
// Main process
if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) {
return true;
}
// Detect the user agent when the `nodeIntegration` option is set to true
if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {
return true;
}
return false;
}
function getTracks(stream, kind) {
if (!stream || !stream.getTracks) {
return [];
}
return stream.getTracks().filter(function(t) {
return t.kind === (kind || 'audio');
});
}
function setSrcObject(stream, element) {
if ('srcObject' in element) {
element.srcObject = stream;
} else if ('mozSrcObject' in element) {
element.mozSrcObject = stream;
} else {
element.srcObject = stream;
}
}
/**
* @param {Blob} file - File or Blob object.
* @param {function} callback - Callback function.
* @example
* getSeekableBlob(blob or file, callback);
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
*/
function getSeekableBlob(inputBlob, callback) {
// EBML.js copyrights goes to: https://github.com/legokichi/ts-ebml
if (typeof EBML === 'undefined') {
throw new Error('Please link: https://www.webrtc-experiment.com/EBML.js');
}
var reader = new EBML.Reader();
var decoder = new EBML.Decoder();
var tools = EBML.tools;
var fileReader = new FileReader();
fileReader.onload = function(e) {
var ebmlElms = decoder.decode(this.result);
ebmlElms.forEach(function(element) {
reader.read(element);
});
reader.stop();
var refinedMetadataBuf = tools.makeMetadataSeekable(reader.metadatas, reader.duration, reader.cues);
var body = this.result.slice(reader.metadataSize);
var newBlob = new Blob([refinedMetadataBuf, body], {
type: 'video/webm'
});
callback(newBlob);
};
fileReader.readAsArrayBuffer(inputBlob);
}
if (typeof RecordRTC !== 'undefined') {
RecordRTC.invokeSaveAsDialog = invokeSaveAsDialog;
RecordRTC.getTracks = getTracks;
RecordRTC.getSeekableBlob = getSeekableBlob;
RecordRTC.bytesToSize = bytesToSize;
RecordRTC.isElectron = isElectron;
}
// __________ (used to handle stuff like http://goo.gl/xmE5eg) issue #129
// Storage.js
/**
* Storage is a standalone object used by {@link RecordRTC} to store reusable objects e.g. "new AudioContext".
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @example
* Storage.AudioContext === webkitAudioContext
* @property {webkitAudioContext} AudioContext - Keeps a reference to AudioContext object.
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
*/
var Storage = {};
if (typeof AudioContext !== 'undefined') {
Storage.AudioContext = AudioContext;
} else if (typeof webkitAudioContext !== 'undefined') {
Storage.AudioContext = webkitAudioContext;
}
if (typeof RecordRTC !== 'undefined') {
RecordRTC.Storage = Storage;
}
function isMediaRecorderCompatible() {
if (isFirefox || isSafari || isEdge) {
return true;
}
var nVer = navigator.appVersion;
var nAgt = navigator.userAgent;
var fullVersion = '' + parseFloat(navigator.appVersion);
var majorVersion = parseInt(navigator.appVersion, 10);
var nameOffset, verOffset, ix;
if (isChrome || isOpera) {
verOffset = nAgt.indexOf('Chrome');
fullVersion = nAgt.substring(verOffset + 7);
}
// trim the fullVersion string at semicolon/space if present
if ((ix = fullVersion.indexOf(';')) !== -1) {
fullVersion = fullVersion.substring(0, ix);
}
if ((ix = fullVersion.indexOf(' ')) !== -1) {
fullVersion = fullVersion.substring(0, ix);
}
majorVersion = parseInt('' + fullVersion, 10);
if (isNaN(majorVersion)) {
fullVersion = '' + parseFloat(navigator.appVersion);
majorVersion = parseInt(navigator.appVersion, 10);
}
return majorVersion >= 49;
}
// ______________________
// MediaStreamRecorder.js
/**
* MediaStreamRecorder is an abstraction layer for {@link https://w3c.github.io/mediacapture-record/MediaRecorder.html|MediaRecorder API}. It is used by {@link RecordRTC} to record MediaStream(s) in both Chrome and Firefox.
* @summary Runs top over {@link https://w3c.github.io/mediacapture-record/MediaRecorder.html|MediaRecorder API}.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://github.com/muaz-khan|Muaz Khan}
* @typedef MediaStreamRecorder
* @class
* @example
* var config = {
* mimeType: 'video/webm', // vp8, vp9, h264, mkv, opus/vorbis
* audioBitsPerSecond : 256 * 8 * 1024,
* videoBitsPerSecond : 256 * 8 * 1024,
* bitsPerSecond: 256 * 8 * 1024, // if this is provided, skip above two
* checkForInactiveTracks: true,
* timeSlice: 1000, // concatenate intervals based blobs
* ondataavailable: function() {} // get intervals based blobs
* }
* var recorder = new MediaStreamRecorder(mediaStream, config);
* recorder.record();
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
*
* // or
* var blob = recorder.blob;
* });
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.
* @param {object} config - {disableLogs:true, initCallback: function, mimeType: "video/webm", timeSlice: 1000}
* @throws Will throw an error if first argument "MediaStream" is missing. Also throws error if "MediaRecorder API" are not supported by the browser.
*/
function MediaStreamRecorder(mediaStream, config) {
var self = this;
if (typeof mediaStream === 'undefined') {
throw 'First argument "MediaStream" is required.';
}
if (typeof MediaRecorder === 'undefined') {
throw 'Your browser does not support the Media Recorder API. Please try other modules e.g. WhammyRecorder or StereoAudioRecorder.';
}
config = config || {
// bitsPerSecond: 256 * 8 * 1024,
mimeType: 'video/webm'
};
if (config.type === 'audio') {
if (getTracks(mediaStream, 'video').length && getTracks(mediaStream, 'audio').length) {
var stream;
if (!!navigator.mozGetUserMedia) {
stream = new MediaStream();
stream.addTrack(getTracks(mediaStream, 'audio')[0]);
} else {
// webkitMediaStream
stream = new MediaStream(getTracks(mediaStream, 'audio'));
}
mediaStream = stream;
}
if (!config.mimeType || config.mimeType.toString().toLowerCase().indexOf('audio') === -1) {
config.mimeType = isChrome ? 'audio/webm' : 'audio/ogg';
}
if (config.mimeType && config.mimeType.toString().toLowerCase() !== 'audio/ogg' && !!navigator.mozGetUserMedia) {
// forcing better codecs on Firefox (via #166)
config.mimeType = 'audio/ogg';
}
}
var arrayOfBlobs = [];
/**
* This method returns array of blobs. Use only with "timeSlice". Its useful to preview recording anytime, without using the "stop" method.
* @method
* @memberof MediaStreamRecorder
* @example
* var arrayOfBlobs = recorder.getArrayOfBlobs();
* @returns {Array} Returns array of recorded blobs.
*/
this.getArrayOfBlobs = function() {
return arrayOfBlobs;
};
/**
* This method records MediaStream.
* @method
* @memberof MediaStreamRecorder
* @example
* recorder.record();
*/
this.record = function() {
// set defaults
self.blob = null;
self.clearRecordedData();
self.timestamps = [];
allStates = [];
arrayOfBlobs = [];
var recorderHints = config;
if (!config.disableLogs) {
console.log('Passing following config over MediaRecorder API.', recorderHints);
}
if (mediaRecorder) {
// mandatory to make sure Firefox doesn't fails to record streams 3-4 times without reloading the page.
mediaRecorder = null;
}
if (isChrome && !isMediaRecorderCompatible()) {
// to support video-only recording on stable
recorderHints = 'video/vp8';
}
if (typeof MediaRecorder.isTypeSupported === 'function' && recorderHints.mimeType) {
if (!MediaRecorder.isTypeSupported(recorderHints.mimeType)) {
if (!config.disableLogs) {
console.warn('MediaRecorder API seems unable to record mimeType:', recorderHints.mimeType);
}
recorderHints.mimeType = config.type === 'audio' ? 'audio/webm' : 'video/webm';
}
}
// using MediaRecorder API here
try {
mediaRecorder = new MediaRecorder(mediaStream, recorderHints);
// reset
config.mimeType = recorderHints.mimeType;
} catch (e) {
// chrome-based fallback
mediaRecorder = new MediaRecorder(mediaStream);
}
// old hack?
if (recorderHints.mimeType && !MediaRecorder.isTypeSupported && 'canRecordMimeType' in mediaRecorder && mediaRecorder.canRecordMimeType(recorderHints.mimeType) === false) {
if (!config.disableLogs) {
console.warn('MediaRecorder API seems unable to record mimeType:', recorderHints.mimeType);
}
}
// Dispatching OnDataAvailable Handler
mediaRecorder.ondataavailable = function(e) {
if (e.data) {
allStates.push('ondataavailable: ' + bytesToSize(e.data.size));
}
if (typeof config.timeSlice === 'number') {
if (e.data && e.data.size) {
arrayOfBlobs.push(e.data);
updateTimeStamp();
if (typeof config.ondataavailable === 'function') {
// intervals based blobs
var blob = config.getNativeBlob ? e.data : new Blob([e.data], {
type: getMimeType(recorderHints)
});
config.ondataavailable(blob);
}
}
return;
}
if (!e.data || !e.data.size || e.data.size < 100 || self.blob) {
// make sure that stopRecording always getting fired
// even if there is invalid data
if (self.recordingCallback) {
self.recordingCallback(new Blob([], {
type: getMimeType(recorderHints)
}));
self.recordingCallback = null;
}
return;
}
self.blob = config.getNativeBlob ? e.data : new Blob([e.data], {
type: getMimeType(recorderHints)
});
if (self.recordingCallback) {
self.recordingCallback(self.blob);
self.recordingCallback = null;
}
};
mediaRecorder.onstart = function() {
allStates.push('started');
};
mediaRecorder.onpause = function() {
allStates.push('paused');
};
mediaRecorder.onresume = function() {
allStates.push('resumed');
};
mediaRecorder.onstop = function() {
allStates.push('stopped');
};
mediaRecorder.onerror = function(error) {
if (!error) {
return;
}
if (!error.name) {
error.name = 'UnknownError';
}
allStates.push('error: ' + error);
if (!config.disableLogs) {
// via: https://w3c.github.io/mediacapture-record/MediaRecorder.html#exception-summary
if (error.name.toString().toLowerCase().indexOf('invalidstate') !== -1) {
console.error('The MediaRecorder is not in a state in which the proposed operation is allowed to be executed.', error);
} else if (error.name.toString().toLowerCase().indexOf('notsupported') !== -1) {
console.error('MIME type (', recorderHints.mimeType, ') is not supported.', error);
} else if (error.name.toString().toLowerCase().indexOf('security') !== -1) {
console.error('MediaRecorder security error', error);
}
// older code below
else if (error.name === 'OutOfMemory') {
console.error('The UA has exhaused the available memory. User agents SHOULD provide as much additional information as possible in the message attribute.', error);
} else if (error.name === 'IllegalStreamModification') {
console.error('A modification to the stream has occurred that makes it impossible to continue recording. An example would be the addition of a Track while recording is occurring. User agents SHOULD provide as much additional information as possible in the message attribute.', error);
} else if (error.name === 'OtherRecordingError') {
console.error('Used for an fatal error other than those listed above. User agents SHOULD provide as much additional information as possible in the message attribute.', error);
} else if (error.name === 'GenericError') {
console.error('The UA cannot provide the codec or recording option that has been requested.', error);
} else {
console.error('MediaRecorder Error', error);
}
}
(function(looper) {
if (!self.manuallyStopped && mediaRecorder && mediaRecorder.state === 'inactive') {
delete config.timeslice;
// 10 minutes, enough?
mediaRecorder.start(10 * 60 * 1000);
return;
}
setTimeout(looper, 1000);
})();
if (mediaRecorder.state !== 'inactive' && mediaRecorder.state !== 'stopped') {
mediaRecorder.stop();
}
};
if (typeof config.timeSlice === 'number') {
updateTimeStamp();
mediaRecorder.start(config.timeSlice);
} else {
// default is 60 minutes; enough?
// use config => {timeSlice: 1000} otherwise
mediaRecorder.start(3.6e+6);
}
if (config.initCallback) {
config.initCallback(); // old code
}
};
/**
* @property {Array} timestamps - Array of time stamps
* @memberof MediaStreamRecorder
* @example
* console.log(recorder.timestamps);
*/
this.timestamps = [];
function updateTimeStamp() {
self.timestamps.push(new Date().getTime());
if (typeof config.onTimeStamp === 'function') {
config.onTimeStamp(self.timestamps[self.timestamps.length - 1], self.timestamps);
}
}
function getMimeType(secondObject) {
if (mediaRecorder && mediaRecorder.mimeType) {
return mediaRecorder.mimeType;
}
return secondObject.mimeType || 'video/webm';
}
/**
* This method stops recording MediaStream.
* @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.
* @method
* @memberof MediaStreamRecorder
* @example
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
*/
this.stop = function(callback) {
callback = callback || function() {};
self.manuallyStopped = true; // used inside the mediaRecorder.onerror
if (!mediaRecorder) {
return;
}
this.recordingCallback = callback;
if (mediaRecorder.state === 'recording') {
mediaRecorder.stop();
}
if (typeof config.timeSlice === 'number') {
setTimeout(function() {
self.blob = new Blob(arrayOfBlobs, {
type: getMimeType(config)
});
self.recordingCallback(self.blob);
}, 100);
}
};
/**
* This method pauses the recording process.
* @method
* @memberof MediaStreamRecorder
* @example
* recorder.pause();
*/
this.pause = function() {
if (!mediaRecorder) {
return;
}
if (mediaRecorder.state === 'recording') {
mediaRecorder.pause();
}
};
/**
* This method resumes the recording process.
* @method
* @memberof MediaStreamRecorder
* @example
* recorder.resume();
*/
this.resume = function() {
if (!mediaRecorder) {
return;
}
if (mediaRecorder.state === 'paused') {
mediaRecorder.resume();
}
};
/**
* This method resets currently recorded data.
* @method
* @memberof MediaStreamRecorder
* @example
* recorder.clearRecordedData();
*/
this.clearRecordedData = function() {
if (mediaRecorder && mediaRecorder.state === 'recording') {
self.stop(clearRecordedDataCB);
}
clearRecordedDataCB();
};
function clearRecordedDataCB() {
arrayOfBlobs = [];
mediaRecorder = null;
self.timestamps = [];
}
// Reference to "MediaRecorder" object
var mediaRecorder;
/**
* Access to native MediaRecorder API
* @method
* @memberof MediaStreamRecorder
* @instance
* @example
* var internal = recorder.getInternalRecorder();
* internal.ondataavailable = function() {}; // override
* internal.stream, internal.onpause, internal.onstop, etc.
* @returns {Object} Returns internal recording object.
*/
this.getInternalRecorder = function() {
return mediaRecorder;
};
function isMediaStreamActive() {
if ('active' in mediaStream) {
if (!mediaStream.active) {
return false;
}
} else if ('ended' in mediaStream) { // old hack
if (mediaStream.ended) {
return false;
}
}
return true;
}
/**
* @property {Blob} blob - Recorded data as "Blob" object.
* @memberof MediaStreamRecorder
* @example
* recorder.stop(function() {
* var blob = recorder.blob;
* });
*/
this.blob = null;
/**
* Get MediaRecorder readonly state.
* @method
* @memberof MediaStreamRecorder
* @example
* var state = recorder.getState();
* @returns {String} Returns recording state.
*/
this.getState = function() {
if (!mediaRecorder) {
return 'inactive';
}
return mediaRecorder.state || 'inactive';
};
// list of all recording states
var allStates = [];
/**
* Get MediaRecorder all recording states.
* @method
* @memberof MediaStreamRecorder
* @example
* var state = recorder.getAllStates();
* @returns {Array} Returns all recording states
*/
this.getAllStates = function() {
return allStates;
};
// if any Track within the MediaStream is muted or not enabled at any time,
// the browser will only record black frames
// or silence since that is the content produced by the Track
// so we need to stopRecording as soon as any single track ends.
if (typeof config.checkForInactiveTracks === 'undefined') {
config.checkForInactiveTracks = false; // disable to minimize CPU usage
}
var self = this;
// this method checks if media stream is stopped
// or if any track is ended.
(function looper() {
if (!mediaRecorder || config.checkForInactiveTracks === false) {
return;
}
if (isMediaStreamActive() === false) {
if (!config.disableLogs) {
console.log('MediaStream seems stopped.');
}
self.stop();
return;
}
setTimeout(looper, 1000); // check every second
})();
// for debugging
this.name = 'MediaStreamRecorder';
this.toString = function() {
return this.name;
};
}
if (typeof RecordRTC !== 'undefined') {
RecordRTC.MediaStreamRecorder = MediaStreamRecorder;
}
// source code from: http://typedarray.org/wp-content/projects/WebAudioRecorder/script.js
// https://github.com/mattdiamond/Recorderjs#license-mit
// ______________________
// StereoAudioRecorder.js
/**
* StereoAudioRecorder is a standalone class used by {@link RecordRTC} to bring "stereo" audio-recording in chrome.
* @summary JavaScript standalone object for stereo audio recording.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef StereoAudioRecorder
* @class
* @example
* var recorder = new StereoAudioRecorder(MediaStream, {
* sampleRate: 44100,
* bufferSize: 4096
* });
* recorder.record();
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.
* @param {object} config - {sampleRate: 44100, bufferSize: 4096, numberOfAudioChannels: 1, etc.}
*/
function StereoAudioRecorder(mediaStream, config) {
if (!getTracks(mediaStream, 'audio').length) {
throw 'Your stream has no audio tracks.';
}
config = config || {};
var self = this;
// variables
var leftchannel = [];
var rightchannel = [];
var recording = false;
var recordingLength = 0;
var jsAudioNode;
var numberOfAudioChannels = 2;
/**
* Set sample rates such as 8K or 16K. Reference: http://stackoverflow.com/a/28977136/552182
* @property {number} desiredSampRate - Desired Bits per sample * 1000
* @memberof StereoAudioRecorder
* @instance
* @example
* var recorder = StereoAudioRecorder(mediaStream, {
* desiredSampRate: 16 * 1000 // bits-per-sample * 1000
* });
*/
var desiredSampRate = config.desiredSampRate;
// backward compatibility
if (config.leftChannel === true) {
numberOfAudioChannels = 1;
}
if (config.numberOfAudioChannels === 1) {
numberOfAudioChannels = 1;
}
if (!numberOfAudioChannels || numberOfAudioChannels < 1) {
numberOfAudioChannels = 2;
}
if (!config.disableLogs) {
console.log('StereoAudioRecorder is set to record number of channels: ' + numberOfAudioChannels);
}
// if any Track within the MediaStream is muted or not enabled at any time,
// the browser will only record black frames
// or silence since that is the content produced by the Track
// so we need to stopRecording as soon as any single track ends.
if (typeof config.checkForInactiveTracks === 'undefined') {
config.checkForInactiveTracks = true;
}
function isMediaStreamActive() {
if (config.checkForInactiveTracks === false) {
// always return "true"
return true;
}
if ('active' in mediaStream) {
if (!mediaStream.active) {
return false;
}
} else if ('ended' in mediaStream) { // old hack
if (mediaStream.ended) {
return false;
}
}
return true;
}
/**
* This method records MediaStream.
* @method
* @memberof StereoAudioRecorder
* @example
* recorder.record();
*/
this.record = function() {
if (isMediaStreamActive() === false) {
throw 'Please make sure MediaStream is active.';
}
resetVariables();
isAudioProcessStarted = isPaused = false;
recording = true;
if (typeof config.timeSlice !== 'undefined') {
looper();
}
};
function mergeLeftRightBuffers(config, callback) {
function mergeAudioBuffers(config, cb) {
var numberOfAudioChannels = config.numberOfAudioChannels;
// todo: "slice(0)" --- is it causes loop? Should be removed?
var leftBuffers = config.leftBuffers.slice(0);
var rightBuffers = config.rightBuffers.slice(0);
var sampleRate = config.sampleRate;
var internalInterleavedLength = config.internalInterleavedLength;
var desiredSampRate = config.desiredSampRate;
if (numberOfAudioChannels === 2) {
leftBuffers = mergeBuffers(leftBuffers, internalInterleavedLength);
rightBuffers = mergeBuffers(rightBuffers, internalInterleavedLength);
if (desiredSampRate) {
leftBuffers = interpolateArray(leftBuffers, desiredSampRate, sampleRate);
rightBuffers = interpolateArray(rightBuffers, desiredSampRate, sampleRate);
}
}
if (numberOfAudioChannels === 1) {
leftBuffers = mergeBuffers(leftBuffers, internalInterleavedLength);
if (desiredSampRate) {
leftBuffers = interpolateArray(leftBuffers, desiredSampRate, sampleRate);
}
}
// set sample rate as desired sample rate
if (desiredSampRate) {
sampleRate = desiredSampRate;
}
// for changing the sampling rate, reference:
// http://stackoverflow.com/a/28977136/552182
function interpolateArray(data, newSampleRate, oldSampleRate) {
var fitCount = Math.round(data.length * (newSampleRate / oldSampleRate));
var newData = [];
var springFactor = Number((data.length - 1) / (fitCount - 1));
newData[0] = data[0];
for (var i = 1; i < fitCount - 1; i++) {
var tmp = i * springFactor;
var before = Number(Math.floor(tmp)).toFixed();
var after = Number(Math.ceil(tmp)).toFixed();
var atPoint = tmp - before;
newData[i] = linearInterpolate(data[before], data[after], atPoint);
}
newData[fitCount - 1] = data[data.length - 1];
return newData;
}
function linearInterpolate(before, after, atPoint) {
return before + (after - before) * atPoint;
}
function mergeBuffers(channelBuffer, rLength) {
var result = new Float64Array(rLength);
var offset = 0;
var lng = channelBuffer.length;
for (var i = 0; i < lng; i++) {
var buffer = channelBuffer[i];
result.set(buffer, offset);
offset += buffer.length;
}
return result;
}
function interleave(leftChannel, rightChannel) {
var length = leftChannel.length + rightChannel.length;
var result = new Float64Array(length);
var inputIndex = 0;
for (var index = 0; index < length;) {
result[index++] = leftChannel[inputIndex];
result[index++] = rightChannel[inputIndex];
inputIndex++;
}
return result;
}
function writeUTFBytes(view, offset, string) {
var lng = string.length;
for (var i = 0; i < lng; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
}
// interleave both channels together
var interleaved;
if (numberOfAudioChannels === 2) {
interleaved = interleave(leftBuffers, rightBuffers);
}
if (numberOfAudioChannels === 1) {
interleaved = leftBuffers;
}
var interleavedLength = interleaved.length;
// create wav file
var resultingBufferLength = 44 + interleavedLength * 2;
var buffer = new ArrayBuffer(resultingBufferLength);
var view = new DataView(buffer);
// RIFF chunk descriptor/identifier
writeUTFBytes(view, 0, 'RIFF');
// RIFF chunk length
// changed "44" to "36" via #401
view.setUint32(4, 36 + interleavedLength * 2, true);
// RIFF type
writeUTFBytes(view, 8, 'WAVE');
// format chunk identifier
// FMT sub-chunk
writeUTFBytes(view, 12, 'fmt ');
// format chunk length
view.setUint32(16, 16, true);
// sample format (raw)
view.setUint16(20, 1, true);
// stereo (2 channels)
view.setUint16(22, numberOfAudioChannels, true);
// sample rate
view.setUint32(24, sampleRate, true);
// byte rate (sample rate * block align)
view.setUint32(28, sampleRate * numberOfAudioChannels * 2, true);
// block align (channel count * bytes per sample)
view.setUint16(32, numberOfAudioChannels * 2, true);
// bits per sample
view.setUint16(34, 16, true);
// data sub-chunk
// data chunk identifier
writeUTFBytes(view, 36, 'data');
// data chunk length
view.setUint32(40, interleavedLength * 2, true);
// write the PCM samples
var lng = interleavedLength;
var index = 44;
var volume = 1;
for (var i = 0; i < lng; i++) {
view.setInt16(index, interleaved[i] * (0x7FFF * volume), true);
index += 2;
}
if (cb) {
return cb({
buffer: buffer,
view: view
});
}
postMessage({
buffer: buffer,
view: view
});
}
if (config.noWorker) {
mergeAudioBuffers(config, function(data) {
callback(data.buffer, data.view);
});
return;
}
var webWorker = processInWebWorker(mergeAudioBuffers);
webWorker.onmessage = function(event) {
callback(event.data.buffer, event.data.view);
// release memory
URL.revokeObjectURL(webWorker.workerURL);
// kill webworker (or Chrome will kill your page after ~25 calls)
webWorker.terminate();
};
webWorker.postMessage(config);
}
function processInWebWorker(_function) {
var workerURL = URL.createObjectURL(new Blob([_function.toString(),
';this.onmessage = function (eee) {' + _function.name + '(eee.data);}'
], {
type: 'application/javascript'
}));
var worker = new Worker(workerURL);
worker.workerURL = workerURL;
return worker;
}
/**
* This method stops recording MediaStream.
* @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.
* @method
* @memberof StereoAudioRecorder
* @example
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
*/
this.stop = function(callback) {
callback = callback || function() {};
// stop recording
recording = false;
mergeLeftRightBuffers({
desiredSampRate: desiredSampRate,
sampleRate: sampleRate,
numberOfAudioChannels: numberOfAudioChannels,
internalInterleavedLength: recordingLength,
leftBuffers: leftchannel,
rightBuffers: numberOfAudioChannels === 1 ? [] : rightchannel,
noWorker: config.noWorker
}, function(buffer, view) {
/**
* @property {Blob} blob - The recorded blob object.
* @memberof StereoAudioRecorder
* @example
* recorder.stop(function(){
* var blob = recorder.blob;
* });
*/
self.blob = new Blob([view], {
type: 'audio/wav'
});
/**
* @property {ArrayBuffer} buffer - The recorded buffer object.
* @memberof StereoAudioRecorder
* @example
* recorder.stop(function(){
* var buffer = recorder.buffer;
* });
*/
self.buffer = new ArrayBuffer(view.buffer.byteLength);
/**
* @property {DataView} view - The recorded data-view object.
* @memberof StereoAudioRecorder
* @example
* recorder.stop(function(){
* var view = recorder.view;
* });
*/
self.view = view;
self.sampleRate = desiredSampRate || sampleRate;
self.bufferSize = bufferSize;
// recorded audio length
self.length = recordingLength;
isAudioProcessStarted = false;
if (callback) {
callback(self.blob);
}
});
};
if (typeof RecordRTC.Storage === 'undefined') {
RecordRTC.Storage = {
AudioContextConstructor: null,
AudioContext: window.AudioContext || window.webkitAudioContext
};
}
if (!RecordRTC.Storage.AudioContextConstructor || RecordRTC.Storage.AudioContextConstructor.state === 'closed') {
RecordRTC.Storage.AudioContextConstructor = new RecordRTC.Storage.AudioContext();
}
var context = RecordRTC.Storage.AudioContextConstructor;
// creates an audio node from the microphone incoming stream
var audioInput = context.createMediaStreamSource(mediaStream);
var legalBufferValues = [0, 256, 512, 1024, 2048, 4096, 8192, 16384];
/**
* From the spec: This value controls how frequently the audioprocess event is
* dispatched and how many sample-frames need to be processed each call.
* Lower values for buffer size will result in a lower (better) latency.
* Higher values will be necessary to avoid audio breakup and glitches
* The size of the buffer (in sample-frames) which needs to
* be processed each time onprocessaudio is called.
* Legal values are (256, 512, 1024, 2048, 4096, 8192, 16384).
* @property {number} bufferSize - Buffer-size for how frequently the audioprocess event is dispatched.
* @memberof StereoAudioRecorder
* @example
* recorder = new StereoAudioRecorder(mediaStream, {
* bufferSize: 4096
* });
*/
// "0" means, let chrome decide the most accurate buffer-size for current platform.
var bufferSize = typeof config.bufferSize === 'undefined' ? 4096 : config.bufferSize;
if (legalBufferValues.indexOf(bufferSize) === -1) {
if (!config.disableLogs) {
console.log('Legal values for buffer-size are ' + JSON.stringify(legalBufferValues, null, '\t'));
}
}
if (context.createJavaScriptNode) {
jsAudioNode = context.createJavaScriptNode(bufferSize, numberOfAudioChannels, numberOfAudioChannels);
} else if (context.createScriptProcessor) {
jsAudioNode = context.createScriptProcessor(bufferSize, numberOfAudioChannels, numberOfAudioChannels);
} else {
throw 'WebAudio API has no support on this browser.';
}
// connect the stream to the script processor
audioInput.connect(jsAudioNode);
if (!config.bufferSize) {
bufferSize = jsAudioNode.bufferSize; // device buffer-size
}
/**
* The sample rate (in sample-frames per second) at which the
* AudioContext handles audio. It is assumed that all AudioNodes
* in the context run at this rate. In making this assumption,
* sample-rate converters or "varispeed" processors are not supported
* in real-time processing.
* The sampleRate parameter describes the sample-rate of the
* linear PCM audio data in the buffer in sample-frames per second.
* An implementation must support sample-rates in at least
* the range 22050 to 96000.
* @property {number} sampleRate - Buffer-size for how frequently the audioprocess event is dispatched.
* @memberof StereoAudioRecorder
* @example
* recorder = new StereoAudioRecorder(mediaStream, {
* sampleRate: 44100
* });
*/
var sampleRate = typeof config.sampleRate !== 'undefined' ? config.sampleRate : context.sampleRate || 44100;
if (sampleRate < 22050 || sampleRate > 96000) {
// Ref: http://stackoverflow.com/a/26303918/552182
if (!config.disableLogs) {
console.log('sample-rate must be under range 22050 and 96000.');
}
}
if (!config.disableLogs) {
if (config.desiredSampRate) {
console.log('Desired sample-rate: ' + config.desiredSampRate);
}
}
var isPaused = false;
/**
* This method pauses the recording process.
* @method
* @memberof StereoAudioRecorder
* @example
* recorder.pause();
*/
this.pause = function() {
isPaused = true;
};
/**
* This method resumes the recording process.
* @method
* @memberof StereoAudioRecorder
* @example
* recorder.resume();
*/
this.resume = function() {
if (isMediaStreamActive() === false) {
throw 'Please make sure MediaStream is active.';
}
if (!recording) {
if (!config.disableLogs) {
console.log('Seems recording has been restarted.');
}
this.record();
return;
}
isPaused = false;
};
/**
* This method resets currently recorded data.
* @method
* @memberof StereoAudioRecorder
* @example
* recorder.clearRecordedData();
*/
this.clearRecordedData = function() {
config.checkForInactiveTracks = false;
if (recording) {
this.stop(clearRecordedDataCB);
}
clearRecordedDataCB();
};
function resetVariables() {
leftchannel = [];
rightchannel = [];
recordingLength = 0;
isAudioProcessStarted = false;
recording = false;
isPaused = false;
context = null;
self.leftchannel = leftchannel;
self.rightchannel = rightchannel;
self.numberOfAudioChannels = numberOfAudioChannels;
self.desiredSampRate = desiredSampRate;
self.sampleRate = sampleRate;
self.recordingLength = recordingLength;
intervalsBasedBuffers = {
left: [],
right: [],
recordingLength: 0
};
}
function clearRecordedDataCB() {
if (jsAudioNode) {
jsAudioNode.onaudioprocess = null;
jsAudioNode.disconnect();
jsAudioNode = null;
}
if (audioInput) {
audioInput.disconnect();
audioInput = null;
}
resetVariables();
}
// for debugging
this.name = 'StereoAudioRecorder';
this.toString = function() {
return this.name;
};
var isAudioProcessStarted = false;
function onAudioProcessDataAvailable(e) {
if (isPaused) {
return;
}
if (isMediaStreamActive() === false) {
if (!config.disableLogs) {
console.log('MediaStream seems stopped.');
}
jsAudioNode.disconnect();
recording = false;
}
if (!recording) {
if (audioInput) {
audioInput.disconnect();
audioInput = null;
}
return;
}
/**
* This method is called on "onaudioprocess" event's first invocation.
* @method {function} onAudioProcessStarted
* @memberof StereoAudioRecorder
* @example
* recorder.onAudioProcessStarted: function() { };
*/
if (!isAudioProcessStarted) {
isAudioProcessStarted = true;
if (config.onAudioProcessStarted) {
config.onAudioProcessStarted();
}
if (config.initCallback) {
config.initCallback();
}
}
var left = e.inputBuffer.getChannelData(0);
// we clone the samples
var chLeft = new Float32Array(left);
leftchannel.push(chLeft);
if (numberOfAudioChannels === 2) {
var right = e.inputBuffer.getChannelData(1);
var chRight = new Float32Array(right);
rightchannel.push(chRight);
}
recordingLength += bufferSize;
// export raw PCM
self.recordingLength = recordingLength;
if (typeof config.timeSlice !== 'undefined') {
intervalsBasedBuffers.recordingLength += bufferSize;
intervalsBasedBuffers.left.push(chLeft);
if (numberOfAudioChannels === 2) {
intervalsBasedBuffers.right.push(chRight);
}
}
}
jsAudioNode.onaudioprocess = onAudioProcessDataAvailable;
// to prevent self audio to be connected with speakers
if (context.createMediaStreamDestination) {
jsAudioNode.connect(context.createMediaStreamDestination());
} else {
jsAudioNode.connect(context.destination);
}
// export raw PCM
this.leftchannel = leftchannel;
this.rightchannel = rightchannel;
this.numberOfAudioChannels = numberOfAudioChannels;
this.desiredSampRate = desiredSampRate;
this.sampleRate = sampleRate;
self.recordingLength = recordingLength;
// helper for intervals based blobs
var intervalsBasedBuffers = {
left: [],
right: [],
recordingLength: 0
};
// this looper is used to support intervals based blobs (via timeSlice+ondataavailable)
function looper() {
if (!recording || typeof config.ondataavailable !== 'function' || typeof config.timeSlice === 'undefined') {
return;
}
if (intervalsBasedBuffers.left.length) {
mergeLeftRightBuffers({
desiredSampRate: desiredSampRate,
sampleRate: sampleRate,
numberOfAudioChannels: numberOfAudioChannels,
internalInterleavedLength: intervalsBasedBuffers.recordingLength,
leftBuffers: intervalsBasedBuffers.left,
rightBuffers: numberOfAudioChannels === 1 ? [] : intervalsBasedBuffers.right
}, function(buffer, view) {
var blob = new Blob([view], {
type: 'audio/wav'
});
config.ondataavailable(blob);
setTimeout(looper, config.timeSlice);
});
intervalsBasedBuffers = {
left: [],
right: [],
recordingLength: 0
};
} else {
setTimeout(looper, config.timeSlice);
}
}
}
if (typeof RecordRTC !== 'undefined') {
RecordRTC.StereoAudioRecorder = StereoAudioRecorder;
}
// _________________
// CanvasRecorder.js
/**
* CanvasRecorder is a standalone class used by {@link RecordRTC} to bring HTML5-Canvas recording into video WebM. It uses HTML2Canvas library and runs top over {@link Whammy}.
* @summary HTML2Canvas recording into video WebM.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef CanvasRecorder
* @class
* @example
* var recorder = new CanvasRecorder(htmlElement, { disableLogs: true, useWhammyRecorder: true });
* recorder.record();
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {HTMLElement} htmlElement - querySelector/getElementById/getElementsByTagName[0]/etc.
* @param {object} config - {disableLogs:true, initCallback: function}
*/
function CanvasRecorder(htmlElement, config) {
if (typeof html2canvas === 'undefined') {
throw 'Please link: https://www.webrtc-experiment.com/screenshot.js';
}
config = config || {};
if (!config.frameInterval) {
config.frameInterval = 10;
}
// via DetectRTC.js
var isCanvasSupportsStreamCapturing = false;
['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) {
if (item in document.createElement('canvas')) {
isCanvasSupportsStreamCapturing = true;
}
});
var _isChrome = (!!window.webkitRTCPeerConnection || !!window.webkitGetUserMedia) && !!window.chrome;
var chromeVersion = 50;
var matchArray = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
if (_isChrome && matchArray && matchArray[2]) {
chromeVersion = parseInt(matchArray[2], 10);
}
if (_isChrome && chromeVersion < 52) {
isCanvasSupportsStreamCapturing = false;
}
if (config.useWhammyRecorder) {
isCanvasSupportsStreamCapturing = false;
}
var globalCanvas, mediaStreamRecorder;
if (isCanvasSupportsStreamCapturing) {
if (!config.disableLogs) {
console.log('Your browser supports both MediRecorder API and canvas.captureStream!');
}
if (htmlElement instanceof HTMLCanvasElement) {
globalCanvas = htmlElement;
} else if (htmlElement instanceof CanvasRenderingContext2D) {
globalCanvas = htmlElement.canvas;
} else {
throw 'Please pass either HTMLCanvasElement or CanvasRenderingContext2D.';
}
} else if (!!navigator.mozGetUserMedia) {
if (!config.disableLogs) {
console.error('Canvas recording is NOT supported in Firefox.');
}
}
var isRecording;
/**
* This method records Canvas.
* @method
* @memberof CanvasRecorder
* @example
* recorder.record();
*/
this.record = function() {
isRecording = true;
if (isCanvasSupportsStreamCapturing && !config.useWhammyRecorder) {
// CanvasCaptureMediaStream
var canvasMediaStream;
if ('captureStream' in globalCanvas) {
canvasMediaStream = globalCanvas.captureStream(25); // 25 FPS
} else if ('mozCaptureStream' in globalCanvas) {
canvasMediaStream = globalCanvas.mozCaptureStream(25);
} else if ('webkitCaptureStream' in globalCanvas) {
canvasMediaStream = globalCanvas.webkitCaptureStream(25);
}
try {
var mdStream = new MediaStream();
mdStream.addTrack(getTracks(canvasMediaStream, 'video')[0]);
canvasMediaStream = mdStream;
} catch (e) {}
if (!canvasMediaStream) {
throw 'captureStream API are NOT available.';
}
// Note: Jan 18, 2016 status is that,
// Firefox MediaRecorder API can't record CanvasCaptureMediaStream object.
mediaStreamRecorder = new MediaStreamRecorder(canvasMediaStream, {
mimeType: config.mimeType || 'video/webm'
});
mediaStreamRecorder.record();
} else {
whammy.frames = [];
lastTime = new Date().getTime();
drawCanvasFrame();
}
if (config.initCallback) {
config.initCallback();
}
};
this.getWebPImages = function(callback) {
if (htmlElement.nodeName.toLowerCase() !== 'canvas') {
callback();
return;
}
var framesLength = whammy.frames.length;
whammy.frames.forEach(function(frame, idx) {
var framesRemaining = framesLength - idx;
if (!config.disableLogs) {
console.log(framesRemaining + '/' + framesLength + ' frames remaining');
}
if (config.onEncodingCallback) {
config.onEncodingCallback(framesRemaining, framesLength);
}
var webp = frame.image.toDataURL('image/webp', 1);
whammy.frames[idx].image = webp;
});
if (!config.disableLogs) {
console.log('Generating WebM');
}
callback();
};
/**
* This method stops recording Canvas.
* @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.
* @method
* @memberof CanvasRecorder
* @example
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
*/
this.stop = function(callback) {
isRecording = false;
var that = this;
if (isCanvasSupportsStreamCapturing && mediaStreamRecorder) {
mediaStreamRecorder.stop(callback);
return;
}
this.getWebPImages(function() {
/**
* @property {Blob} blob - Recorded frames in video/webm blob.
* @memberof CanvasRecorder
* @example
* recorder.stop(function() {
* var blob = recorder.blob;
* });
*/
whammy.compile(function(blob) {
if (!config.disableLogs) {
console.log('Recording finished!');
}
that.blob = blob;
if (that.blob.forEach) {
that.blob = new Blob([], {
type: 'video/webm'
});
}
if (callback) {
callback(that.blob);
}
whammy.frames = [];
});
});
};
var isPausedRecording = false;
/**
* This method pauses the recording process.
* @method
* @memberof CanvasRecorder
* @example
* recorder.pause();
*/
this.pause = function() {
isPausedRecording = true;
if (mediaStreamRecorder instanceof MediaStreamRecorder) {
mediaStreamRecorder.pause();
return;
}
};
/**
* This method resumes the recording process.
* @method
* @memberof CanvasRecorder
* @example
* recorder.resume();
*/
this.resume = function() {
isPausedRecording = false;
if (mediaStreamRecorder instanceof MediaStreamRecorder) {
mediaStreamRecorder.resume();
return;
}
if (!isRecording) {
this.record();
}
};
/**
* This method resets currently recorded data.
* @method
* @memberof CanvasRecorder
* @example
* recorder.clearRecordedData();
*/
this.clearRecordedData = function() {
if (isRecording) {
this.stop(clearRecordedDataCB);
}
clearRecordedDataCB();
};
function clearRecordedDataCB() {
whammy.frames = [];
isRecording = false;
isPausedRecording = false;
}
// for debugging
this.name = 'CanvasRecorder';
this.toString = function() {
return this.name;
};
function cloneCanvas() {
//create a new canvas
var newCanvas = document.createElement('canvas');
var context = newCanvas.getContext('2d');
//set dimensions
newCanvas.width = htmlElement.width;
newCanvas.height = htmlElement.height;
//apply the old canvas to the new one
context.drawImage(htmlElement, 0, 0);
//return the new canvas
return newCanvas;
}
function drawCanvasFrame() {
if (isPausedRecording) {
lastTime = new Date().getTime();
return setTimeout(drawCanvasFrame, 500);
}
if (htmlElement.nodeName.toLowerCase() === 'canvas') {
var duration = new Date().getTime() - lastTime;
// via #206, by Jack i.e. @Seymourr
lastTime = new Date().getTime();
whammy.frames.push({
image: cloneCanvas(),
duration: duration
});
if (isRecording) {
setTimeout(drawCanvasFrame, config.frameInterval);
}
return;
}
html2canvas(htmlElement, {
grabMouse: typeof config.showMousePointer === 'undefined' || config.showMousePointer,
onrendered: function(canvas) {
var duration = new Date().getTime() - lastTime;
if (!duration) {
return setTimeout(drawCanvasFrame, config.frameInterval);
}
// via #206, by Jack i.e. @Seymourr
lastTime = new Date().getTime();
whammy.frames.push({
image: canvas.toDataURL('image/webp', 1),
duration: duration
});
if (isRecording) {
setTimeout(drawCanvasFrame, config.frameInterval);
}
}
});
}
var lastTime = new Date().getTime();
var whammy = new Whammy.Video(100);
}
if (typeof RecordRTC !== 'undefined') {
RecordRTC.CanvasRecorder = CanvasRecorder;
}
// _________________
// WhammyRecorder.js
/**
* WhammyRecorder is a standalone class used by {@link RecordRTC} to bring video recording in Chrome. It runs top over {@link Whammy}.
* @summary Video recording feature in Chrome.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef WhammyRecorder
* @class
* @example
* var recorder = new WhammyRecorder(mediaStream);
* recorder.record();
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.
* @param {object} config - {disableLogs: true, initCallback: function, video: HTMLVideoElement, etc.}
*/
function WhammyRecorder(mediaStream, config) {
config = config || {};
if (!config.frameInterval) {
config.frameInterval = 10;
}
if (!config.disableLogs) {
console.log('Using frames-interval:', config.frameInterval);
}
/**
* This method records video.
* @method
* @memberof WhammyRecorder
* @example
* recorder.record();
*/
this.record = function() {
if (!config.width) {
config.width = 320;
}
if (!config.height) {
config.height = 240;
}
if (!config.video) {
config.video = {
width: config.width,
height: config.height
};
}
if (!config.canvas) {
config.canvas = {
width: config.width,
height: config.height
};
}
canvas.width = config.canvas.width || 320;
canvas.height = config.canvas.height || 240;
context = canvas.getContext('2d');
// setting defaults
if (config.video && config.video instanceof HTMLVideoElement) {
video = config.video.cloneNode();
if (config.initCallback) {
config.initCallback();
}
} else {
video = document.createElement('video');
setSrcObject(mediaStream, video);
video.onloadedmetadata = function() { // "onloadedmetadata" may NOT work in FF?
if (config.initCallback) {
config.initCallback();
}
};
video.width = config.video.width;
video.height = config.video.height;
}
video.muted = true;
video.play();
lastTime = new Date().getTime();
whammy = new Whammy.Video();
if (!config.disableLogs) {
console.log('canvas resolutions', canvas.width, '*', canvas.height);
console.log('video width/height', video.width || canvas.width, '*', video.height || canvas.height);
}
drawFrames(config.frameInterval);
};
/**
* Draw and push frames to Whammy
* @param {integer} frameInterval - set minimum interval (in milliseconds) between each time we push a frame to Whammy
*/
function drawFrames(frameInterval) {
frameInterval = typeof frameInterval !== 'undefined' ? frameInterval : 10;
var duration = new Date().getTime() - lastTime;
if (!duration) {
return setTimeout(drawFrames, frameInterval, frameInterval);
}
if (isPausedRecording) {
lastTime = new Date().getTime();
return setTimeout(drawFrames, 100);
}
// via #206, by Jack i.e. @Seymourr
lastTime = new Date().getTime();
if (video.paused) {
// via: https://github.com/muaz-khan/WebRTC-Experiment/pull/316
// Tweak for Android Chrome
video.play();
}
context.drawImage(video, 0, 0, canvas.width, canvas.height);
whammy.frames.push({
duration: duration,
image: canvas.toDataURL('image/webp')
});
if (!isStopDrawing) {
setTimeout(drawFrames, frameInterval, frameInterval);
}
}
function asyncLoop(o) {
var i = -1,
length = o.length;
(function loop() {
i++;
if (i === length) {
o.callback();
return;
}
// "setTimeout" added by Jim McLeod
setTimeout(function() {
o.functionToLoop(loop, i);
}, 1);
})();
}
/**
* remove black frames from the beginning to the specified frame
* @param {Array} _frames - array of frames to be checked
* @param {number} _framesToCheck - number of frame until check will be executed (-1 - will drop all frames until frame not matched will be found)
* @param {number} _pixTolerance - 0 - very strict (only black pixel color) ; 1 - all
* @param {number} _frameTolerance - 0 - very strict (only black frame color) ; 1 - all
* @returns {Array} - array of frames
*/
// pull#293 by @volodalexey
function dropBlackFrames(_frames, _framesToCheck, _pixTolerance, _frameTolerance, callback) {
var localCanvas = document.createElement('canvas');
localCanvas.width = canvas.width;
localCanvas.height = canvas.height;
var context2d = localCanvas.getContext('2d');
var resultFrames = [];
var checkUntilNotBlack = _framesToCheck === -1;
var endCheckFrame = (_framesToCheck && _framesToCheck > 0 && _framesToCheck <= _frames.length) ?
_framesToCheck : _frames.length;
var sampleColor = {
r: 0,
g: 0,
b: 0
};
var maxColorDifference = Math.sqrt(
Math.pow(255, 2) +
Math.pow(255, 2) +
Math.pow(255, 2)
);
var pixTolerance = _pixTolerance && _pixTolerance >= 0 && _pixTolerance <= 1 ? _pixTolerance : 0;
var frameTolerance = _frameTolerance && _frameTolerance >= 0 && _frameTolerance <= 1 ? _frameTolerance : 0;
var doNotCheckNext = false;
asyncLoop({
length: endCheckFrame,
functionToLoop: function(loop, f) {
var matchPixCount, endPixCheck, maxPixCount;
var finishImage = function() {
if (!doNotCheckNext && maxPixCount - matchPixCount <= maxPixCount * frameTolerance) {
// console.log('removed black frame : ' + f + ' ; frame duration ' + _frames[f].duration);
} else {
// console.log('frame is passed : ' + f);
if (checkUntilNotBlack) {
doNotCheckNext = true;
}
resultFrames.push(_frames[f]);
}
loop();
};
if (!doNotCheckNext) {
var image = new Image();
image.onload = function() {
context2d.drawImage(image, 0, 0, canvas.width, canvas.height);
var imageData = context2d.getImageData(0, 0, canvas.width, canvas.height);
matchPixCount = 0;
endPixCheck = imageData.data.length;
maxPixCount = imageData.data.length / 4;
for (var pix = 0; pix < endPixCheck; pix += 4) {
var currentColor = {
r: imageData.data[pix],
g: imageData.data[pix + 1],
b: imageData.data[pix + 2]
};
var colorDifference = Math.sqrt(
Math.pow(currentColor.r - sampleColor.r, 2) +
Math.pow(currentColor.g - sampleColor.g, 2) +
Math.pow(currentColor.b - sampleColor.b, 2)
);
// difference in color it is difference in color vectors (r1,g1,b1) <=> (r2,g2,b2)
if (colorDifference <= maxColorDifference * pixTolerance) {
matchPixCount++;
}
}
finishImage();
};
image.src = _frames[f].image;
} else {
finishImage();
}
},
callback: function() {
resultFrames = resultFrames.concat(_frames.slice(endCheckFrame));
if (resultFrames.length <= 0) {
// at least one last frame should be available for next manipulation
// if total duration of all frames will be < 1000 than ffmpeg doesn't work well...
resultFrames.push(_frames[_frames.length - 1]);
}
callback(resultFrames);
}
});
}
var isStopDrawing = false;
/**
* This method stops recording video.
* @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.
* @method
* @memberof WhammyRecorder
* @example
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
*/
this.stop = function(callback) {
callback = callback || function() {};
isStopDrawing = true;
var _this = this;
// analyse of all frames takes some time!
setTimeout(function() {
// e.g. dropBlackFrames(frames, 10, 1, 1) - will cut all 10 frames
// e.g. dropBlackFrames(frames, 10, 0.5, 0.5) - will analyse 10 frames
// e.g. dropBlackFrames(frames, 10) === dropBlackFrames(frames, 10, 0, 0) - will analyse 10 frames with strict black color
dropBlackFrames(whammy.frames, -1, null, null, function(frames) {
whammy.frames = frames;
// to display advertisement images!
if (config.advertisement && config.advertisement.length) {
whammy.frames = config.advertisement.concat(whammy.frames);
}
/**
* @property {Blob} blob - Recorded frames in video/webm blob.
* @memberof WhammyRecorder
* @example
* recorder.stop(function() {
* var blob = recorder.blob;
* });
*/
whammy.compile(function(blob) {
_this.blob = blob;
if (_this.blob.forEach) {
_this.blob = new Blob([], {
type: 'video/webm'
});
}
if (callback) {
callback(_this.blob);
}
});
});
}, 10);
};
var isPausedRecording = false;
/**
* This method pauses the recording process.
* @method
* @memberof WhammyRecorder
* @example
* recorder.pause();
*/
this.pause = function() {
isPausedRecording = true;
};
/**
* This method resumes the recording process.
* @method
* @memberof WhammyRecorder
* @example
* recorder.resume();
*/
this.resume = function() {
isPausedRecording = false;
if (isStopDrawing) {
this.record();
}
};
/**
* This method resets currently recorded data.
* @method
* @memberof WhammyRecorder
* @example
* recorder.clearRecordedData();
*/
this.clearRecordedData = function() {
if (!isStopDrawing) {
this.stop(clearRecordedDataCB);
}
clearRecordedDataCB();
};
function clearRecordedDataCB() {
whammy.frames = [];
isStopDrawing = true;
isPausedRecording = false;
}
// for debugging
this.name = 'WhammyRecorder';
this.toString = function() {
return this.name;
};
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var video;
var lastTime;
var whammy;
}
if (typeof RecordRTC !== 'undefined') {
RecordRTC.WhammyRecorder = WhammyRecorder;
}
// https://github.com/antimatter15/whammy/blob/master/LICENSE
// _________
// Whammy.js
// todo: Firefox now supports webp for webm containers!
// their MediaRecorder implementation works well!
// should we provide an option to record via Whammy.js or MediaRecorder API is a better solution?
/**
* Whammy is a standalone class used by {@link RecordRTC} to bring video recording in Chrome. It is written by {@link https://github.com/antimatter15|antimatter15}
* @summary A real time javascript webm encoder based on a canvas hack.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef Whammy
* @class
* @example
* var recorder = new Whammy().Video(15);
* recorder.add(context || canvas || dataURL);
* var output = recorder.compile();
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
*/
var Whammy = (function() {
// a more abstract-ish API
function WhammyVideo(duration) {
this.frames = [];
this.duration = duration || 1;
this.quality = 0.8;
}
/**
* Pass Canvas or Context or image/webp(string) to {@link Whammy} encoder.
* @method
* @memberof Whammy
* @example
* recorder = new Whammy().Video(0.8, 100);
* recorder.add(canvas || context || 'image/webp');
* @param {string} frame - Canvas || Context || image/webp
* @param {number} duration - Stick a duration (in milliseconds)
*/
WhammyVideo.prototype.add = function(frame, duration) {
if ('canvas' in frame) { //CanvasRenderingContext2D
frame = frame.canvas;
}
if ('toDataURL' in frame) {
frame = frame.toDataURL('image/webp', this.quality);
}
if (!(/^data:image\/webp;base64,/ig).test(frame)) {
throw 'Input must be formatted properly as a base64 encoded DataURI of type image/webp';
}
this.frames.push({
image: frame,
duration: duration || this.duration
});
};
function processInWebWorker(_function) {
var blob = URL.createObjectURL(new Blob([_function.toString(),
'this.onmessage = function (eee) {' + _function.name + '(eee.data);}'
], {
type: 'application/javascript'
}));
var worker = new Worker(blob);
URL.revokeObjectURL(blob);
return worker;
}
function whammyInWebWorker(frames) {
function ArrayToWebM(frames) {
var info = checkFrames(frames);
if (!info) {
return [];
}
var clusterMaxDuration = 30000;
var EBML = [{
'id': 0x1a45dfa3, // EBML
'data': [{
'data': 1,
'id': 0x4286 // EBMLVersion
}, {
'data': 1,
'id': 0x42f7 // EBMLReadVersion
}, {
'data': 4,
'id': 0x42f2 // EBMLMaxIDLength
}, {
'data': 8,
'id': 0x42f3 // EBMLMaxSizeLength
}, {
'data': 'webm',
'id': 0x4282 // DocType
}, {
'data': 2,
'id': 0x4287 // DocTypeVersion
}, {
'data': 2,
'id': 0x4285 // DocTypeReadVersion
}]
}, {
'id': 0x18538067, // Segment
'data': [{
'id': 0x1549a966, // Info
'data': [{
'data': 1e6, //do things in millisecs (num of nanosecs for duration scale)
'id': 0x2ad7b1 // TimecodeScale
}, {
'data': 'whammy',
'id': 0x4d80 // MuxingApp
}, {
'data': 'whammy',
'id': 0x5741 // WritingApp
}, {
'data': doubleToString(info.duration),
'id': 0x4489 // Duration
}]
}, {
'id': 0x1654ae6b, // Tracks
'data': [{
'id': 0xae, // TrackEntry
'data': [{
'data': 1,
'id': 0xd7 // TrackNumber
}, {
'data': 1,
'id': 0x73c5 // TrackUID
}, {
'data': 0,
'id': 0x9c // FlagLacing
}, {
'data': 'und',
'id': 0x22b59c // Language
}, {
'data': 'V_VP8',
'id': 0x86 // CodecID
}, {
'data': 'VP8',
'id': 0x258688 // CodecName
}, {
'data': 1,
'id': 0x83 // TrackType
}, {
'id': 0xe0, // Video
'data': [{
'data': info.width,
'id': 0xb0 // PixelWidth
}, {
'data': info.height,
'id': 0xba // PixelHeight
}]
}]
}]
}]
}];
//Generate clusters (max duration)
var frameNumber = 0;
var clusterTimecode = 0;
while (frameNumber < frames.length) {
var clusterFrames = [];
var clusterDuration = 0;
do {
clusterFrames.push(frames[frameNumber]);
clusterDuration += frames[frameNumber].duration;
frameNumber++;
} while (frameNumber < frames.length && clusterDuration < clusterMaxDuration);
var clusterCounter = 0;
var cluster = {
'id': 0x1f43b675, // Cluster
'data': getClusterData(clusterTimecode, clusterCounter, clusterFrames)
}; //Add cluster to segment
EBML[1].data.push(cluster);
clusterTimecode += clusterDuration;
}
return generateEBML(EBML);
}
function getClusterData(clusterTimecode, clusterCounter, clusterFrames) {
return [{
'data': clusterTimecode,
'id': 0xe7 // Timecode
}].concat(clusterFrames.map(function(webp) {
var block = makeSimpleBlock({
discardable: 0,
frame: webp.data.slice(4),
invisible: 0,
keyframe: 1,
lacing: 0,
trackNum: 1,
timecode: Math.round(clusterCounter)
});
clusterCounter += webp.duration;
return {
data: block,
id: 0xa3
};
}));
}
// sums the lengths of all the frames and gets the duration
function checkFrames(frames) {
if (!frames[0]) {
postMessage({
error: 'Something went wrong. Maybe WebP format is not supported in the current browser.'
});
return;
}
var width = frames[0].width,
height = frames[0].height,
duration = frames[0].duration;
for (var i = 1; i < frames.length; i++) {
duration += frames[i].duration;
}
return {
duration: duration,
width: width,
height: height
};
}
function numToBuffer(num) {
var parts = [];
while (num > 0) {
parts.push(num & 0xff);
num = num >> 8;
}
return new Uint8Array(parts.reverse());
}
function strToBuffer(str) {
return new Uint8Array(str.split('').map(function(e) {
return e.charCodeAt(0);
}));
}
function bitsToBuffer(bits) {
var data = [];
var pad = (bits.length % 8) ? (new Array(1 + 8 - (bits.length % 8))).join('0') : '';
bits = pad + bits;
for (var i = 0; i < bits.length; i += 8) {
data.push(parseInt(bits.substr(i, 8), 2));
}
return new Uint8Array(data);
}
function generateEBML(json) {
var ebml = [];
for (var i = 0; i < json.length; i++) {
var data = json[i].data;
if (typeof data === 'object') {
data = generateEBML(data);
}
if (typeof data === 'number') {
data = bitsToBuffer(data.toString(2));
}
if (typeof data === 'string') {
data = strToBuffer(data);
}
var len = data.size || data.byteLength || data.length;
var zeroes = Math.ceil(Math.ceil(Math.log(len) / Math.log(2)) / 8);
var sizeToString = len.toString(2);
var padded = (new Array((zeroes * 7 + 7 + 1) - sizeToString.length)).join('0') + sizeToString;
var size = (new Array(zeroes)).join('0') + '1' + padded;
ebml.push(numToBuffer(json[i].id));
ebml.push(bitsToBuffer(size));
ebml.push(data);
}
return new Blob(ebml, {
type: 'video/webm'
});
}
function toBinStrOld(bits) {
var data = '';
var pad = (bits.length % 8) ? (new Array(1 + 8 - (bits.length % 8))).join('0') : '';
bits = pad + bits;
for (var i = 0; i < bits.length; i += 8) {
data += String.fromCharCode(parseInt(bits.substr(i, 8), 2));
}
return data;
}
function makeSimpleBlock(data) {
var flags = 0;
if (data.keyframe) {
flags |= 128;
}
if (data.invisible) {
flags |= 8;
}
if (data.lacing) {
flags |= (data.lacing << 1);
}
if (data.discardable) {
flags |= 1;
}
if (data.trackNum > 127) {
throw 'TrackNumber > 127 not supported';
}
var out = [data.trackNum | 0x80, data.timecode >> 8, data.timecode & 0xff, flags].map(function(e) {
return String.fromCharCode(e);
}).join('') + data.frame;
return out;
}
function parseWebP(riff) {
var VP8 = riff.RIFF[0].WEBP[0];
var frameStart = VP8.indexOf('\x9d\x01\x2a'); // A VP8 keyframe starts with the 0x9d012a header
for (var i = 0, c = []; i < 4; i++) {
c[i] = VP8.charCodeAt(frameStart + 3 + i);
}
var width, height, tmp;
//the code below is literally copied verbatim from the bitstream spec
tmp = (c[1] << 8) | c[0];
width = tmp & 0x3FFF;
tmp = (c[3] << 8) | c[2];
height = tmp & 0x3FFF;
return {
width: width,
height: height,
data: VP8,
riff: riff
};
}
function getStrLength(string, offset) {
return parseInt(string.substr(offset + 4, 4).split('').map(function(i) {
var unpadded = i.charCodeAt(0).toString(2);
return (new Array(8 - unpadded.length + 1)).join('0') + unpadded;
}).join(''), 2);
}
function parseRIFF(string) {
var offset = 0;
var chunks = {};
while (offset < string.length) {
var id = string.substr(offset, 4);
var len = getStrLength(string, offset);
var data = string.substr(offset + 4 + 4, len);
offset += 4 + 4 + len;
chunks[id] = chunks[id] || [];
if (id === 'RIFF' || id === 'LIST') {
chunks[id].push(parseRIFF(data));
} else {
chunks[id].push(data);
}
}
return chunks;
}
function doubleToString(num) {
return [].slice.call(
new Uint8Array((new Float64Array([num])).buffer), 0).map(function(e) {
return String.fromCharCode(e);
}).reverse().join('');
}
var webm = new ArrayToWebM(frames.map(function(frame) {
var webp = parseWebP(parseRIFF(atob(frame.image.slice(23))));
webp.duration = frame.duration;
return webp;
}));
postMessage(webm);
}
/**
* Encodes frames in WebM container. It uses WebWorkinvoke to invoke 'ArrayToWebM' method.
* @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.
* @method
* @memberof Whammy
* @example
* recorder = new Whammy().Video(0.8, 100);
* recorder.compile(function(blob) {
* // blob.size - blob.type
* });
*/
WhammyVideo.prototype.compile = function(callback) {
var webWorker = processInWebWorker(whammyInWebWorker);
webWorker.onmessage = function(event) {
if (event.data.error) {
console.error(event.data.error);
return;
}
callback(event.data);
};
webWorker.postMessage(this.frames);
};
return {
/**
* A more abstract-ish API.
* @method
* @memberof Whammy
* @example
* recorder = new Whammy().Video(0.8, 100);
* @param {?number} speed - 0.8
* @param {?number} quality - 100
*/
Video: WhammyVideo
};
})();
if (typeof RecordRTC !== 'undefined') {
RecordRTC.Whammy = Whammy;
}
// ______________ (indexed-db)
// DiskStorage.js
/**
* DiskStorage is a standalone object used by {@link RecordRTC} to store recorded blobs in IndexedDB storage.
* @summary Writing blobs into IndexedDB.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @example
* DiskStorage.Store({
* audioBlob: yourAudioBlob,
* videoBlob: yourVideoBlob,
* gifBlob : yourGifBlob
* });
* DiskStorage.Fetch(function(dataURL, type) {
* if(type === 'audioBlob') { }
* if(type === 'videoBlob') { }
* if(type === 'gifBlob') { }
* });
* // DiskStorage.dataStoreName = 'recordRTC';
* // DiskStorage.onError = function(error) { };
* @property {function} init - This method must be called once to initialize IndexedDB ObjectStore. Though, it is auto-used internally.
* @property {function} Fetch - This method fetches stored blobs from IndexedDB.
* @property {function} Store - This method stores blobs in IndexedDB.
* @property {function} onError - This function is invoked for any known/unknown error.
* @property {string} dataStoreName - Name of the ObjectStore created in IndexedDB storage.
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
*/
var DiskStorage = {
/**
* This method must be called once to initialize IndexedDB ObjectStore. Though, it is auto-used internally.
* @method
* @memberof DiskStorage
* @internal
* @example
* DiskStorage.init();
*/
init: function() {
var self = this;
if (typeof indexedDB === 'undefined' || typeof indexedDB.open === 'undefined') {
console.error('IndexedDB API are not available in this browser.');
return;
}
var dbVersion = 1;
var dbName = this.dbName || location.href.replace(/\/|:|#|%|\.|\[|\]/g, ''),
db;
var request = indexedDB.open(dbName, dbVersion);
function createObjectStore(dataBase) {
dataBase.createObjectStore(self.dataStoreName);
}
function putInDB() {
var transaction = db.transaction([self.dataStoreName], 'readwrite');
if (self.videoBlob) {
transaction.objectStore(self.dataStoreName).put(self.videoBlob, 'videoBlob');
}
if (self.gifBlob) {
transaction.objectStore(self.dataStoreName).put(self.gifBlob, 'gifBlob');
}
if (self.audioBlob) {
transaction.objectStore(self.dataStoreName).put(self.audioBlob, 'audioBlob');
}
function getFromStore(portionName) {
transaction.objectStore(self.dataStoreName).get(portionName).onsuccess = function(event) {
if (self.callback) {
self.callback(event.target.result, portionName);
}
};
}
getFromStore('audioBlob');
getFromStore('videoBlob');
getFromStore('gifBlob');
}
request.onerror = self.onError;
request.onsuccess = function() {
db = request.result;
db.onerror = self.onError;
if (db.setVersion) {
if (db.version !== dbVersion) {
var setVersion = db.setVersion(dbVersion);
setVersion.onsuccess = function() {
createObjectStore(db);
putInDB();
};
} else {
putInDB();
}
} else {
putInDB();
}
};
request.onupgradeneeded = function(event) {
createObjectStore(event.target.result);
};
},
/**
* This method fetches stored blobs from IndexedDB.
* @method
* @memberof DiskStorage
* @internal
* @example
* DiskStorage.Fetch(function(dataURL, type) {
* if(type === 'audioBlob') { }
* if(type === 'videoBlob') { }
* if(type === 'gifBlob') { }
* });
*/
Fetch: function(callback) {
this.callback = callback;
this.init();
return this;
},
/**
* This method stores blobs in IndexedDB.
* @method
* @memberof DiskStorage
* @internal
* @example
* DiskStorage.Store({
* audioBlob: yourAudioBlob,
* videoBlob: yourVideoBlob,
* gifBlob : yourGifBlob
* });
*/
Store: function(config) {
this.audioBlob = config.audioBlob;
this.videoBlob = config.videoBlob;
this.gifBlob = config.gifBlob;
this.init();
return this;
},
/**
* This function is invoked for any known/unknown error.
* @method
* @memberof DiskStorage
* @internal
* @example
* DiskStorage.onError = function(error){
* alerot( JSON.stringify(error) );
* };
*/
onError: function(error) {
console.error(JSON.stringify(error, null, '\t'));
},
/**
* @property {string} dataStoreName - Name of the ObjectStore created in IndexedDB storage.
* @memberof DiskStorage
* @internal
* @example
* DiskStorage.dataStoreName = 'recordRTC';
*/
dataStoreName: 'recordRTC',
dbName: null
};
if (typeof RecordRTC !== 'undefined') {
RecordRTC.DiskStorage = DiskStorage;
}
// ______________
// GifRecorder.js
/**
* GifRecorder is standalone calss used by {@link RecordRTC} to record video or canvas into animated gif.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef GifRecorder
* @class
* @example
* var recorder = new GifRecorder(mediaStream || canvas || context, { onGifPreview: function, onGifRecordingStarted: function, width: 1280, height: 720, frameRate: 200, quality: 10 });
* recorder.record();
* recorder.stop(function(blob) {
* img.src = URL.createObjectURL(blob);
* });
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStream} mediaStream - MediaStream object or HTMLCanvasElement or CanvasRenderingContext2D.
* @param {object} config - {disableLogs:true, initCallback: function, width: 320, height: 240, frameRate: 200, quality: 10}
*/
function GifRecorder(mediaStream, config) {
if (typeof GIFEncoder === 'undefined') {
var script = document.createElement('script');
script.src = 'https://www.webrtc-experiment.com/gif-recorder.js';
(document.body || document.documentElement).appendChild(script);
}
config = config || {};
var isHTMLObject = mediaStream instanceof CanvasRenderingContext2D || mediaStream instanceof HTMLCanvasElement;
/**
* This method records MediaStream.
* @method
* @memberof GifRecorder
* @example
* recorder.record();
*/
this.record = function() {
if (typeof GIFEncoder === 'undefined') {
setTimeout(self.record, 1000);
return;
}
if (!isLoadedMetaData) {
setTimeout(self.record, 1000);
return;
}
if (!isHTMLObject) {
if (!config.width) {
config.width = video.offsetWidth || 320;
}
if (!config.height) {
config.height = video.offsetHeight || 240;
}
if (!config.video) {
config.video = {
width: config.width,
height: config.height
};
}
if (!config.canvas) {
config.canvas = {
width: config.width,
height: config.height
};
}
canvas.width = config.canvas.width || 320;
canvas.height = config.canvas.height || 240;
video.width = config.video.width || 320;
video.height = config.video.height || 240;
}
// external library to record as GIF images
gifEncoder = new GIFEncoder();
// void setRepeat(int iter)
// Sets the number of times the set of GIF frames should be played.
// Default is 1; 0 means play indefinitely.
gifEncoder.setRepeat(0);
// void setFrameRate(Number fps)
// Sets frame rate in frames per second.
// Equivalent to setDelay(1000/fps).
// Using "setDelay" instead of "setFrameRate"
gifEncoder.setDelay(config.frameRate || 200);
// void setQuality(int quality)
// Sets quality of color quantization (conversion of images to the
// maximum 256 colors allowed by the GIF specification).
// Lower values (minimum = 1) produce better colors,
// but slow processing significantly. 10 is the default,
// and produces good color mapping at reasonable speeds.
// Values greater than 20 do not yield significant improvements in speed.
gifEncoder.setQuality(config.quality || 10);
// Boolean start()
// This writes the GIF Header and returns false if it fails.
gifEncoder.start();
if (typeof config.onGifRecordingStarted === 'function') {
config.onGifRecordingStarted();
}
startTime = Date.now();
function drawVideoFrame(time) {
if (self.clearedRecordedData === true) {
return;
}
if (isPausedRecording) {
return setTimeout(function() {
drawVideoFrame(time);
}, 100);
}
lastAnimationFrame = requestAnimationFrame(drawVideoFrame);
if (typeof lastFrameTime === undefined) {
lastFrameTime = time;
}
// ~10 fps
if (time - lastFrameTime < 90) {
return;
}
if (!isHTMLObject && video.paused) {
// via: https://github.com/muaz-khan/WebRTC-Experiment/pull/316
// Tweak for Android Chrome
video.play();
}
if (!isHTMLObject) {
context.drawImage(video, 0, 0, canvas.width, canvas.height);
}
if (config.onGifPreview) {
config.onGifPreview(canvas.toDataURL('image/png'));
}
gifEncoder.addFrame(context);
lastFrameTime = time;
}
lastAnimationFrame = requestAnimationFrame(drawVideoFrame);
if (config.initCallback) {
config.initCallback();
}
};
/**
* This method stops recording MediaStream.
* @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.
* @method
* @memberof GifRecorder
* @example
* recorder.stop(function(blob) {
* img.src = URL.createObjectURL(blob);
* });
*/
this.stop = function(callback) {
callback = callback || function() {};
if (lastAnimationFrame) {
cancelAnimationFrame(lastAnimationFrame);
}
endTime = Date.now();
/**
* @property {Blob} blob - The recorded blob object.
* @memberof GifRecorder
* @example
* recorder.stop(function(){
* var blob = recorder.blob;
* });
*/
this.blob = new Blob([new Uint8Array(gifEncoder.stream().bin)], {
type: 'image/gif'
});
callback(this.blob);
// bug: find a way to clear old recorded blobs
gifEncoder.stream().bin = [];
};
var isPausedRecording = false;
/**
* This method pauses the recording process.
* @method
* @memberof GifRecorder
* @example
* recorder.pause();
*/
this.pause = function() {
isPausedRecording = true;
};
/**
* This method resumes the recording process.
* @method
* @memberof GifRecorder
* @example
* recorder.resume();
*/
this.resume = function() {
isPausedRecording = false;
};
/**
* This method resets currently recorded data.
* @method
* @memberof GifRecorder
* @example
* recorder.clearRecordedData();
*/
this.clearRecordedData = function() {
self.clearedRecordedData = true;
clearRecordedDataCB();
};
function clearRecordedDataCB() {
if (gifEncoder) {
gifEncoder.stream().bin = [];
}
}
// for debugging
this.name = 'GifRecorder';
this.toString = function() {
return this.name;
};
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
if (isHTMLObject) {
if (mediaStream instanceof CanvasRenderingContext2D) {
context = mediaStream;
canvas = context.canvas;
} else if (mediaStream instanceof HTMLCanvasElement) {
context = mediaStream.getContext('2d');
canvas = mediaStream;
}
}
var isLoadedMetaData = true;
if (!isHTMLObject) {
var video = document.createElement('video');
video.muted = true;
video.autoplay = true;
video.playsInline = true;
isLoadedMetaData = false;
video.onloadedmetadata = function() {
isLoadedMetaData = true;
};
setSrcObject(mediaStream, video);
video.play();
}
var lastAnimationFrame = null;
var startTime, endTime, lastFrameTime;
var gifEncoder;
var self = this;
}
if (typeof RecordRTC !== 'undefined') {
RecordRTC.GifRecorder = GifRecorder;
}
// Last time updated: 2019-06-21 4:09:42 AM UTC
// ________________________
// MultiStreamsMixer v1.2.2
// Open-Sourced: https://github.com/muaz-khan/MultiStreamsMixer
// --------------------------------------------------
// Muaz Khan - www.MuazKhan.com
// MIT License - www.WebRTC-Experiment.com/licence
// --------------------------------------------------
function MultiStreamsMixer(arrayOfMediaStreams, elementClass) {
var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45';
(function(that) {
if (typeof RecordRTC !== 'undefined') {
return;
}
if (!that) {
return;
}
if (typeof window !== 'undefined') {
return;
}
if (typeof global === 'undefined') {
return;
}
global.navigator = {
userAgent: browserFakeUserAgent,
getUserMedia: function() {}
};
if (!global.console) {
global.console = {};
}
if (typeof global.console.log === 'undefined' || typeof global.console.error === 'undefined') {
global.console.error = global.console.log = global.console.log || function() {
console.log(arguments);
};
}
if (typeof document === 'undefined') {
/*global document:true */
that.document = {
documentElement: {
appendChild: function() {
return '';
}
}
};
document.createElement = document.captureStream = document.mozCaptureStream = function() {
var obj = {
getContext: function() {
return obj;
},
play: function() {},
pause: function() {},
drawImage: function() {},
toDataURL: function() {
return '';
},
style: {}
};
return obj;
};
that.HTMLVideoElement = function() {};
}
if (typeof location === 'undefined') {
/*global location:true */
that.location = {
protocol: 'file:',
href: '',
hash: ''
};
}
if (typeof screen === 'undefined') {
/*global screen:true */
that.screen = {
width: 0,
height: 0
};
}
if (typeof URL === 'undefined') {
/*global screen:true */
that.URL = {
createObjectURL: function() {
return '';
},
revokeObjectURL: function() {
return '';
}
};
}
/*global window:true */
that.window = global;
})(typeof global !== 'undefined' ? global : null);
// requires: chrome://flags/#enable-experimental-web-platform-features
elementClass = elementClass || 'multi-streams-mixer';
var videos = [];
var isStopDrawingFrames = false;
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
canvas.style.opacity = 0;
canvas.style.position = 'absolute';
canvas.style.zIndex = -1;
canvas.style.top = '-1000em';
canvas.style.left = '-1000em';
canvas.className = elementClass;
(document.body || document.documentElement).appendChild(canvas);
this.disableLogs = false;
this.frameInterval = 10;
this.width = 360;
this.height = 240;
// use gain node to prevent echo
this.useGainNode = true;
var self = this;
// _____________________________
// Cross-Browser-Declarations.js
// WebAudio API representer
var AudioContext = window.AudioContext;
if (typeof AudioContext === 'undefined') {
if (typeof webkitAudioContext !== 'undefined') {
/*global AudioContext:true */
AudioContext = webkitAudioContext;
}
if (typeof mozAudioContext !== 'undefined') {
/*global AudioContext:true */
AudioContext = mozAudioContext;
}
}
/*jshint -W079 */
var URL = window.URL;
if (typeof URL === 'undefined' && typeof webkitURL !== 'undefined') {
/*global URL:true */
URL = webkitURL;
}
if (typeof navigator !== 'undefined' && typeof navigator.getUserMedia === 'undefined') { // maybe window.navigator?
if (typeof navigator.webkitGetUserMedia !== 'undefined') {
navigator.getUserMedia = navigator.webkitGetUserMedia;
}
if (typeof navigator.mozGetUserMedia !== 'undefined') {
navigator.getUserMedia = navigator.mozGetUserMedia;
}
}
var MediaStream = window.MediaStream;
if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') {
MediaStream = webkitMediaStream;
}
/*global MediaStream:true */
if (typeof MediaStream !== 'undefined') {
// override "stop" method for all browsers
if (typeof MediaStream.prototype.stop === 'undefined') {
MediaStream.prototype.stop = function() {
this.getTracks().forEach(function(track) {
track.stop();
});
};
}
}
var Storage = {};
if (typeof AudioContext !== 'undefined') {
Storage.AudioContext = AudioContext;
} else if (typeof webkitAudioContext !== 'undefined') {
Storage.AudioContext = webkitAudioContext;
}
function setSrcObject(stream, element) {
if ('srcObject' in element) {
element.srcObject = stream;
} else if ('mozSrcObject' in element) {
element.mozSrcObject = stream;
} else {
element.srcObject = stream;
}
}
this.startDrawingFrames = function() {
drawVideosToCanvas();
};
function drawVideosToCanvas() {
if (isStopDrawingFrames) {
return;
}
var videosLength = videos.length;
var fullcanvas = false;
var remaining = [];
videos.forEach(function(video) {
if (!video.stream) {
video.stream = {};
}
if (video.stream.fullcanvas) {
fullcanvas = video;
} else {
// todo: video.stream.active or video.stream.live to fix blank frames issues?
remaining.push(video);
}
});
if (fullcanvas) {
canvas.width = fullcanvas.stream.width;
canvas.height = fullcanvas.stream.height;
} else if (remaining.length) {
canvas.width = videosLength > 1 ? remaining[0].width * 2 : remaining[0].width;
var height = 1;
if (videosLength === 3 || videosLength === 4) {
height = 2;
}
if (videosLength === 5 || videosLength === 6) {
height = 3;
}
if (videosLength === 7 || videosLength === 8) {
height = 4;
}
if (videosLength === 9 || videosLength === 10) {
height = 5;
}
canvas.height = remaining[0].height * height;
} else {
canvas.width = self.width || 360;
canvas.height = self.height || 240;
}
if (fullcanvas && fullcanvas instanceof HTMLVideoElement) {
drawImage(fullcanvas);
}
remaining.forEach(function(video, idx) {
drawImage(video, idx);
});
setTimeout(drawVideosToCanvas, self.frameInterval);
}
function drawImage(video, idx) {
if (isStopDrawingFrames) {
return;
}
var x = 0;
var y = 0;
var width = video.width;
var height = video.height;
if (idx === 1) {
x = video.width;
}
if (idx === 2) {
y = video.height;
}
if (idx === 3) {
x = video.width;
y = video.height;
}
if (idx === 4) {
y = video.height * 2;
}
if (idx === 5) {
x = video.width;
y = video.height * 2;
}
if (idx === 6) {
y = video.height * 3;
}
if (idx === 7) {
x = video.width;
y = video.height * 3;
}
if (typeof video.stream.left !== 'undefined') {
x = video.stream.left;
}
if (typeof video.stream.top !== 'undefined') {
y = video.stream.top;
}
if (typeof video.stream.width !== 'undefined') {
width = video.stream.width;
}
if (typeof video.stream.height !== 'undefined') {
height = video.stream.height;
}
context.drawImage(video, x, y, width, height);
if (typeof video.stream.onRender === 'function') {
video.stream.onRender(context, x, y, width, height, idx);
}
}
function getMixedStream() {
isStopDrawingFrames = false;
var mixedVideoStream = getMixedVideoStream();
var mixedAudioStream = getMixedAudioStream();
if (mixedAudioStream) {
mixedAudioStream.getTracks().filter(function(t) {
return t.kind === 'audio';
}).forEach(function(track) {
mixedVideoStream.addTrack(track);
});
}
var fullcanvas;
arrayOfMediaStreams.forEach(function(stream) {
if (stream.fullcanvas) {
fullcanvas = true;
}
});
// mixedVideoStream.prototype.appendStreams = appendStreams;
// mixedVideoStream.prototype.resetVideoStreams = resetVideoStreams;
// mixedVideoStream.prototype.clearRecordedData = clearRecordedData;
return mixedVideoStream;
}
function getMixedVideoStream() {
resetVideoStreams();
var capturedStream;
if ('captureStream' in canvas) {
capturedStream = canvas.captureStream();
} else if ('mozCaptureStream' in canvas) {
capturedStream = canvas.mozCaptureStream();
} else if (!self.disableLogs) {
console.error('Upgrade to latest Chrome or otherwise enable this flag: chrome://flags/#enable-experimental-web-platform-features');
}
var videoStream = new MediaStream();
capturedStream.getTracks().filter(function(t) {
return t.kind === 'video';
}).forEach(function(track) {
videoStream.addTrack(track);
});
canvas.stream = videoStream;
return videoStream;
}
function getMixedAudioStream() {
// via: @pehrsons
if (!Storage.AudioContextConstructor) {
Storage.AudioContextConstructor = new Storage.AudioContext();
}
self.audioContext = Storage.AudioContextConstructor;
self.audioSources = [];
if (self.useGainNode === true) {
self.gainNode = self.audioContext.createGain();
self.gainNode.connect(self.audioContext.destination);
self.gainNode.gain.value = 0; // don't hear self
}
var audioTracksLength = 0;
arrayOfMediaStreams.forEach(function(stream) {
if (!stream.getTracks().filter(function(t) {
return t.kind === 'audio';
}).length) {
return;
}
audioTracksLength++;
var audioSource = self.audioContext.createMediaStreamSource(stream);
if (self.useGainNode === true) {
audioSource.connect(self.gainNode);
}
self.audioSources.push(audioSource);
});
if (!audioTracksLength) {
// because "self.audioContext" is not initialized
// that's why we've to ignore rest of the code
return;
}
self.audioDestination = self.audioContext.createMediaStreamDestination();
self.audioSources.forEach(function(audioSource) {
audioSource.connect(self.audioDestination);
});
return self.audioDestination.stream;
}
function getVideo(stream) {
var video = document.createElement('video');
setSrcObject(stream, video);
video.className = elementClass;
video.muted = true;
video.volume = 0;
video.width = stream.width || self.width || 360;
video.height = stream.height || self.height || 240;
video.play();
return video;
}
this.appendStreams = function(streams) {
if (!streams) {
throw 'First parameter is required.';
}
if (!(streams instanceof Array)) {
streams = [streams];
}
streams.forEach(function(stream) {
var newStream = new MediaStream();
if (stream.getTracks().filter(function(t) {
return t.kind === 'video';
}).length) {
var video = getVideo(stream);
video.stream = stream;
videos.push(video);
newStream.addTrack(stream.getTracks().filter(function(t) {
return t.kind === 'video';
})[0]);
}
if (stream.getTracks().filter(function(t) {
return t.kind === 'audio';
}).length) {
var audioSource = self.audioContext.createMediaStreamSource(stream);
self.audioDestination = self.audioContext.createMediaStreamDestination();
audioSource.connect(self.audioDestination);
newStream.addTrack(self.audioDestination.stream.getTracks().filter(function(t) {
return t.kind === 'audio';
})[0]);
}
arrayOfMediaStreams.push(newStream);
});
};
this.releaseStreams = function() {
videos = [];
isStopDrawingFrames = true;
if (self.gainNode) {
self.gainNode.disconnect();
self.gainNode = null;
}
if (self.audioSources.length) {
self.audioSources.forEach(function(source) {
source.disconnect();
});
self.audioSources = [];
}
if (self.audioDestination) {
self.audioDestination.disconnect();
self.audioDestination = null;
}
if (self.audioContext) {
self.audioContext.close();
}
self.audioContext = null;
context.clearRect(0, 0, canvas.width, canvas.height);
if (canvas.stream) {
canvas.stream.stop();
canvas.stream = null;
}
};
this.resetVideoStreams = function(streams) {
if (streams && !(streams instanceof Array)) {
streams = [streams];
}
resetVideoStreams(streams);
};
function resetVideoStreams(streams) {
videos = [];
streams = streams || arrayOfMediaStreams;
// via: @adrian-ber
streams.forEach(function(stream) {
if (!stream.getTracks().filter(function(t) {
return t.kind === 'video';
}).length) {
return;
}
var video = getVideo(stream);
video.stream = stream;
videos.push(video);
});
}
// for debugging
this.name = 'MultiStreamsMixer';
this.toString = function() {
return this.name;
};
this.getMixedStream = getMixedStream;
}
if (typeof RecordRTC === 'undefined') {
if (true /* && !!module.exports*/ ) {
module.exports = MultiStreamsMixer;
}
if (true) {
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function() {
return MultiStreamsMixer;
}).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
}
}
// ______________________
// MultiStreamRecorder.js
/*
* Video conference recording, using captureStream API along with WebAudio and Canvas2D API.
*/
/**
* MultiStreamRecorder can record multiple videos in single container.
* @summary Multi-videos recorder.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef MultiStreamRecorder
* @class
* @example
* var options = {
* mimeType: 'video/webm'
* }
* var recorder = new MultiStreamRecorder(ArrayOfMediaStreams, options);
* recorder.record();
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
*
* // or
* var blob = recorder.blob;
* });
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStreams} mediaStreams - Array of MediaStreams.
* @param {object} config - {disableLogs:true, frameInterval: 1, mimeType: "video/webm"}
*/
function MultiStreamRecorder(arrayOfMediaStreams, options) {
arrayOfMediaStreams = arrayOfMediaStreams || [];
var self = this;
var mixer;
var mediaRecorder;
options = options || {
elementClass: 'multi-streams-mixer',
mimeType: 'video/webm',
video: {
width: 360,
height: 240
}
};
if (!options.frameInterval) {
options.frameInterval = 10;
}
if (!options.video) {
options.video = {};
}
if (!options.video.width) {
options.video.width = 360;
}
if (!options.video.height) {
options.video.height = 240;
}
/**
* This method records all MediaStreams.
* @method
* @memberof MultiStreamRecorder
* @example
* recorder.record();
*/
this.record = function() {
// github/muaz-khan/MultiStreamsMixer
mixer = new MultiStreamsMixer(arrayOfMediaStreams, options.elementClass || 'multi-streams-mixer');
if (getAllVideoTracks().length) {
mixer.frameInterval = options.frameInterval || 10;
mixer.width = options.video.width || 360;
mixer.height = options.video.height || 240;
mixer.startDrawingFrames();
}
if (options.previewStream && typeof options.previewStream === 'function') {
options.previewStream(mixer.getMixedStream());
}
// record using MediaRecorder API
mediaRecorder = new MediaStreamRecorder(mixer.getMixedStream(), options);
mediaRecorder.record();
};
function getAllVideoTracks() {
var tracks = [];
arrayOfMediaStreams.forEach(function(stream) {
getTracks(stream, 'video').forEach(function(track) {
tracks.push(track);
});
});
return tracks;
}
/**
* This method stops recording MediaStream.
* @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.
* @method
* @memberof MultiStreamRecorder
* @example
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
*/
this.stop = function(callback) {
if (!mediaRecorder) {
return;
}
mediaRecorder.stop(function(blob) {
self.blob = blob;
callback(blob);
self.clearRecordedData();
});
};
/**
* This method pauses the recording process.
* @method
* @memberof MultiStreamRecorder
* @example
* recorder.pause();
*/
this.pause = function() {
if (mediaRecorder) {
mediaRecorder.pause();
}
};
/**
* This method resumes the recording process.
* @method
* @memberof MultiStreamRecorder
* @example
* recorder.resume();
*/
this.resume = function() {
if (mediaRecorder) {
mediaRecorder.resume();
}
};
/**
* This method resets currently recorded data.
* @method
* @memberof MultiStreamRecorder
* @example
* recorder.clearRecordedData();
*/
this.clearRecordedData = function() {
if (mediaRecorder) {
mediaRecorder.clearRecordedData();
mediaRecorder = null;
}
if (mixer) {
mixer.releaseStreams();
mixer = null;
}
};
/**
* Add extra media-streams to existing recordings.
* @method
* @memberof MultiStreamRecorder
* @param {MediaStreams} mediaStreams - Array of MediaStreams
* @example
* recorder.addStreams([newAudioStream, newVideoStream]);
*/
this.addStreams = function(streams) {
if (!streams) {
throw 'First parameter is required.';
}
if (!(streams instanceof Array)) {
streams = [streams];
}
arrayOfMediaStreams.concat(streams);
if (!mediaRecorder || !mixer) {
return;
}
mixer.appendStreams(streams);
if (options.previewStream && typeof options.previewStream === 'function') {
options.previewStream(mixer.getMixedStream());
}
};
/**
* Reset videos during live recording. Replace old videos e.g. replace cameras with full-screen.
* @method
* @memberof MultiStreamRecorder
* @param {MediaStreams} mediaStreams - Array of MediaStreams
* @example
* recorder.resetVideoStreams([newVideo1, newVideo2]);
*/
this.resetVideoStreams = function(streams) {
if (!mixer) {
return;
}
if (streams && !(streams instanceof Array)) {
streams = [streams];
}
mixer.resetVideoStreams(streams);
};
/**
* Returns MultiStreamsMixer
* @method
* @memberof MultiStreamRecorder
* @example
* let mixer = recorder.getMixer();
* mixer.appendStreams([newStream]);
*/
this.getMixer = function() {
return mixer;
};
// for debugging
this.name = 'MultiStreamRecorder';
this.toString = function() {
return this.name;
};
}
if (typeof RecordRTC !== 'undefined') {
RecordRTC.MultiStreamRecorder = MultiStreamRecorder;
}
// _____________________
// RecordRTC.promises.js
/**
* RecordRTCPromisesHandler adds promises support in {@link RecordRTC}. Try a {@link https://github.com/muaz-khan/RecordRTC/blob/master/simple-demos/RecordRTCPromisesHandler.html|demo here}
* @summary Promises for {@link RecordRTC}
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef RecordRTCPromisesHandler
* @class
* @example
* var recorder = new RecordRTCPromisesHandler(mediaStream, options);
* recorder.startRecording()
* .then(successCB)
* .catch(errorCB);
* // Note: You can access all RecordRTC API using "recorder.recordRTC" e.g.
* recorder.recordRTC.onStateChanged = function(state) {};
* recorder.recordRTC.setRecordingDuration(5000);
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStream} mediaStream - Single media-stream object, array of media-streams, html-canvas-element, etc.
* @param {object} config - {type:"video", recorderType: MediaStreamRecorder, disableLogs: true, numberOfAudioChannels: 1, bufferSize: 0, sampleRate: 0, video: HTMLVideoElement, etc.}
* @throws Will throw an error if "new" keyword is not used to initiate "RecordRTCPromisesHandler". Also throws error if first argument "MediaStream" is missing.
* @requires {@link RecordRTC}
*/
function RecordRTCPromisesHandler(mediaStream, options) {
if (!this) {
throw 'Use "new RecordRTCPromisesHandler()"';
}
if (typeof mediaStream === 'undefined') {
throw 'First argument "MediaStream" is required.';
}
var self = this;
/**
* @property {Blob} blob - Access/reach the native {@link RecordRTC} object.
* @memberof RecordRTCPromisesHandler
* @example
* let internal = recorder.recordRTC.getInternalRecorder();
* alert(internal instanceof MediaStreamRecorder);
* recorder.recordRTC.onStateChanged = function(state) {};
*/
self.recordRTC = new RecordRTC(mediaStream, options);
/**
* This method records MediaStream.
* @method
* @memberof RecordRTCPromisesHandler
* @example
* recorder.startRecording()
* .then(successCB)
* .catch(errorCB);
*/
this.startRecording = function() {
return new Promise(function(resolve, reject) {
try {
self.recordRTC.startRecording();
resolve();
} catch (e) {
reject(e);
}
});
};
/**
* This method stops the recording.
* @method
* @memberof RecordRTCPromisesHandler
* @example
* recorder.stopRecording().then(function() {
* var blob = recorder.getBlob();
* }).catch(errorCB);
*/
this.stopRecording = function() {
return new Promise(function(resolve, reject) {
try {
self.recordRTC.stopRecording(function(url) {
self.blob = self.recordRTC.getBlob();
if (!self.blob || !self.blob.size) {
reject('Empty blob.', self.blob);
return;
}
resolve(url);
});
} catch (e) {
reject(e);
}
});
};
/**
* This method pauses the recording. You can resume recording using "resumeRecording" method.
* @method
* @memberof RecordRTCPromisesHandler
* @example
* recorder.pauseRecording()
* .then(successCB)
* .catch(errorCB);
*/
this.pauseRecording = function() {
return new Promise(function(resolve, reject) {
try {
self.recordRTC.pauseRecording();
resolve();
} catch (e) {
reject(e);
}
});
};
/**
* This method resumes the recording.
* @method
* @memberof RecordRTCPromisesHandler
* @example
* recorder.resumeRecording()
* .then(successCB)
* .catch(errorCB);
*/
this.resumeRecording = function() {
return new Promise(function(resolve, reject) {
try {
self.recordRTC.resumeRecording();
resolve();
} catch (e) {
reject(e);
}
});
};
/**
* This method returns data-url for the recorded blob.
* @method
* @memberof RecordRTCPromisesHandler
* @example
* recorder.stopRecording().then(function() {
* recorder.getDataURL().then(function(dataURL) {
* window.open(dataURL);
* }).catch(errorCB);;
* }).catch(errorCB);
*/
this.getDataURL = function(callback) {
return new Promise(function(resolve, reject) {
try {
self.recordRTC.getDataURL(function(dataURL) {
resolve(dataURL);
});
} catch (e) {
reject(e);
}
});
};
/**
* This method returns the recorded blob.
* @method
* @memberof RecordRTCPromisesHandler
* @example
* recorder.stopRecording().then(function() {
* recorder.getBlob().then(function(blob) {})
* }).catch(errorCB);
*/
this.getBlob = function() {
return new Promise(function(resolve, reject) {
try {
resolve(self.recordRTC.getBlob());
} catch (e) {
reject(e);
}
});
};
/**
* This method returns the internal recording object.
* @method
* @memberof RecordRTCPromisesHandler
* @example
* let internalRecorder = await recorder.getInternalRecorder();
* if(internalRecorder instanceof MultiStreamRecorder) {
* internalRecorder.addStreams([newAudioStream]);
* internalRecorder.resetVideoStreams([screenStream]);
* }
* @returns {Object}
*/
this.getInternalRecorder = function() {
return new Promise(function(resolve, reject) {
try {
resolve(self.recordRTC.getInternalRecorder());
} catch (e) {
reject(e);
}
});
};
/**
* This method resets the recorder. So that you can reuse single recorder instance many times.
* @method
* @memberof RecordRTCPromisesHandler
* @example
* await recorder.reset();
* recorder.startRecording(); // record again
*/
this.reset = function() {
return new Promise(function(resolve, reject) {
try {
resolve(self.recordRTC.reset());
} catch (e) {
reject(e);
}
});
};
/**
* Destroy RecordRTC instance. Clear all recorders and objects.
* @method
* @memberof RecordRTCPromisesHandler
* @example
* recorder.destroy().then(successCB).catch(errorCB);
*/
this.destroy = function() {
return new Promise(function(resolve, reject) {
try {
resolve(self.recordRTC.destroy());
} catch (e) {
reject(e);
}
});
};
/**
* Get recorder's readonly state.
* @method
* @memberof RecordRTCPromisesHandler
* @example
* let state = await recorder.getState();
* // or
* recorder.getState().then(state => { console.log(state); })
* @returns {String} Returns recording state.
*/
this.getState = function() {
return new Promise(function(resolve, reject) {
try {
resolve(self.recordRTC.getState());
} catch (e) {
reject(e);
}
});
};
/**
* @property {Blob} blob - Recorded data as "Blob" object.
* @memberof RecordRTCPromisesHandler
* @example
* await recorder.stopRecording();
* let blob = recorder.getBlob(); // or "recorder.recordRTC.blob"
* invokeSaveAsDialog(blob);
*/
this.blob = null;
/**
* RecordRTC version number
* @property {String} version - Release version number.
* @memberof RecordRTCPromisesHandler
* @static
* @readonly
* @example
* alert(recorder.version);
*/
this.version = '5.6.2';
}
if (typeof RecordRTC !== 'undefined') {
RecordRTC.RecordRTCPromisesHandler = RecordRTCPromisesHandler;
}
// ______________________
// WebAssemblyRecorder.js
/**
* WebAssemblyRecorder lets you create webm videos in JavaScript via WebAssembly. The library consumes raw RGBA32 buffers (4 bytes per pixel) and turns them into a webm video with the given framerate and quality. This makes it compatible out-of-the-box with ImageData from a CANVAS. With realtime mode you can also use webm-wasm for streaming webm videos.
* @summary Video recording feature in Chrome, Firefox and maybe Edge.
* @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}
* @author {@link https://MuazKhan.com|Muaz Khan}
* @typedef WebAssemblyRecorder
* @class
* @example
* var recorder = new WebAssemblyRecorder(mediaStream);
* recorder.record();
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.
* @param {object} config - {webAssemblyPath:'webm-wasm.wasm',workerPath: 'webm-worker.js', frameRate: 30, width: 1920, height: 1080, bitrate: 1024, realtime: true}
*/
function WebAssemblyRecorder(stream, config) {
// based on: github.com/GoogleChromeLabs/webm-wasm
if (typeof ReadableStream === 'undefined' || typeof WritableStream === 'undefined') {
// because it fixes readable/writable streams issues
console.error('Following polyfill is strongly recommended: https://unpkg.com/@mattiasbuelens/web-streams-polyfill/dist/polyfill.min.js');
}
config = config || {};
config.width = config.width || 640;
config.height = config.height || 480;
config.frameRate = config.frameRate || 30;
config.bitrate = config.bitrate || 1200;
config.realtime = config.realtime || true;
function createBufferURL(buffer, type) {
return URL.createObjectURL(new Blob([buffer], {
type: type || ''
}));
}
var finished;
function cameraStream() {
return new ReadableStream({
start: function(controller) {
var cvs = document.createElement('canvas');
var video = document.createElement('video');
var first = true;
video.srcObject = stream;
video.muted = true;
video.height = config.height;
video.width = config.width;
video.volume = 0;
video.onplaying = function() {
cvs.width = config.width;
cvs.height = config.height;
var ctx = cvs.getContext('2d');
var frameTimeout = 1000 / config.frameRate;
var cameraTimer = setInterval(function f() {
if (finished) {
clearInterval(cameraTimer);
controller.close();
}
if (first) {
first = false;
if (config.onVideoProcessStarted) {
config.onVideoProcessStarted();
}
}
ctx.drawImage(video, 0, 0);
if (controller._controlledReadableStream.state !== 'closed') {
try {
controller.enqueue(
ctx.getImageData(0, 0, config.width, config.height)
);
} catch (e) {}
}
}, frameTimeout);
};
video.play();
}
});
}
var worker;
function startRecording(stream, buffer) {
if (!config.workerPath && !buffer) {
finished = false;
// is it safe to use @latest ?
fetch(
'https://unpkg.com/webm-wasm@latest/dist/webm-worker.js'
).then(function(r) {
r.arrayBuffer().then(function(buffer) {
startRecording(stream, buffer);
});
});
return;
}
if (!config.workerPath && buffer instanceof ArrayBuffer) {
var blob = new Blob([buffer], {
type: 'text/javascript'
});
config.workerPath = URL.createObjectURL(blob);
}
if (!config.workerPath) {
console.error('workerPath parameter is missing.');
}
worker = new Worker(config.workerPath);
worker.postMessage(config.webAssemblyPath || 'https://unpkg.com/webm-wasm@latest/dist/webm-wasm.wasm');
worker.addEventListener('message', function(event) {
if (event.data === 'READY') {
worker.postMessage({
width: config.width,
height: config.height,
bitrate: config.bitrate || 1200,
timebaseDen: config.frameRate || 30,
realtime: config.realtime
});
cameraStream().pipeTo(new WritableStream({
write: function(image) {
if (finished) {
console.error('Got image, but recorder is finished!');
return;
}
worker.postMessage(image.data.buffer, [image.data.buffer]);
}
}));
} else if (!!event.data) {
if (!isPaused) {
arrayOfBuffers.push(event.data);
}
}
});
}
/**
* This method records video.
* @method
* @memberof WebAssemblyRecorder
* @example
* recorder.record();
*/
this.record = function() {
arrayOfBuffers = [];
isPaused = false;
this.blob = null;
startRecording(stream);
if (typeof config.initCallback === 'function') {
config.initCallback();
}
};
var isPaused;
/**
* This method pauses the recording process.
* @method
* @memberof WebAssemblyRecorder
* @example
* recorder.pause();
*/
this.pause = function() {
isPaused = true;
};
/**
* This method resumes the recording process.
* @method
* @memberof WebAssemblyRecorder
* @example
* recorder.resume();
*/
this.resume = function() {
isPaused = false;
};
function terminate(callback) {
if (!worker) {
if (callback) {
callback();
}
return;
}
// Wait for null event data to indicate that the encoding is complete
worker.addEventListener('message', function(event) {
if (event.data === null) {
worker.terminate();
worker = null;
if (callback) {
callback();
}
}
});
worker.postMessage(null);
}
var arrayOfBuffers = [];
/**
* This method stops recording video.
* @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.
* @method
* @memberof WebAssemblyRecorder
* @example
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
*/
this.stop = function(callback) {
finished = true;
var recorder = this;
terminate(function() {
recorder.blob = new Blob(arrayOfBuffers, {
type: 'video/webm'
});
callback(recorder.blob);
});
};
// for debugging
this.name = 'WebAssemblyRecorder';
this.toString = function() {
return this.name;
};
/**
* This method resets currently recorded data.
* @method
* @memberof WebAssemblyRecorder
* @example
* recorder.clearRecordedData();
*/
this.clearRecordedData = function() {
arrayOfBuffers = [];
isPaused = false;
this.blob = null;
// todo: if recording-ON then STOP it first
};
/**
* @property {Blob} blob - The recorded blob object.
* @memberof WebAssemblyRecorder
* @example
* recorder.stop(function(){
* var blob = recorder.blob;
* });
*/
this.blob = null;
}
if (typeof RecordRTC !== 'undefined') {
RecordRTC.WebAssemblyRecorder = WebAssemblyRecorder;
}
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__("DuR2"), __webpack_require__("W2nU")))
/***/ }),
/***/ "ncfW":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
if (true) {
module.exports = __webpack_require__("LpuX");
} else {
module.exports = require('./cjs/react-is.development.js');
}
/***/ }),
/***/ "nxuO":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAALBJREFUOE/dkj0OAWEQhp83Ua349GoOQBTcQ3AC9e4N9ga7jiNWtByBU/D5KY1YivWTbKGQmPaZeTKZecWXpdt8LbSloPPkEjOfaPjgC0G3yM2YH6YaqDGx4Bhw+rDI2aeqlvF/EGAmF7EFmi9HXPlEfTC5kA3QernT2qfq5V8gtorb44oNvs6OWJcyfhd8UbnARZZhtAseAzKfapzz0Oa85yTziUb/8MayqJZx/TwHV4vrphErqTZQAAAAAElFTkSuQmCC"
/***/ }),
/***/ "pBGO":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAhCAYAAADdy1suAAAAAXNSR0IArs4c6QAABKNJREFUSEuNlltMFFcYx38zuy4LcpVbQwPIpQ8taAJIk160NioaWlCsAVqTpq0Y9qV9aC0RnvrSRGlMJCSiAVJLQ4qoWEIqsJBIbWNLJESthsrFKqbWIqhFQWDXnebMZbPL7gLnaeZ/zvmdb77zXUYiwGiGKDN8oMA2IAuIk8AB3FOgX4GOaWgv1zSfIS1WOiBkFiqBzxUICXSwrv8tQ2UxfL94nRe4FdJdcA7IXAboNS3BmXn46EOYMSbc4BZIAy4CCcZkxLp1pJaVEb91KyFJSaAozNy5w/3ubm7V1/Pk5k3PA/pkyC+GZ0JUwd+CNRgGgAzxLgcFkVVTQ9r+/Uiy7Nd4l9PJSG0tVysqUJxOY01jKZS5wafgawWqVKjVyma7ndiNG1fkjX+6uviloMANl2BbCfRKbRC9ALeBUEHKbWggdd++FUGNRX8eOcLVAwfUVwl+K4HXpVNgU6BOiJHZ2eRdvuz1+bPj4/yUno7LoUWVZDJRMD5OcIL7KtS5zowMno6MqGtc8LLUAh3Au0LIOX6c9PJyL2vvnj7NpeJit5Zms7GhTrXDawwdPsy1gwdVTYYvBFi4IVkI74yNEZqa6rXhWmUlQ4cOqVpIYiI7rl9nVXi4D/jh4CA9OTmqrsB3AjwLBCNJ7Jmbw2SxeG36OS+P+z09qrYqIoKg6GhePXnS53LnJyf5MTbW8PN5ARZxZw0EPhcTw8LUlPsw8UU7btzAZLV6GTD34AHtcXGG1inAo2jJQf7oKGFp6qN7jB47xlR/P7ebmlTtzbY2Xiwq8nHFo8FB7LorgAYBPgO8J1bm1NWRbrP5bBo7cYIBm434LVvY3NvrMy8Ez8sDPhXh9okCjWIyMiuLvIEBn2z7o6qKoepqtl+5QkSmbxlRwy0zk6fDw/rd8ZLUBKstcBeIEuqG+nrSytSsdI/f9+7FsmYN2bW1fq31TBDAXgrb1VrRAl8C1eJZ1Im37HbiNm1yQ/7t6SEqNxdLZKQP+F5nJ78WFhop/VyBN96HfhV8AcwTcFGB1wx41tGjWhEymfxaKYrQcE2NmhQeReibUqgQG9xlswniLHDJiBAxGZ6ZqZbNF/SyqbhczBpls7FxcdlsL4HdkprRHmDx0gpJLugDUvyaGVg8PwG7P4N5Y4lPa2qGZDNcUFYO7wyHonwPqI/FxmlnIdmhWb52Kcsl6AqDXYuhAcFi4gcN2ifpBWrxARJ0z8Kuj2HO3+E+rvBc1Aopus+TFm22P4OdgaBLWmyAmiHVpEYkBrxXhkKjaQZy1ZIWG5v0Di7gwzIULAddkcUGXPg8FCYKtPq97PBr8VcgvwIifyNk7W/I4gCRgwrw3AXzMswEwaOd8GRFl9cOYQ5IcUKQKEwShJnAoqitTB0CLCLhPwmmnTBjhlvFsOB5gJfFuqXrAZMeZsFisQLxkugyoAiYAo+F7oJJCSZlmN4DWovWhxdYAVGf18tgViBRhtU6OEb0RUn9yWJawHV9AngorC/WOpF/sFBFfQ6BtQ7RYDUfq65waYcpMjg9XDHjgOkE+OttcP9nLRkVrRBhgfAFCBH+Nus+NmvgeQfMhMLjQFHyP/MffkAUiD/oAAAAAElFTkSuQmCC"
/***/ }),
/***/ "pdZy":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
var _interopRequireDefault = __webpack_require__("ouCL");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = Common;
__webpack_require__("UQ5M");
var _message2 = _interopRequireDefault(__webpack_require__("/qCn"));
var _rtcClient = _interopRequireDefault(__webpack_require__("adV+"));
var _shareClient = _interopRequireDefault(__webpack_require__("Xusr"));
var _presetting = _interopRequireDefault(__webpack_require__("2mw7"));
var _api = __webpack_require__("H/Zg");
function Common() {
var isCamOn = true;
var isMicOn = true;
var isScreenOn = false;
var isJoined = true;
var rtc = null;
var share = null;
var shareUserId = "";
var cameraId = "";
var micId = "";
var number = 0;
var videoThis = null;
var common = {
login: function login(roomId, mobilePhone, cid, name, _this) {
var _this2 = this;
(0, _presetting.default)(false, roomId, mobilePhone, cid, name, function (options) {
rtc = new _rtcClient.default(options, _this);
console.log(_this);
videoThis = _this;
_this2.join();
});
(0, _presetting.default)(true, roomId, mobilePhone, cid, name, function (options) {
shareUserId = options.userId;
share = new _shareClient.default(options);
});
// if ($("#userId").val() == "") {
// alert("用户名不能为空!");
// return;
// }
// if ($("#roomId").val() == "") {
// alert("房间号不能为空!");
// return;
// }
// presetting.login(false, (options) => {
// const options = {
// sdkAppId: 1400246643,
// userId: "user_43707228",
// userSig:
// "eJwtzMEKgzAQBNB-ydUia4y6Cr0Ueih4UqG9lWoSWaJVkrQUSv*9oh7nzTBf1pR1*FaWFYyHwA5rJqmenjSt-HLK3kWcQcY57gMnzWOeSbIiEgBcpKmIt8bTqBZNEFFEOeCm6jOTXTwFgQD7B-XLe95IDzc7TH2gz21QXU5j1155OdLQmUrWueZZMiBOxh3Z7w8UZDLR",
// roomId: "83999",
// };
// rtc = new RtcClient(options);
// // join();
// // });
// // presetting.login(true, (options) => {
// // shareUserId = options.userId;
// share = new ShareClient(options);
// });
},
join: function join() {
rtc.join();
// $("#login-root").hide();
// $("#room-root").show();
// $("#header-roomId").html("房间号: " + $("#roomId").val());
// $("#member-me")
// .find(".member-id")
// .html($("#userId").val() + "(我)");
},
leave: function leave(roomNo) {
// document
// .getElementById("main-video")
// .appendChild(document.getElementById("mask_main"));
// $("#mask_main").appendTo($("#main-video"));
/**
* 调用结束时间的接口
* 主要作用是服务端记录结束时间
*/
(0, _api.updateRoomEndTime)({
roomNo: roomNo
}).then(function (res) {
console.log(res);
});
rtc.leave();
share.leave();
_message2.default.info('您已离开房间');
},
publish: function publish() {
rtc.publish();
},
unpublish: function unpublish() {
rtc.unpublish();
},
muteAudio: function muteAudio() {
rtc.muteLocalAudio();
},
unmuteAudio: function unmuteAudio() {
rtc.unmuteLocalAudio();
},
muteVideo: function muteVideo() {
document.getElementById("mask_main").style.display = "flex";
// $("#mask_main").show();
rtc.muteLocalVideo();
},
unmuteVideo: function unmuteVideo() {
rtc.unmuteLocalVideo();
document.getElementById("mask_main").style.display = "none";
// $("#mask_main").hide();
},
startSharing: function startSharing() {
share.join();
},
stopSharing: function stopSharing() {
share.leave();
},
//主视频点击切换
mainVideoClick: function mainVideoClick() {
var mainVideo = document.getElementById("main-video");
if (document.getElementById("div-video-local").childNodes[0].getAttribute("id") == mainVideo.getAttribute("id")) {
return;
}
//释放main-video grid-area
// mainVideo.style.gridArea = "auto/auto/auto/auto";
// exchangeView(document.getElementById("main-video"), mainVideo);
// //将video-grid中第一个div设为main-video
// document.getElementsByClassName("video-box")[0].style.gridArea =
// "1/1/3/4";
mainVideo.style.height = '100%';
// mainVideo.style.paddingBottom='0';
var right = mainVideo;
var left = document.querySelector('#div-video-local').childNodes[0];
document.querySelector('#div-video-local').removeChild(left);
document.querySelector('#div-video-local').appendChild(right);
left.style.height = '200px';
// document
// .querySelector('#div-video-child')
// .removeChild(
// right
// );
document.querySelector('#div-video-child').appendChild(left);
//chromeM71以下会自动暂停,手动唤醒
if (getBroswer().broswer == "Chrome" && getBroswer().version < "72") {
rtc.resumeStreams();
}
// $("#main-video").on("click", () => {
// let mainVideo = $(".video-box").first();
// if ($("#main-video").is(mainVideo)) {
// return;
// }
// //释放main-video grid-area
// mainVideo.css("grid-area", "auto/auto/auto/auto");
// exchangeView($("#main-video"), mainVideo);
// //将video-grid中第一个div设为main-video
// $(".video-box").first().css("grid-area", "1/1/3/4");
// //chromeM71以下会自动暂停,手动唤醒
// if (getBroswer().broswer == "Chrome" && getBroswer().version < "72") {
// rtc.resumeStreams();
// }
// });
},
//共享屏幕按钮点击
screenShareClick: function screenShareClick() {
// throttle(() => {
if (!TRTC.isScreenShareSupported()) {
alert("当前浏览器不支持屏幕分享!");
return;
}
if (isScreenOn) {
// document
// .getElementById("screen-btn")
// .setAttribute(
// "src",
// require("../../../assets/images/prevention/screen-off.png")
// );
// $("#screen-btn").attr("src", "./img/screen-off.png");
this.stopSharing();
isScreenOn = false;
} else {
// document
// .getElementById("screen-btn")
// .setAttribute(
// "src",
// require("../../../assets/images/prevention/screen-on.png")
// );
// $("#screen-btn").attr("src", "./img/screen-on.png");
this.startSharing();
isScreenOn = true;
}
// }, 2000);
},
setBtnClickFuc: function setBtnClickFuc() {
//userid roomid规格
//$('#userId').on('input', function(e) {
// e.preventDefault();
// let val = $('#userId').val().slice(5);
// $('#userId').val('user_'+val.replace(/[^\d]/g,''));
//});
// $("#roomId").on("input", function (e) {
// e.preventDefault();
// let val = $("#roomId").val();
// $("#roomId").val(val.replace(/[^\d]/g, ""));
// });
//login
// $("#login-btn").click(() => {
// login();
// });
//open or close camera
// $("#video-btn").on("click", () => {
// if (isCamOn) {
// $("#video-btn").attr("src", "./img/big-camera-off.png");
// $("#video-btn").attr("title", "打开摄像头");
// $("#member-me")
// .find(".member-video-btn")
// .attr("src", "img/camera-off.png");
// isCamOn = false;
// muteVideo();
// } else {
// $("#video-btn").attr("src", "./img/big-camera-on.png");
// $("#video-btn").attr("title", "关闭摄像头");
// $("#member-me")
// .find(".member-video-btn")
// .attr("src", "img/camera-on.png");
// isCamOn = true;
// unmuteVideo();
// }
// });
//open or close microphone
// $("#mic-btn").on("click", () => {
// if (isMicOn) {
// $("#mic-btn").attr("src", "./img/big-mic-off.png");
// $("#mic-btn").attr("title", "打开麦克风");
// $("#member-me")
// .find(".member-audio-btn")
// .attr("src", "img/mic-off.png");
// isMicOn = false;
// muteAudio();
// } else {
// $("#mic-btn").attr("src", "./img/big-mic-on.png");
// $("#mic-btn").attr("title", "关闭麦克风");
// $("#member-me")
// .find(".member-audio-btn")
// .attr("src", "img/mic-on.png");
// isMicOn = true;
// unmuteAudio();
// }
// });
//share screen or not
// $("#screen-btn").on(
// "click",
// throttle(() => {
// if (!TRTC.isScreenShareSupported()) {
// alert("当前浏览器不支持屏幕分享!");
// return;
// }
// if ($("#screen-btn").attr("src") == "./img/screen-on.png") {
// $("#screen-btn").attr("src", "./img/screen-off.png");
// stopSharing();
// isScreenOn = false;
// } else {
// $("#screen-btn").attr("src", "./img/screen-on.png");
// startSharing();
// isScreenOn = true;
// }
// }, 2000)
// );
//logout
// $("#logout-btn").on("click", () => {
// leave();
// $("#room-root").hide();
// $("#login-root").show();
// });
//switch main video
// $("#main-video").on("click", () => {
// let mainVideo = $(".video-box").first();
// if ($("#main-video").is(mainVideo)) {
// return;
// }
// //释放main-video grid-area
// mainVideo.css("grid-area", "auto/auto/auto/auto");
// exchangeView($("#main-video"), mainVideo);
// //将video-grid中第一个div设为main-video
// $(".video-box").first().css("grid-area", "1/1/3/4");
// //chromeM71以下会自动暂停,手动唤醒
// if (getBroswer().broswer == "Chrome" && getBroswer().version < "72") {
// rtc.resumeStreams();
// }
// });
//chrome60以下不支持popover,防止error
if (getBroswer().broswer == "Chrome" && getBroswer().version < "60") return;
//开启popover
$(function () {
$('[data-toggle="popover"]').popover();
});
$("#camera").popover({
html: true,
content: function content() {
return $("#camera-option").html();
}
});
$("#microphone").popover({
html: true,
content: function content() {
return $("#mic-option").html();
}
});
$("#camera").on("click", function () {
$("#microphone").popover("hide");
$(".popover-body").find("div").attr("onclick", "setCameraId(this)");
});
$("#microphone").on("click", function () {
$("#camera").popover("hide");
$(".popover-body").find("div").attr("onclick", "setMicId(this)");
});
//点击body关闭popover
$("body").click(function () {
$("#camera").popover("hide");
$("#microphone").popover("hide");
});
//popover事件
$("#camera").on("show.bs.popover", function () {
$("#camera").attr("src", "./img/camera-on.png");
});
$("#camera").on("hide.bs.popover", function () {
$("#camera").attr("src", "./img/camera.png");
});
$("#microphone").on("show.bs.popover", function () {
$("#microphone").attr("src", "./img/mic-on.png");
});
$("#microphone").on("hide.bs.popover", function () {
$("#microphone").attr("src", "./img/mic.png");
});
},
setCameraId: function setCameraId(thisDiv) {
// cameraId = $(thisDiv).attr("id");
cameraId = thisDiv;
},
setMicId: function setMicId(thisDiv) {
micId = $(thisDiv).attr("id");
},
addVideoView: function addVideoView(id, videoThis) {
var isLocal = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var div = document.createElement("div");
div.setAttribute("id", id);
div.className = "video-box video-container";
div.style.justifyContent = "center";
number = number + 1;
document.getElementById("div-video-child").appendChild(div);
// div.onclick = ()=>{
// console.log(123);
// videoThis.setLayout(id)
// }
// document.getElementById("video-content").appendChild(div);
//设置监听
//切换排版
// div.onclick = () => {
// let list = document.querySelectorAll('.video-container');
// let left = document.getElementById('video-left');
// let right = document.getElementById('video-right');
// left.style.display = "block";
// right.style.display = "block";
// for (let i = 0; i < list.length; i++) {
// list[i].width = "100%";
// list[i].height = "100%";
// if (list[i] != div) {
// document.querySelector('#video-right').appendChild(list[i]);
// }
// }
// document.querySelector('#video-left').appendChild(div)
// //chromeM71以下会自动暂停,手动唤醒
// if (getBroswer().broswer == "Chrome" && getBroswer().version < "72") {
// rtc.resumeStreams();
// }
// };
//拖动事件
// let clientX = 0, clientY = 0, x = 0, y = 0, isDown = false,
// offsetLeft = 0, offsetTop = 0
// div.onmousedown = (e) => {
// isDown = true
// clientX = e.clientX
// clientY = e.clientY
// offsetLeft = document.getElementById(id).offsetLeft
// offsetTop = document.getElementById(id).offsetTop
// }
// div.onmousemove = (e) => {
// if (isDown == false) {
// return
// }
// //获取x坐标和y坐标
// var nx = e.clientX;
// var ny = e.clientY;
// // //计算移动后的左偏移量和顶部的偏移量
// var nl = nx - (clientX - offsetLeft);
// var nt = ny - (clientY - offsetTop);
// x = nl;
// y = nt;
// div.style.top = y + 'px'
// div.style.left = x + 'px'
// }
// div.onmouseup = (e) => {
// isDown = false
// }
},
addMemberView: function addMemberView(id) {
var memberElm = document.getElementById("member-me").cloneNode(true);
// let memberElm = $("#member-me").clone();
memberElm.setAttribute("id", id);
memberElm.getElementsByClassName("member-id")[0].innerHTML = id;
// memberElm.find("div.member-id").html(id);
memberElm.style.display = "flex";
// memberElm.css("display", "flex");
document.getElementById("member-list").appendChild(memberElm);
// memberElm.appendTo($("#member-list"));
},
removeView: function removeView(id) {
if (document.getElementById(id)) {
// document.getElementsByClassName("video-box")[0].style.gridArea =
// "1/1/3/4";
document.getElementById(id).remove();
if (document.getElementById('div-video-local').childNodes.length == 0) {
var mainVideo = document.getElementById("main-video");
mainVideo.style.height = '25%';
// mainVideo.style.paddingBottom='0';
var right = mainVideo;
document.querySelector('#div-video-local').appendChild(right);
}
}
// if ($("#" + id)[0]) {
// $("#" + id).remove();
// //将video-grid中第一个div设为main-video
// $(".video-box").first().css("grid-area", "1/1/3/4");
// }
},
isPC: function isPC() {
var userAgentInfo = navigator.userAgent;
var Agents = new Array("Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod");
var flag = true;
for (var v = 0; v < Agents.length; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
},
getCameraId: function getCameraId() {
var _this3 = this;
// return cameraId;
TRTC.getCameras().then(function (devices) {
// devices.forEach(device => {
// if (!cameraId) {
// return cameraId = device.deviceId;
// }
// // let div = $('');
// // div.attr('id', device.deviceId);
// // div.html(device.label);
// // div.appendTo('#camera-option');
// });
if (devices.length > 0) {
_this3.isCamOn = true;
cameraId = devices[0].deviceId;
return cameraId;
} else {
_this3.isCamOn = false;
console.error("您的电脑没有摄像头或不允许该浏览器使用");
_message2.default.warning("您的电脑没有摄像头或不允许该浏览器使用");
}
});
},
getMicrophoneId: function getMicrophoneId() {
var _this4 = this;
// return micId;
TRTC.getMicrophones().then(function (devices) {
console.log(devices);
// devices.forEach(device => {
// if (!Common.micId) {
// Common.micId = device.deviceId;
// }
// // let div = $('');
// // div.attr('id', device.deviceId);
// // div.html(device.label);
// // div.appendTo('#mic-option');
// });
if (devices.length > 0) {
_this4.isMicOn = true;
micId = devices[0].deviceId;
return micId;
} else {
_this4.isMicOn = false;
console.error("您的电脑没有麦克风或不允许该浏览器使用");
_message2.default.error("您的电脑没有麦克风或不允许该浏览器使用");
}
});
},
throttle: function throttle(func, delay) {
var timer = null;
var startTime = Date.now();
return function () {
var curTime = Date.now();
var remaining = delay - (curTime - startTime);
var context = this;
var args = arguments;
clearTimeout(timer);
if (remaining <= 0) {
func.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(function () {
console.log("duplicate click");
}, remaining);
}
};
},
isHidden: function isHidden() {
var hidden, visibilityChange;
if (typeof document.hidden !== "undefined") {
hidden = "hidden";
visibilityChange = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
return document[hidden];
},
isCamOn: isCamOn,
isMicOn: isMicOn,
isScreenOn: isScreenOn,
isJoined: isJoined,
rtc: rtc,
share: share,
shareUserId: shareUserId,
cameraId: cameraId,
micId: micId
};
function getBroswer() {
var sys = {};
var ua = navigator.userAgent.toLowerCase();
var s;
(s = ua.match(/edge\/([\d.]+)/)) ? sys.edge = s[1] : (s = ua.match(/rv:([\d.]+)\) like gecko/)) ? sys.ie = s[1] : (s = ua.match(/msie ([\d.]+)/)) ? sys.ie = s[1] : (s = ua.match(/firefox\/([\d.]+)/)) ? sys.firefox = s[1] : (s = ua.match(/chrome\/([\d.]+)/)) ? sys.chrome = s[1] : (s = ua.match(/opera.([\d.]+)/)) ? sys.opera = s[1] : (s = ua.match(/version\/([\d.]+).*safari/)) ? sys.safari = s[1] : 0;
if (sys.edge) return {
broswer: "Edge",
version: sys.edge
};
if (sys.ie) return {
broswer: "IE",
version: sys.ie
};
if (sys.firefox) return {
broswer: "Firefox",
version: sys.firefox
};
if (sys.chrome) return {
broswer: "Chrome",
version: sys.chrome
};
if (sys.opera) return {
broswer: "Opera",
version: sys.opera
};
if (sys.safari) return {
broswer: "Safari",
version: sys.safari
};
return {
broswer: "",
version: "0"
};
}
function throttle(func, delay) {
var timer = null;
var startTime = Date.now();
return function () {
var curTime = Date.now();
var remaining = delay - (curTime - startTime);
var context = this;
var args = arguments;
clearTimeout(timer);
if (remaining <= 0) {
func.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(function () {
console.log("duplicate click");
}, remaining);
}
};
}
//交换位置
function exchangeView(a, b) {
var ep1 = a.parentNode,
ep2 = b.parentNode,
index1 = Array.prototype.indexOf.call(ep1.children, a),
index2 = Array.prototype.indexOf.call(ep2.children, b);
ep2.insertBefore(a, ep2.children[index2]);
ep1.insertBefore(b, ep1.children[index1]);
}
return common;
}
/***/ }),
/***/ "qEAy":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAXxJREFUaEPtWT1Ow1AM/txEZWFiA3EHrsBeLgA7gjIjGGiqKk0HEHMrxA4XAGauwB0Q3VhgoUowSkuqgET1fhKSVzmz7efP/pw8fyE4/pDj+WMKoHd+vRlP4gsA22Be1wJFNAbw6Df9k97p/rOWbwHGlCaffMRPDF6ziUegV2/F3/pvENTpj27AvGuT/NyX6DYK2nv5WEE4GgA4ZnCzkDO+gxBoAuAyBfAypw01WlFw+KBzUHdw1fpMkrupD9E4CtobPwEM3xhY1YmpakvAO3XCIWcOUffIaKgXxSi/AyUDUK2mqV3pHTBNTNVPAKSVKmKOVCv+2046IB0w5U72QSuCv0XEMMUhM7D0M1D+VSJ3mWt43k54dnCvw8fqL3OuX6edX2icXyl1+F5HW6MFpk5ABEDV3RBdKOuA6EKaXBRdKF8w0YU06ZM3l4Vm6RcaC3YouQqFhEJKRPnbSCgkFKo7hUQXWtCh2U8+0YVmJapsoRFdyPItYuvuvKzyBZX/tzipRvsMAAAAAElFTkSuQmCC"
/***/ }),
/***/ "rm0p":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAAEEfUpiAAAAAXNSR0IArs4c6QAAAttJREFUWAnFl79rFEEUx+8SyaGRNFFMbMTGgKAg9oIgnFVae/8B27Sef4CV8T/Q3jpwZWxiIwixUYgEwer0YmFzfj+zM7tzs/P2bi8ePng77+f3zc7OvtntdDxNJpNfiBe4SJloeOBGPN6AY7UbRSAWJM8rcR+tG8K9r7MShK4oG+EClHaX1JSCkwIx3Y8V5taPDansCgOFw0FGlzAxZ5J/TXwsHkYxhShjjk7wcuNjjetOESaRyJ62pwzUTAKqlQopBEGlnmYERxhZ6xdBSccSSSiHICW0lSawWD3xOAlM1YGVmAaif84Zve3QAUnhCWSJADmmHn4SOGIRPjqkxS4bAJxauap2TSt5xGoGUuyzOD7/lsQRzfLr0q1qJ8n9zVLrjxE0ZW2JR0b2flmxSVAyb9lQTFfgHeG1XGvKcT4FDcRNxCbrZYHkaLMGd6ZAlJzb68zkGxeDipnIyaLl6ApVcg5voxO4ljDSuIGS0Lo2z2+CE3us9tiJueQ4qEl+CcB56NFMgPAOhFHVvkQVt5sA3kaBTtRyXJRwM7If1dpk5JxHfMxj2melF6GywiLJyhnEANZmsrDduVICIChyXpCimU5lR4qArDWhR+SbSJRfE/3MALWajFw1Ipac9gWZAYli6w2XqzWBNXsyBInb9KW2MwE7PxE5ZnXTuNhzv1KrMr6JHXPK1Rb2QNbGsfAukQcpoOnktvKxFweUBA6gtvQvJkDNPs1sz93K/7ns0Qz5VL/csr47bchRPrn3jHy671PxE8M/BoBzvy3dMACzZoF/MAoc849yIL6VzbSNXwVoe+f3HPAI+OL5IT7P2Tx/ySryp8SrKzpq/0jYEZ9VvqVL1NqhNm9BR8J3DZvi9+hLJmps+pr1Unok/Dwuox2DOf1JWS9fWRTcE9OeZ/01KcQkcsEoPkMr+FIqfwlLiyEIhM36ULwrvi2+7lmD+8051fhJ/E489HtLYjP9Bc0SAk/RIOKMAAAAAElFTkSuQmCC"
/***/ }),
/***/ "rpBe":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__style_index_less__ = __webpack_require__("vtiu");
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__style_index_less___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__style_index_less__);
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__index_less__ = __webpack_require__("uznb");
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__index_less___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__index_less__);
/***/ }),
/***/ "tiui":
/***/ (function(module, exports, __webpack_require__) {
var map = {
"./float-check.png": "Pe7w",
"./magnify-check.png": "9rO7",
"./minimize-check.png": "TGlF"
};
function webpackContext(req) {
return __webpack_require__(webpackContextResolve(req));
};
function webpackContextResolve(req) {
var id = map[req];
if(!(id + 1)) // check for number or string
throw new Error("Cannot find module '" + req + "'.");
return id;
};
webpackContext.keys = function webpackContextKeys() {
return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = "tiui";
/***/ }),
/***/ "uznb":
/***/ (function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ }),
/***/ "vD6o":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,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"
/***/ }),
/***/ "vzCy":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var R = typeof Reflect === 'object' ? Reflect : null
var ReflectApply = R && typeof R.apply === 'function'
? R.apply
: function ReflectApply(target, receiver, args) {
return Function.prototype.apply.call(target, receiver, args);
}
var ReflectOwnKeys
if (R && typeof R.ownKeys === 'function') {
ReflectOwnKeys = R.ownKeys
} else if (Object.getOwnPropertySymbols) {
ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target)
.concat(Object.getOwnPropertySymbols(target));
};
} else {
ReflectOwnKeys = function ReflectOwnKeys(target) {
return Object.getOwnPropertyNames(target);
};
}
function ProcessEmitWarning(warning) {
if (console && console.warn) console.warn(warning);
}
var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
return value !== value;
}
function EventEmitter() {
EventEmitter.init.call(this);
}
module.exports = EventEmitter;
module.exports.once = once;
// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;
EventEmitter.prototype._events = undefined;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype._maxListeners = undefined;
// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
var defaultMaxListeners = 10;
function checkListener(listener) {
if (typeof listener !== 'function') {
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
}
}
Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
}
defaultMaxListeners = arg;
}
});
EventEmitter.init = function() {
if (this._events === undefined ||
this._events === Object.getPrototypeOf(this)._events) {
this._events = Object.create(null);
this._eventsCount = 0;
}
this._maxListeners = this._maxListeners || undefined;
};
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
}
this._maxListeners = n;
return this;
};
function _getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}
EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return _getMaxListeners(this);
};
EventEmitter.prototype.emit = function emit(type) {
var args = [];
for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
var doError = (type === 'error');
var events = this._events;
if (events !== undefined)
doError = (doError && events.error === undefined);
else if (!doError)
return false;
// If there is no 'error' event listener then throw.
if (doError) {
var er;
if (args.length > 0)
er = args[0];
if (er instanceof Error) {
// Note: The comments on the `throw` lines are intentional, they show
// up in Node's output if this results in an unhandled exception.
throw er; // Unhandled 'error' event
}
// At least give some kind of context to the user
var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
err.context = er;
throw err; // Unhandled 'error' event
}
var handler = events[type];
if (handler === undefined)
return false;
if (typeof handler === 'function') {
ReflectApply(handler, this, args);
} else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
ReflectApply(listeners[i], this, args);
}
return true;
};
function _addListener(target, type, listener, prepend) {
var m;
var events;
var existing;
checkListener(listener);
events = target._events;
if (events === undefined) {
events = target._events = Object.create(null);
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener !== undefined) {
target.emit('newListener', type,
listener.listener ? listener.listener : listener);
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events;
}
existing = events[type];
}
if (existing === undefined) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener;
++target._eventsCount;
} else {
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
// If we've already got an array, just append.
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
}
// Check for listener leak
m = _getMaxListeners(target);
if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true;
// No error code for this since it is a Warning
// eslint-disable-next-line no-restricted-syntax
var w = new Error('Possible EventEmitter memory leak detected. ' +
existing.length + ' ' + String(type) + ' listeners ' +
'added. Use emitter.setMaxListeners() to ' +
'increase limit');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
ProcessEmitWarning(w);
}
}
return target;
}
EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false);
};
EventEmitter.prototype.on = EventEmitter.prototype.addListener;
EventEmitter.prototype.prependListener =
function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
function onceWrapper() {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
if (arguments.length === 0)
return this.listener.call(this.target);
return this.listener.apply(this.target, arguments);
}
}
function _onceWrap(target, type, listener) {
var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
var wrapped = onceWrapper.bind(state);
wrapped.listener = listener;
state.wrapFn = wrapped;
return wrapped;
}
EventEmitter.prototype.once = function once(type, listener) {
checkListener(listener);
this.on(type, _onceWrap(this, type, listener));
return this;
};
EventEmitter.prototype.prependOnceListener =
function prependOnceListener(type, listener) {
checkListener(listener);
this.prependListener(type, _onceWrap(this, type, listener));
return this;
};
// Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
var list, events, position, i, originalListener;
checkListener(listener);
events = this._events;
if (events === undefined)
return this;
list = events[type];
if (list === undefined)
return this;
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else {
delete events[type];
if (events.removeListener)
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') {
position = -1;
for (i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
originalListener = list[i].listener;
position = i;
break;
}
}
if (position < 0)
return this;
if (position === 0)
list.shift();
else {
spliceOne(list, position);
}
if (list.length === 1)
events[type] = list[0];
if (events.removeListener !== undefined)
this.emit('removeListener', type, originalListener || listener);
}
return this;
};
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
EventEmitter.prototype.removeAllListeners =
function removeAllListeners(type) {
var listeners, events, i;
events = this._events;
if (events === undefined)
return this;
// not listening for removeListener, no need to emit
if (events.removeListener === undefined) {
if (arguments.length === 0) {
this._events = Object.create(null);
this._eventsCount = 0;
} else if (events[type] !== undefined) {
if (--this._eventsCount === 0)
this._events = Object.create(null);
else
delete events[type];
}
return this;
}
// emit removeListener for all listeners on all events
if (arguments.length === 0) {
var keys = Object.keys(events);
var key;
for (i = 0; i < keys.length; ++i) {
key = keys[i];
if (key === 'removeListener') continue;
this.removeAllListeners(key);
}
this.removeAllListeners('removeListener');
this._events = Object.create(null);
this._eventsCount = 0;
return this;
}
listeners = events[type];
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
} else if (listeners !== undefined) {
// LIFO order
for (i = listeners.length - 1; i >= 0; i--) {
this.removeListener(type, listeners[i]);
}
}
return this;
};
function _listeners(target, type, unwrap) {
var events = target._events;
if (events === undefined)
return [];
var evlistener = events[type];
if (evlistener === undefined)
return [];
if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener];
return unwrap ?
unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}
EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
EventEmitter.listenerCount = function(emitter, type) {
if (typeof emitter.listenerCount === 'function') {
return emitter.listenerCount(type);
} else {
return listenerCount.call(emitter, type);
}
};
EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
var events = this._events;
if (events !== undefined) {
var evlistener = events[type];
if (typeof evlistener === 'function') {
return 1;
} else if (evlistener !== undefined) {
return evlistener.length;
}
}
return 0;
}
EventEmitter.prototype.eventNames = function eventNames() {
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
};
function arrayClone(arr, n) {
var copy = new Array(n);
for (var i = 0; i < n; ++i)
copy[i] = arr[i];
return copy;
}
function spliceOne(list, index) {
for (; index + 1 < list.length; index++)
list[index] = list[index + 1];
list.pop();
}
function unwrapListeners(arr) {
var ret = new Array(arr.length);
for (var i = 0; i < ret.length; ++i) {
ret[i] = arr[i].listener || arr[i];
}
return ret;
}
function once(emitter, name) {
return new Promise(function (resolve, reject) {
function errorListener(err) {
emitter.removeListener(name, resolver);
reject(err);
}
function resolver() {
if (typeof emitter.removeListener === 'function') {
emitter.removeListener('error', errorListener);
}
resolve([].slice.call(arguments));
};
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
if (name !== 'error') {
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
}
});
}
function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
if (typeof emitter.on === 'function') {
eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
}
}
function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
if (typeof emitter.on === 'function') {
if (flags.once) {
emitter.once(name, listener);
} else {
emitter.on(name, listener);
}
} else if (typeof emitter.addEventListener === 'function') {
// EventTarget does not have `error` event semantics like Node
// EventEmitters, we do not listen for `error` events here.
emitter.addEventListener(name, function wrapListener(arg) {
// IE does not have builtin `{ once: true }` support so we
// have to do it manually.
if (flags.once) {
emitter.removeEventListener(name, wrapListener);
}
listener(arg);
});
} else {
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
}
}
/***/ }),
/***/ "wEcu":
/***/ (function(module, exports) {
module.exports = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAA89JREFUaEPtmltIFGEUx8/5djVLUzIj6iki8qmop3roIdCCoMtDKQhFEEg7GwVdDNKd6XNmLFDwIWJ3th7sISLUh8Qg6EKB0OUhinoJqZ586X5RE2vnOzG7zjqu620dnRHcp2Hnu/x/55z9zvm+bxEW+AcXuH7ICsCv3CwW/QPbhBBbGGChl5ACaJAx9potL3rBTx3+nallHEBEMw4BiCgQrPJS+Li5Eb4AsLAuhzqd78YAJMWT6PCV8HEmZ1VOiDSAFTaJX7/f25ZHgF5E1gVAf7wFwmVE4gABbEzqQPgSLCneYIdTGkBpMnYJU9xPtYHewOrAVn78uMfiU6bj8fgy85P5yoZgAbZbbQg9GNGaahTRonVA0Gw9M2Qtqhw6763lx86uaEazIFE34oXzuhxuGQOgaDEuiC6mALBRlSWeK4CsxpoA6BQAXtEUqSHXcZz9JtI3GkJTAChNRoUQULK5vLSrurranEyUrMaGCSgfAf9qirTEcwCuR7cnBDxLugyhY1N5Wc1kEBE1SrZoXQm7kixn5QH5UnwbJczntihEvBWIhI5wRJHNur4DsEQqWqxVEJ1OQzC8oTaEjiFi2tr2O18CpFYq4yqQOJGGALimylIoE8K3AESEsm7Egah2FAKvaop00hlKvgWwRI5AtAHRUYcnWjUlfNb3IWQL5EQsoRs3gajG/o4hXFblcH0y1Py2CmVbafjjx0Gz591tAjo4CpFKgAsCwBIdj8fz+j6LTiLan4ZEVg8kLvkqD0yWSXl7e7757tsdAtqTrZ0vEtlUpQBvaytI9A11A0FlZlvPAVKFGZyzapupQHzpAVmN9hNAUS7iAZF0WWI59c3olHMtNAsPEAI+1RRph6cAbkzuxhg5e8CNyd0YYxHADSvOZgxZNxQSotEaAxm7qEVCavLZHtTNPfFshE7Ul2uxnQmAh9b7IEAll6UnngO0trYv/T74rQIANzGgCffOAnAYgN4uyQ9+MMW/YX4h/NEG9cwDvOn6ZtP815E+sJqG25LnVYG8Kt5Q+8ZTAMvyPwa+vp6JeIfg3hVFZVvOnKke8iyEIlpsLxB1T8Po2Zsg7tNl6a6HAMYFZ8k9YxBk9bocuuwZgHPFm7H4jJNDT37EiwCOs9tFD+QSw4shlC2E5vOCY248MI9XTBFtDvKAdcln/uz/QEBlIwnC9Us+QkqAgHuEuMb1TGyJnpdrVoTBYEHRenNooMfVWsheTeb8otsCKCleC/1D61ytRp3L4Vz91cAOIU0Jv7Tmm+l+oLRw5SO7Ch1XTueynvuhjysXcF6C/AdXXnde+gQhVQAAAABJRU5ErkJggg=="
/***/ }),
/***/ "x85o":
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = findDOMNode;
var _reactDom = _interopRequireDefault(__webpack_require__("O27J"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Return if a node is a DOM node. Else will return by `findDOMNode`
*/
function findDOMNode(node) {
if (node instanceof HTMLElement) {
return node;
}
return _reactDom.default.findDOMNode(node);
}
/***/ }),
/***/ "z+gd":
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* WEBPACK VAR INJECTION */(function(global) {/**
* A collection of shims that provide minimal functionality of the ES6 collections.
*
* These implementations are not meant to be used outside of the ResizeObserver
* modules as they cover only a limited range of use cases.
*/
/* eslint-disable require-jsdoc, valid-jsdoc */
var MapShim = (function () {
if (typeof Map !== 'undefined') {
return Map;
}
/**
* Returns index in provided array that matches the specified key.
*
* @param {Array