/**
* videojs-record
* @version 2.1.3
* @see https://github.com/collab-project/videojs-record
* @copyright 2014-2018 Collab
* @license MIT
*/
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.videojs || (g.videojs = {})).record = f()}})(function(){var define,module,exports;return (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o'
});
}
}]);
return AnimationDisplay;
}(Component);
Component.registerComponent('AnimationDisplay', AnimationDisplay);
exports.default = AnimationDisplay;
},{}],2:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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 _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* @file camera-button.js
* @since 2.0.0
*/
var Button = videojs.getComponent('Button');
var Component = videojs.getComponent('Component');
/**
* Button to toggle between create and retry snapshot image.
*
* @class
* @augments videojs.Button
*/
var CameraButton = function (_Button) {
_inherits(CameraButton, _Button);
function CameraButton() {
_classCallCheck(this, CameraButton);
return _possibleConstructorReturn(this, (CameraButton.__proto__ || Object.getPrototypeOf(CameraButton)).apply(this, arguments));
}
_createClass(CameraButton, [{
key: 'buildCSSClass',
/**
* Builds the default DOM `className`.
*
* @return {string}
* The DOM `className` for this object.
*/
value: function buildCSSClass() {
return 'vjs-camera-button vjs-control vjs-button vjs-icon-photo-camera';
}
/**
* Enable the `CameraButton` element so that it can be activated or clicked.
*/
}, {
key: 'enable',
value: function enable() {
_get(CameraButton.prototype.__proto__ || Object.getPrototypeOf(CameraButton.prototype), 'enable', this).call(this);
this.on(this.player_, 'startRecord', this.onStart);
this.on(this.player_, 'stopRecord', this.onStop);
}
/**
* Disable the `CameraButton` element so that it cannot be activated or clicked.
*/
}, {
key: 'disable',
value: function disable() {
_get(CameraButton.prototype.__proto__ || Object.getPrototypeOf(CameraButton.prototype), 'disable', this).call(this);
this.off(this.player_, 'startRecord', this.onStart);
this.off(this.player_, 'stopRecord', this.onStop);
}
/**
* This gets called when the button is clicked.
*
* @param {EventTarget~Event} event
* The `tap` or `click` event that caused this function to be
* called.
*
* @listens tap
* @listens click
*/
}, {
key: 'handleClick',
value: function handleClick(event) {
var recorder = this.player_.record();
if (!recorder.isProcessing()) {
// create snapshot
recorder.start();
} else {
// retry
recorder.retrySnapshot();
// reset camera button
this.onStop();
}
}
/**
* Add the vjs-icon-replay class to the element so it can change appearance.
*
* @param {EventTarget~Event} [event]
* The event that caused this function to run.
*
* @listens Player#startRecord
*/
}, {
key: 'onStart',
value: function onStart(event) {
// replace element class so it can change appearance
this.removeClass('vjs-icon-photo-camera');
this.addClass('vjs-icon-replay');
// change the button text
this.controlText('Retry');
}
/**
* Add the vjs-icon-photo-camera class to the element so it can change appearance.
*
* @param {EventTarget~Event} [event]
* The event that caused this function to run.
*
* @listens Player#stopRecord
*/
}, {
key: 'onStop',
value: function onStop(event) {
// replace element class so it can change appearance
this.removeClass('vjs-icon-replay');
this.addClass('vjs-icon-photo-camera');
// change the button text
this.controlText('Image');
}
}]);
return CameraButton;
}(Button);
/**
* The text that should display over the `CameraButton`s controls. Added for localization.
*
* @type {string}
* @private
*/
CameraButton.prototype.controlText_ = 'Image';
Component.registerComponent('CameraButton', CameraButton);
exports.default = CameraButton;
},{}],3:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* @file device-button.js
* @since 2.0.0
*/
var Button = videojs.getComponent('Button');
var Component = videojs.getComponent('Component');
/**
* Button to select recording device.
*
* @class
* @augments videojs.Button
*/
var DeviceButton = function (_Button) {
_inherits(DeviceButton, _Button);
function DeviceButton() {
_classCallCheck(this, DeviceButton);
return _possibleConstructorReturn(this, (DeviceButton.__proto__ || Object.getPrototypeOf(DeviceButton)).apply(this, arguments));
}
_createClass(DeviceButton, [{
key: 'handleClick',
/**
* This gets called when this button gets:
*
* - Clicked (via the `click` event, listening starts in the constructor)
* - Tapped (via the `tap` event, listening starts in the constructor)
*
* @param {EventTarget~Event} event
* The `keydown`, `tap`, or `click` event that caused this function to be
* called.
*
* @listens tap
* @listens click
*/
value: function handleClick(event) {
// open device dialog
this.player_.record().getDevice();
}
}]);
return DeviceButton;
}(Button);
/**
* The text that should display over the `DeviceButton`s controls. Added for localization.
*
* @type {string}
* @private
*/
DeviceButton.prototype.controlText_ = 'Device';
Component.registerComponent('DeviceButton', DeviceButton);
exports.default = DeviceButton;
},{}],4:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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 _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* @file record-canvas
* @since 2.0.0
*/
var Component = videojs.getComponent('Component');
/**
* Canvas for displaying snapshot image.
*
* @class
* @augments videojs.Component
*/
var RecordCanvas = function (_Component) {
_inherits(RecordCanvas, _Component);
function RecordCanvas() {
_classCallCheck(this, RecordCanvas);
return _possibleConstructorReturn(this, (RecordCanvas.__proto__ || Object.getPrototypeOf(RecordCanvas)).apply(this, arguments));
}
_createClass(RecordCanvas, [{
key: 'createEl',
/**
* Create the `RecordCanvas`s DOM element.
*
* @return {Element}
* The dom element that gets created.
*/
value: function createEl() {
return _get(RecordCanvas.prototype.__proto__ || Object.getPrototypeOf(RecordCanvas.prototype), 'createEl', this).call(this, 'div', {
className: 'vjs-record-canvas',
innerHTML: ''
});
}
}]);
return RecordCanvas;
}(Component);
Component.registerComponent('RecordCanvas', RecordCanvas);
exports.default = RecordCanvas;
},{}],5:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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 _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* @file record-indicator.js
* @since 2.0.0
*/
var Component = videojs.getComponent('Component');
/**
* Icon indicating recording is active.
*
* @class
* @augments videojs.Component
*/
var RecordIndicator = function (_Component) {
_inherits(RecordIndicator, _Component);
/**
* The constructor function for the class.
*
* @private
* @param {(videojs.Player|Object)} player - Video.js player instance.
* @param {Object} options - Player options.
*/
function RecordIndicator(player, options) {
_classCallCheck(this, RecordIndicator);
var _this = _possibleConstructorReturn(this, (RecordIndicator.__proto__ || Object.getPrototypeOf(RecordIndicator)).call(this, player, options));
_this.enable();
return _this;
}
/**
* Create the `RecordIndicator`s DOM element.
*
* @return {Element}
* The dom element that gets created.
*/
_createClass(RecordIndicator, [{
key: 'createEl',
value: function createEl() {
return _get(RecordIndicator.prototype.__proto__ || Object.getPrototypeOf(RecordIndicator.prototype), 'createEl', this).call(this, 'div', {
className: 'vjs-record-indicator vjs-control',
dir: 'ltr'
});
}
/**
* Enable event handlers.
*/
}, {
key: 'enable',
value: function enable() {
this.on(this.player_, 'startRecord', this.show);
this.on(this.player_, 'stopRecord', this.hide);
}
/**
* Disable event handlers.
*/
}, {
key: 'disable',
value: function disable() {
this.off(this.player_, 'startRecord', this.show);
this.off(this.player_, 'stopRecord', this.hide);
}
}]);
return RecordIndicator;
}(Component);
Component.registerComponent('RecordIndicator', RecordIndicator);
exports.default = RecordIndicator;
},{}],6:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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 _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* @file record-toggle.js
* @since 2.0.0
*/
var Button = videojs.getComponent('Button');
var Component = videojs.getComponent('Component');
/**
* Button to toggle between start and stop recording.
*
* @class
* @augments videojs.Button
*/
var RecordToggle = function (_Button) {
_inherits(RecordToggle, _Button);
function RecordToggle() {
_classCallCheck(this, RecordToggle);
return _possibleConstructorReturn(this, (RecordToggle.__proto__ || Object.getPrototypeOf(RecordToggle)).apply(this, arguments));
}
_createClass(RecordToggle, [{
key: 'buildCSSClass',
/**
* Builds the default DOM `className`.
*
* @return {string}
* The DOM `className` for this object.
*/
value: function buildCSSClass() {
return 'vjs-record-button vjs-control vjs-button vjs-icon-record-start';
}
/**
* Enable the `RecordToggle` element so that it can be activated or clicked.
*/
}, {
key: 'enable',
value: function enable() {
_get(RecordToggle.prototype.__proto__ || Object.getPrototypeOf(RecordToggle.prototype), 'enable', this).call(this);
this.on(this.player_, 'startRecord', this.onStart);
this.on(this.player_, 'stopRecord', this.onStop);
}
/**
* Disable the `RecordToggle` element so that it cannot be activated or clicked.
*/
}, {
key: 'disable',
value: function disable() {
_get(RecordToggle.prototype.__proto__ || Object.getPrototypeOf(RecordToggle.prototype), 'disable', this).call(this);
this.off(this.player_, 'startRecord', this.onStart);
this.off(this.player_, 'stopRecord', this.onStop);
}
/**
* This gets called when the button is clicked.
*
* @param {EventTarget~Event} event
* The `tap` or `click` event that caused this function to be
* called.
*
* @listens tap
* @listens click
*/
}, {
key: 'handleClick',
value: function handleClick(event) {
var recorder = this.player_.record();
if (!recorder.isRecording()) {
recorder.start();
} else {
recorder.stop();
}
}
/**
* Add the vjs-icon-record-stop class to the element so it can change appearance.
*
* @param {EventTarget~Event} [event]
* The event that caused this function to run.
*
* @listens Player#startRecord
*/
}, {
key: 'onStart',
value: function onStart(event) {
// replace element class so it can change appearance
this.removeClass('vjs-icon-record-start');
this.addClass('vjs-icon-record-stop');
// change the button text
this.controlText('Stop');
}
/**
* Add the vjs-icon-record-start class to the element so it can change appearance.
*
* @param {EventTarget~Event} [event]
* The event that caused this function to run.
*
* @listens Player#stopRecord
*/
}, {
key: 'onStop',
value: function onStop(event) {
// replace element class so it can change appearance
this.removeClass('vjs-icon-record-stop');
this.addClass('vjs-icon-record-start');
// change the button text
this.controlText('Record');
}
}]);
return RecordToggle;
}(Button);
/**
* The text that should display over the `RecordToggle`s controls. Added for localization.
*
* @type {string}
* @private
*/
RecordToggle.prototype.controlText_ = 'Record';
Component.registerComponent('RecordToggle', RecordToggle);
exports.default = RecordToggle;
},{}],7:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* @file defaults.js
* @since 2.0.0
*/
//plugin defaults
var pluginDefaultOptions = {
// Single snapshot image.
image: false,
// Include audio in the recorded clip.
audio: false,
// Include video in the recorded clip.
video: false,
// Animated GIF.
animation: false,
// Maximum length of the recorded clip.
maxLength: 10,
// Width of the recorded video frames.
frameWidth: 320,
// Height of the recorded video frames.
frameHeight: 240,
// Enables console logging for debugging purposes.
debug: false,
// The mime type for the video recorder. Default to 'video/webm'.
// Use 'video/mp4' (Firefox) or 'video/webm;codecs=H264' (Chrome 52 and
// newer) for MP4.
videoMimeType: 'video/webm',
// Video recorder type to use. This allows you to specify an alternative
// recorder class, e.g. WhammyRecorder. Defaults to 'auto' which let's
// recordrtc specify the best available recorder type.
videoRecorderType: 'auto',
// Audio recording library to use. Legal values are 'recordrtc',
// 'libvorbis.js', 'opus-recorder', 'lamejs' and 'recorder.js'.
audioEngine: 'recordrtc',
// Audio recorder type to use. This allows you to specify an alternative
// recorder class, e.g. StereoAudioRecorder. Defaults to 'auto' which let's
// recordrtc specify the best available recorder type. Currently this
// setting is only used with the 'recordrtc' audioEngine.
audioRecorderType: 'auto',
// The mime type for the audio recorder. Defaults to 'auto' which will pick
// the best option available in the browser (e.g. either 'audio/wav',
// 'audio/ogg' or 'audio/webm').
audioMimeType: 'auto',
// The size of the audio buffer (in sample-frames) which needs to
// be processed each time onprocessaudio is called.
// 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.
// Legal values are 256, 512, 1024, 2048, 4096, 8192 or 16384.
audioBufferSize: 4096,
// The audio 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.
audioSampleRate: 44100,
// The audio bitrate in kbps (only used in lamejs plugin).
audioBitRate: 128,
// Allows you to record single-channel audio, which can reduce the
// filesize.
audioChannels: 2,
// URL for the audio worker.
audioWorkerURL: '',
// Frame rate in frames per second.
animationFrameRate: 200,
// 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.
animationQuality: 10,
// Accepts numbers in milliseconds; use this to force intervals-based blobs.
timeSlice: 0
};
exports.default = pluginDefaultOptions;
},{}],8:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
/**
* @file record-engine.js
* @since 2.0.0
*/
var Component = videojs.getComponent('Component');
// supported recorder plugin engines
var RECORDRTC = 'recordrtc';
var LIBVORBISJS = 'libvorbis.js';
var RECORDERJS = 'recorder.js';
var LAMEJS = 'lamejs';
var OPUSRECORDER = 'opus-recorder';
/**
* Base class for recorder backends.
* @class
* @augments videojs.Component
*/
var RecordEngine = function (_Component) {
_inherits(RecordEngine, _Component);
/**
* Creates an instance of this class.
*
* @param {Player} player
* The `Player` that this class should be attached to.
*
* @param {Object} [options]
* The key/value store of player options.
*/
function RecordEngine(player, options) {
_classCallCheck(this, RecordEngine);
// auto mixin the evented mixin (required since video.js v6.6.0)
options.evented = true;
return _possibleConstructorReturn(this, (RecordEngine.__proto__ || Object.getPrototypeOf(RecordEngine)).call(this, player, options));
}
/**
* Remove any temporary data and references to streams.
* @private
*/
_createClass(RecordEngine, [{
key: 'dispose',
value: function dispose() {
// dispose previous recording
if (this.recordedData !== undefined) {
URL.revokeObjectURL(this.recordedData);
}
}
/**
* Add filename and timestamp to recorded file object.
*
* @param {(blob|file)} fileObj - Blob or File object.
*/
}, {
key: 'addFileInfo',
value: function addFileInfo(fileObj) {
if (fileObj instanceof Blob || fileObj instanceof File) {
// set modification date
var now = new Date();
try {
fileObj.lastModified = now.getTime();
fileObj.lastModifiedDate = now;
} catch (e) {
if (e instanceof TypeError) {
// ignore: setting getter-only property "lastModifiedDate"
} else {
// re-raise error
throw e;
}
}
// guess extension name from mime type, e.g. audio/ogg, but
// any extension is valid here. Chrome also accepts extended
// mime types like video/webm;codecs=h264,vp9,opus
var fileExtension = '.' + fileObj.type.split('/')[1];
if (fileExtension.indexOf(';') > -1) {
fileExtension = fileExtension.split(';')[0];
}
// use timestamp in filename, e.g. 1451180941326.ogg
try {
fileObj.name = now.getTime() + fileExtension;
} catch (e) {
if (e instanceof TypeError) {
// ignore: setting getter-only property "name"
} else {
// re-raise error
throw e;
}
}
}
}
/**
* Invoked when recording is stopped and resulting stream is available.
*
* @param {blob} data - Reference to the recorded Blob.
*/
}, {
key: 'onStopRecording',
value: function onStopRecording(data) {
this.recordedData = data;
// add filename and timestamp to recorded file object
this.addFileInfo(this.recordedData);
// remove reference to recorded stream
this.dispose();
// notify listeners
this.trigger('recordComplete');
}
/**
* Show save as dialog in browser so the user can store the recorded media
* locally.
*
* @param {object} name - Object with names for the particular blob(s)
* you want to save. File extensions are added automatically. For
* example: {'video': 'name-of-video-file'}. Supported keys are
* 'audio', 'video' and 'gif'.
*/
}, {
key: 'saveAs',
value: function saveAs(name) {
var fileName = name[Object.keys(name)[0]];
if (typeof navigator.msSaveOrOpenBlob !== 'undefined') {
return navigator.msSaveOrOpenBlob(this.recordedData, fileName);
} else if (typeof navigator.msSaveBlob !== 'undefined') {
return navigator.msSaveBlob(this.recordedData, fileName);
}
var hyperlink = document.createElement('a');
hyperlink.href = URL.createObjectURL(this.recordedData);
hyperlink.download = fileName;
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);
}
}]);
return RecordEngine;
}(Component);
// expose component for external plugins
videojs.RecordEngine = RecordEngine;
Component.registerComponent('RecordEngine', RecordEngine);
exports.RecordEngine = RecordEngine;
exports.RECORDRTC = RECORDRTC;
exports.LIBVORBISJS = LIBVORBISJS;
exports.RECORDERJS = RECORDERJS;
exports.LAMEJS = LAMEJS;
exports.OPUSRECORDER = OPUSRECORDER;
},{}],9:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* @file record-mode.js
* @since 2.0.0
*/
// recorder modes
var IMAGE_ONLY = 'image_only';
var AUDIO_ONLY = 'audio_only';
var VIDEO_ONLY = 'video_only';
var AUDIO_VIDEO = 'audio_video';
var ANIMATION = 'animation';
var getRecorderMode = function getRecorderMode(image, audio, video, animation) {
if (isModeEnabled(image)) {
return IMAGE_ONLY;
} else if (isModeEnabled(animation)) {
return ANIMATION;
} else if (isModeEnabled(audio) && !isModeEnabled(video)) {
return AUDIO_ONLY;
} else if (isModeEnabled(audio) && isModeEnabled(video)) {
return AUDIO_VIDEO;
} else if (!isModeEnabled(audio) && isModeEnabled(video)) {
return VIDEO_ONLY;
}
};
/**
* Return boolean indicating whether mode is enabled or not.
*/
var isModeEnabled = function isModeEnabled(mode) {
return mode === Object(mode) || mode === true;
};
exports.getRecorderMode = getRecorderMode;
exports.IMAGE_ONLY = IMAGE_ONLY;
exports.AUDIO_ONLY = AUDIO_ONLY;
exports.VIDEO_ONLY = VIDEO_ONLY;
exports.AUDIO_VIDEO = AUDIO_VIDEO;
exports.ANIMATION = ANIMATION;
},{}],10:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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 _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
var _recordEngine = require('./record-engine');
var _detectBrowser = require('../utils/detect-browser');
var _recordMode = require('./record-mode');
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
* @file record-rtc.js
* @since 2.0.0
*/
var Component = videojs.getComponent('Component');
/**
* Engine used with the MRecordRTC class in the RecordRTC library.
*
* @class
* @augments videojs.RecordEngine
*/
var RecordRTCEngine = function (_RecordEngine) {
_inherits(RecordRTCEngine, _RecordEngine);
function RecordRTCEngine() {
_classCallCheck(this, RecordRTCEngine);
return _possibleConstructorReturn(this, (RecordRTCEngine.__proto__ || Object.getPrototypeOf(RecordRTCEngine)).apply(this, arguments));
}
_createClass(RecordRTCEngine, [{
key: 'setup',
/**
* Setup recording engine.
*/
value: function setup(stream, mediaType, debug) {
this.inputStream = stream;
this.mediaType = mediaType;
this.debug = debug;
// setup RecordRTC
this.engine = new RecordRTC.MRecordRTC();
this.engine.mediaType = this.mediaType;
this.engine.disableLogs = !this.debug;
this.engine.mimeType = this.mimeType;
// audio settings
this.engine.bufferSize = this.bufferSize;
this.engine.sampleRate = this.sampleRate;
this.engine.numberOfAudioChannels = this.audioChannels;
// video/canvas settings
this.engine.video = this.video;
this.engine.canvas = this.canvas;
// animated gif settings
this.engine.quality = this.quality;
this.engine.frameRate = this.frameRate;
if (this.onTimeStamp !== undefined) {
this.engine.timeSlice = this.timeSlice;
this.engine.onTimeStamp = this.onTimeStamp;
}
// connect stream to recording engine
this.engine.addStream(this.inputStream);
}
/**
* Remove any temporary data and references to streams.
*/
}, {
key: 'dispose',
value: function dispose() {
_get(RecordRTCEngine.prototype.__proto__ || Object.getPrototypeOf(RecordRTCEngine.prototype), 'dispose', this).call(this);
if (typeof this.engine.destroy === 'function') {
this.engine.destroy();
}
}
/**
* Start recording.
*/
}, {
key: 'start',
value: function start() {
this.engine.startRecording();
}
/**
* Stop recording. Result will be available async when onStopRecording
* is called.
*/
}, {
key: 'stop',
value: function stop() {
this.engine.stopRecording(this.onStopRecording.bind(this));
}
/**
* Pause recording.
*/
}, {
key: 'pause',
value: function pause() {
this.engine.pauseRecording();
}
/**
* Resume recording.
*/
}, {
key: 'resume',
value: function resume() {
this.engine.resumeRecording();
}
/**
* Show save as dialog in browser so the user can store the recorded media
* locally.
*
* @param {object} name - Object with names for the particular blob(s)
* you want to save. File extensions are added automatically. For
* example: {'video': 'name-of-video-file'}. Supported keys are
* 'audio', 'video' and 'gif'.
*/
}, {
key: 'saveAs',
value: function saveAs(name) {
if (this.engine && name !== undefined) {
this.engine.save(name);
}
}
/**
* Invoked when recording is stopped and resulting stream is available.
*
* @private
* @param {string} audioVideoURL - Reference to the recorded Blob
* object, e.g. 'blob:http://localhost:8080/10100016-4248-9949-b0d6-0bb40db56eba'
* @param {string} type - Media type, eg. 'video' or 'audio'.
*/
}, {
key: 'onStopRecording',
value: function onStopRecording(audioVideoURL, type) {
var _this2 = this;
// store reference to recorded stream URL
this.mediaURL = audioVideoURL;
// store reference to recorded stream data
var recordType = this.player().record().getRecordType();
this.engine.getBlob(function (recording) {
switch (recordType) {
case _recordMode.AUDIO_ONLY:
_this2.recordedData = recording.audio;
_this2.addFileInfo(_this2.recordedData);
// notify listeners
_this2.trigger('recordComplete');
break;
case _recordMode.VIDEO_ONLY:
case _recordMode.AUDIO_VIDEO:
// when recording both audio and video, recordrtc
// calls this twice on chrome, first with audio data
// and then with video data.
// on firefox it's called once but with a single
// blob that includes both audio and video data.
if (recording.video !== undefined) {
// data is video-only but on firefox audio+video
_this2.recordedData = recording.video;
// on the chrome browser two blobs are created
// containing the separate audio/video streams.
if (recordType === _recordMode.AUDIO_VIDEO && (0, _detectBrowser.isChrome)()) {
// store both audio and video
_this2.recordedData = recording;
for (var mtype in _this2.recordedData) {
_this2.addFileInfo(_this2.recordedData[mtype]);
}
} else {
_this2.addFileInfo(_this2.recordedData);
}
// notify listeners
_this2.trigger('recordComplete');
}
break;
case _recordMode.ANIMATION:
_this2.recordedData = recording.gif;
_this2.addFileInfo(_this2.recordedData);
// notify listeners
_this2.trigger('recordComplete');
break;
}
});
}
}]);
return RecordRTCEngine;
}(_recordEngine.RecordEngine);
// expose plugin
videojs.RecordRTCEngine = RecordRTCEngine;
Component.registerComponent('RecordRTCEngine', RecordRTCEngine);
exports.default = RecordRTCEngine;
},{"../utils/detect-browser":12,"./record-engine":8,"./record-mode":9}],11:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* @file browser-shim.js
* @since 2.0.0
*/
var setSrcObject = function setSrcObject(stream, element, ignoreCreateObjectURL) {
if ('createObjectURL' in URL && !ignoreCreateObjectURL) {
try {
element.src = URL.createObjectURL(stream);
} catch (e) {
setSrcObject(stream, element, true);
return;
}
} else if ('srcObject' in element) {
element.srcObject = stream;
} else if ('mozSrcObject' in element) {
element.mozSrcObject = stream;
} else {
console.log('createObjectURL/srcObject both are not supported.');
}
};
exports.default = setSrcObject;
},{}],12:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isSafari = exports.isChrome = exports.isOpera = exports.isEdge = exports.detectBrowser = undefined;
var _window = require('global/window');
var _window2 = _interopRequireDefault(_window);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Browser detector.
*
* @private
* @return {object} result containing browser, version and minVersion
* properties.
*/
var detectBrowser = function detectBrowser() {
// returned result object
var result = {};
result.browser = null;
result.version = null;
result.minVersion = null;
// fail early if it's not a browser
if (typeof _window2.default === 'undefined' || !_window2.default.navigator) {
result.browser = 'Not a supported browser.';
return result;
}
// Firefox
if (navigator.mozGetUserMedia) {
result.browser = 'firefox';
result.version = extractVersion(navigator.userAgent, /Firefox\/(\d+)\./, 1);
result.minVersion = 31;
} else if (navigator.webkitGetUserMedia) {
// Chrome, Chromium, Webview, Opera
if (_window2.default.webkitRTCPeerConnection) {
result.browser = 'chrome';
result.version = extractVersion(navigator.userAgent, /Chrom(e|ium)\/(\d+)\./, 2);
result.minVersion = 38;
} else {
// Safari (in an unpublished version) or unknown webkit-based.
if (navigator.userAgent.match(/Version\/(\d+).(\d+)/)) {
result.browser = 'safari';
result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1);
result.minVersion = 11;
} else {
// unknown webkit-based browser.
result.browser = 'Unsupported webkit-based browser ' + 'with GUM support but no WebRTC support.';
return result;
}
}
// Edge
} else if (navigator.mediaDevices && navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)) {
result.browser = 'edge';
result.version = extractVersion(navigator.userAgent, /Edge\/(\d+).(\d+)$/, 2);
result.minVersion = 10547;
} else if (navigator.mediaDevices && navigator.userAgent.match(/AppleWebKit\/(\d+)\./)) {
// Safari, with webkitGetUserMedia removed.
result.browser = 'safari';
result.version = extractVersion(navigator.userAgent, /AppleWebKit\/(\d+)\./, 1);
} else {
// default fallthrough: not supported.
result.browser = 'Not a supported browser.';
return result;
}
return result;
};
/**
* Extract browser version out of the provided user agent string.
*
* @private
* @param {!string} uastring - userAgent string.
* @param {!string} expr - Regular expression used as match criteria.
* @param {!number} pos - position in the version string to be
* returned.
* @return {!number} browser version.
*/
/**
* @file detect-browser.js
* @since 2.0.0
*/
var extractVersion = function extractVersion(uastring, expr, pos) {
var match = uastring.match(expr);
return match && match.length >= pos && parseInt(match[pos], 10);
};
var isEdge = function isEdge() {
return detectBrowser().browser === 'edge';
};
var isSafari = function isSafari() {
return detectBrowser().browser === 'safari';
};
var isOpera = function isOpera() {
return !!_window2.default.opera || navigator.userAgent.indexOf('OPR/') !== -1;
};
var isChrome = function isChrome() {
return detectBrowser().browser === 'chrome';
};
exports.detectBrowser = detectBrowser;
exports.isEdge = isEdge;
exports.isOpera = isOpera;
exports.isChrome = isChrome;
exports.isSafari = isSafari;
},{"global/window":14}],13:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* @file format-time.js
* @since 2.0.0
*/
/**
* Format seconds as a time string, H:MM:SS, M:SS or M:SS:MMM.
*
* Supplying a guide (in seconds) will force a number of leading zeros
* to cover the length of the guide.
*
* @param {number} seconds - Number of seconds to be turned into a
* string.
* @param {number} guide - Number (in seconds) to model the string
* after.
* @param {number} msDisplayMax - Number (in milliseconds) to model the string
* after.
* @return {string} Time formatted as H:MM:SS, M:SS or M:SS:MMM, e.g.
* 0:00:12.
* @private
*/
var formatTime = function formatTime(seconds, guide, msDisplayMax) {
// Default to using seconds as guide
seconds = seconds < 0 ? 0 : seconds;
guide = guide || seconds;
var s = Math.floor(seconds % 60),
m = Math.floor(seconds / 60 % 60),
h = Math.floor(seconds / 3600),
gm = Math.floor(guide / 60 % 60),
gh = Math.floor(guide / 3600),
ms = Math.floor((seconds - s) * 1000);
// handle invalid times
if (isNaN(seconds) || seconds === Infinity) {
// '-' is false for all relational operators (e.g. <, >=) so this
// setting will add the minimum number of fields specified by the
// guide
h = m = s = ms = '-';
}
// Check if we need to show milliseconds
if (guide > 0 && guide < msDisplayMax) {
if (ms < 100) {
if (ms < 10) {
ms = '00' + ms;
} else {
ms = '0' + ms;
}
}
ms = ':' + ms;
} else {
ms = '';
}
// Check if we need to show hours
h = h > 0 || gh > 0 ? h + ':' : '';
// If hours are showing, we may need to add a leading zero.
// Always show at least one digit of minutes.
m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':';
// Check if leading zero is need for seconds
s = s < 10 ? '0' + s : s;
return h + m + s + ms;
};
exports.default = formatTime;
},{}],14:[function(require,module,exports){
(function (global){
var win;
if (typeof window !== "undefined") {
win = window;
} else if (typeof global !== "undefined") {
win = global;
} else if (typeof self !== "undefined"){
win = self;
} else {
win = {};
}
module.exports = win;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],15:[function(require,module,exports){
(function (global){
'use strict';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
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 _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
var _animationDisplay = require('./controls/animation-display');
var _animationDisplay2 = _interopRequireDefault(_animationDisplay);
var _recordCanvas = require('./controls/record-canvas');
var _recordCanvas2 = _interopRequireDefault(_recordCanvas);
var _deviceButton = require('./controls/device-button');
var _deviceButton2 = _interopRequireDefault(_deviceButton);
var _cameraButton = require('./controls/camera-button');
var _cameraButton2 = _interopRequireDefault(_cameraButton);
var _recordToggle = require('./controls/record-toggle');
var _recordToggle2 = _interopRequireDefault(_recordToggle);
var _recordIndicator = require('./controls/record-indicator');
var _recordIndicator2 = _interopRequireDefault(_recordIndicator);
var _defaults = require('./defaults');
var _defaults2 = _interopRequireDefault(_defaults);
var _formatTime = require('./utils/format-time');
var _formatTime2 = _interopRequireDefault(_formatTime);
var _browserShim = require('./utils/browser-shim');
var _browserShim2 = _interopRequireDefault(_browserShim);
var _detectBrowser = require('./utils/detect-browser');
var _recordRtc = require('./engine/record-rtc');
var _recordRtc2 = _interopRequireDefault(_recordRtc);
var _recordEngine = require('./engine/record-engine');
var _recordMode = require('./engine/record-mode');
var _video = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
var _video2 = _interopRequireDefault(_video);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
* @file videojs.record.js
*
* The main file for the videojs-record project.
* MIT license: https://github.com/collab-project/videojs-record/blob/master/LICENSE
*/
var Plugin = _video2.default.getPlugin('plugin');
var Player = _video2.default.getComponent('Player');
var AUTO = 'auto';
// monkey-patch play (#152)
Player.prototype.play = function play() {
var retval = this.techGet_('play');
// silence errors (unhandled promise from play)
if (retval !== undefined && typeof retval.then === 'function') {
retval.then(null, function (e) {});
}
return retval;
};
/**
* Record audio/video/images using the Video.js player.
*
* @class
* @augments videojs.Plugin
*/
var Record = function (_Plugin) {
_inherits(Record, _Plugin);
/**
* The constructor function for the class.
*
* @param {(videojs.Player|Object)} player
* @param {Object} options - Player options.
*/
function Record(player, options) {
_classCallCheck(this, Record);
// setup plugin options
var _this = _possibleConstructorReturn(this, (Record.__proto__ || Object.getPrototypeOf(Record)).call(this, player, options));
_this.loadOptions();
// (re)set recorder state
_this.resetState();
// add device button with icon based on type
var deviceIcon = 'av-perm';
switch (_this.getRecordType()) {
case _recordMode.IMAGE_ONLY:
case _recordMode.VIDEO_ONLY:
case _recordMode.ANIMATION:
deviceIcon = 'video-perm';
break;
case _recordMode.AUDIO_ONLY:
deviceIcon = 'audio-perm';
break;
}
_deviceButton2.default.prototype.buildCSSClass = function () {
// use dynamic icon class
return 'vjs-device-button vjs-control vjs-icon-' + deviceIcon;
};
player.deviceButton = new _deviceButton2.default(player, options);
player.addChild(player.deviceButton);
// add blinking record indicator
player.recordIndicator = new _recordIndicator2.default(player, options);
player.recordIndicator.hide();
player.addChild(player.recordIndicator);
// add canvas for recording and displaying image
player.recordCanvas = new _recordCanvas2.default(player, options);
player.recordCanvas.hide();
player.addChild(player.recordCanvas);
// add image for animation display
player.animationDisplay = new _animationDisplay2.default(player, options);
player.animationDisplay.hide();
player.addChild(player.animationDisplay);
// add camera button
player.cameraButton = new _cameraButton2.default(player, options);
player.cameraButton.hide();
// add record toggle
player.recordToggle = new _recordToggle2.default(player, options);
player.recordToggle.hide();
// wait until player ui is ready
_this.player.one('ready', _this.setupUI.bind(_this));
return _this;
}
/**
* Setup plugin options.
*/
_createClass(Record, [{
key: 'loadOptions',
value: function loadOptions() {
var recordOptions = _video2.default.mergeOptions(_defaults2.default, this.player.options_.plugins.record);
// record settings
this.recordImage = recordOptions.image;
this.recordAudio = recordOptions.audio;
this.recordVideo = recordOptions.video;
this.recordAnimation = recordOptions.animation;
this.maxLength = recordOptions.maxLength;
this.debug = recordOptions.debug;
this.recordTimeSlice = recordOptions.timeSlice;
// video/canvas settings
this.videoFrameWidth = recordOptions.frameWidth;
this.videoFrameHeight = recordOptions.frameHeight;
this.videoRecorderType = recordOptions.videoRecorderType;
this.videoMimeType = recordOptions.videoMimeType;
// audio settings
this.audioEngine = recordOptions.audioEngine;
this.audioRecorderType = recordOptions.audioRecorderType;
this.audioWorkerURL = recordOptions.audioWorkerURL;
this.audioBufferSize = recordOptions.audioBufferSize;
this.audioSampleRate = recordOptions.audioSampleRate;
this.audioBitRate = recordOptions.audioBitRate;
this.audioChannels = recordOptions.audioChannels;
this.audioMimeType = recordOptions.audioMimeType;
// animation settings
this.animationFrameRate = recordOptions.animationFrameRate;
this.animationQuality = recordOptions.animationQuality;
}
/**
* Player UI is ready.
* @private
*/
}, {
key: 'setupUI',
value: function setupUI() {
var _this2 = this;
// insert custom controls on left-side of controlbar
this.player.controlBar.addChild(this.player.cameraButton);
this.player.controlBar.el().insertBefore(this.player.cameraButton.el(), this.player.controlBar.el().firstChild);
this.player.controlBar.el().insertBefore(this.player.recordToggle.el(), this.player.controlBar.el().firstChild);
// get rid of unused controls
if (this.player.controlBar.remainingTimeDisplay !== undefined) {
this.player.controlBar.remainingTimeDisplay.el().style.display = 'none';
}
if (this.player.controlBar.liveDisplay !== undefined) {
this.player.controlBar.liveDisplay.el().style.display = 'none';
}
// loop feature is never used in this plugin
this.player.loop(false);
// tweak player UI based on type
switch (this.getRecordType()) {
case _recordMode.AUDIO_ONLY:
// reference to videojs-wavesurfer plugin
this.surfer = this.player.wavesurfer();
break;
case _recordMode.IMAGE_ONLY:
case _recordMode.VIDEO_ONLY:
case _recordMode.AUDIO_VIDEO:
case _recordMode.ANIMATION:
// customize controls
this.player.bigPlayButton.hide();
// loadedmetadata resets the durationDisplay for the
// first time
this.player.one('loadedmetadata', function () {
// display max record time
_this2.setDuration(_this2.maxLength);
});
// the native controls don't work for this UI so disable
// them no matter what
if (this.player.usingNativeControls_ === true) {
if (this.player.tech_.el_ !== undefined) {
this.player.tech_.el_.controls = false;
}
}
// clicking or tapping the player video element should not try
// to start playback
this.player.removeTechControlsListeners_();
if (this.player.options_.controls) {
// progress control isn't used by this plugin
this.player.controlBar.progressControl.hide();
// prevent controlbar fadeout
this.player.on('userinactive', function (event) {
_this2.player.userActive(true);
});
// videojs automatically hides the controls when no valid 'source'
// element is included in the video or audio tag. Don't. Ever again.
this.player.controlBar.show();
this.player.controlBar.el().style.display = 'flex';
}
break;
}
// disable time display events that constantly try to reset the current time
// and duration values
this.player.off('timeupdate');
this.player.off('durationchange');
this.player.off('loadedmetadata');
// display max record time
this.setDuration(this.maxLength);
// hide play control
this.player.controlBar.playToggle.hide();
}
/**
* Indicates whether the plugin is currently recording or not.
*
* @return {boolean} Plugin currently recording or not.
*/
}, {
key: 'isRecording',
value: function isRecording() {
return this._recording;
}
/**
* Indicates whether the plugin is currently processing recorded data
* or not.
*
* @return {boolean} Plugin processing or not.
*/
}, {
key: 'isProcessing',
value: function isProcessing() {
return this._processing;
}
/**
* Indicates whether the plugin is destroyed or not.
*
* @return {boolean} Plugin destroyed or not.
*/
}, {
key: 'isDestroyed',
value: function isDestroyed() {
return this.player && this.player.children() === null;
}
/**
* Open the browser's recording device selection dialog.
*/
}, {
key: 'getDevice',
value: function getDevice() {
// define device callbacks once
if (this.deviceReadyCallback === undefined) {
this.deviceReadyCallback = this.onDeviceReady.bind(this);
}
if (this.deviceErrorCallback === undefined) {
this.deviceErrorCallback = this.onDeviceError.bind(this);
}
if (this.engineStopCallback === undefined) {
this.engineStopCallback = this.onRecordComplete.bind(this);
}
// ask the browser to give the user access to the media device
// and get a stream reference in the callback function
switch (this.getRecordType()) {
case _recordMode.AUDIO_ONLY:
// setup microphone
this.mediaType = {
audio: this.audioRecorderType === AUTO ? true : this.audioRecorderType,
video: false
};
// remove existing microphone listeners
this.surfer.surfer.microphone.un('deviceReady', this.deviceReadyCallback);
this.surfer.surfer.microphone.un('deviceError', this.deviceErrorCallback);
// setup new microphone listeners
this.surfer.surfer.microphone.on('deviceReady', this.deviceReadyCallback);
this.surfer.surfer.microphone.on('deviceError', this.deviceErrorCallback);
// disable existing playback events
this.surfer.setupPlaybackEvents(false);
// (re)set surfer liveMode
this.surfer.liveMode = true;
this.surfer.surfer.microphone.paused = false;
// open browser device selection dialog
this.surfer.surfer.microphone.start();
break;
case _recordMode.IMAGE_ONLY:
case _recordMode.VIDEO_ONLY:
// setup camera
this.mediaType = {
audio: false,
video: this.videoRecorderType === AUTO ? true : this.videoRecorderType
};
navigator.mediaDevices.getUserMedia({
audio: false,
video: this.getRecordType() === _recordMode.IMAGE_ONLY ? this.recordImage : this.recordVideo
}).then(this.onDeviceReady.bind(this)).catch(this.onDeviceError.bind(this));
break;
case _recordMode.AUDIO_VIDEO:
// setup camera and microphone
this.mediaType = {
audio: this.audioRecorderType === AUTO ? true : this.audioRecorderType,
video: this.videoRecorderType === AUTO ? true : this.videoRecorderType
};
navigator.mediaDevices.getUserMedia({
audio: this.recordAudio,
video: this.recordVideo
}).then(this.onDeviceReady.bind(this)).catch(this.onDeviceError.bind(this));
break;
case _recordMode.ANIMATION:
// setup camera
this.mediaType = {
// animated GIF
audio: false,
video: false,
gif: true
};
navigator.mediaDevices.getUserMedia({
audio: false,
video: this.recordAnimation
}).then(this.onDeviceReady.bind(this)).catch(this.onDeviceError.bind(this));
break;
}
}
/**
* Invoked when the device is ready.
* @private
* @param stream: LocalMediaStream instance.
*/
}, {
key: 'onDeviceReady',
value: function onDeviceReady(stream) {
var _this3 = this;
this._deviceActive = true;
// store reference to stream for stopping etc.
this.stream = stream;
// hide device selection button
this.player.deviceButton.hide();
// reset time (e.g. when stopDevice was used)
this.setDuration(this.maxLength);
this.setCurrentTime(0);
// hide play/pause control (e.g. when stopDevice was used)
this.player.controlBar.playToggle.hide();
// reset playback listeners
this.off(this.player, 'timeupdate', this.playbackTimeUpdate);
this.off(this.player, 'ended', this.playbackTimeUpdate);
// setup recording engine
if (this.getRecordType() !== _recordMode.IMAGE_ONLY) {
// currently libvorbis.js, recorder.js, opus-recorder and lamejs
// are only supported in audio-only mode
if (this.getRecordType() !== _recordMode.AUDIO_ONLY && (this.audioEngine === _recordEngine.LIBVORBISJS || this.audioEngine === _recordEngine.RECORDERJS || this.audioEngine === _recordEngine.LAMEJS || this.audioEngine === _recordEngine.OPUSRECORDER)) {
throw new Error('Currently ' + this.audioEngine + ' is only supported in audio-only mode.');
}
// get recorder class
var EngineClass;
switch (this.audioEngine) {
case _recordEngine.RECORDRTC:
// RecordRTC.js (default)
EngineClass = _video2.default.RecordRTCEngine;
break;
case _recordEngine.LIBVORBISJS:
// libvorbis.js
EngineClass = _video2.default.LibVorbisEngine;
break;
case _recordEngine.RECORDERJS:
// recorder.js
EngineClass = _video2.default.RecorderjsEngine;
break;
case _recordEngine.LAMEJS:
// lamejs
EngineClass = _video2.default.LamejsEngine;
break;
case _recordEngine.OPUSRECORDER:
// opus-recorder
EngineClass = _video2.default.OpusRecorderEngine;
break;
default:
// unknown engine
throw new Error('Unknown audioEngine: ' + this.audioEngine);
}
try {
// connect stream to recording engine
this.engine = new EngineClass(this.player, this.player.options_);
} catch (err) {
console.error(err);
throw new Error('Could not load ' + this.audioEngine + ' plugin');
}
// listen for events
this.engine.on('recordComplete', this.engineStopCallback);
// audio settings
this.engine.bufferSize = this.audioBufferSize;
this.engine.sampleRate = this.audioSampleRate;
this.engine.bitRate = this.audioBitRate;
this.engine.audioChannels = this.audioChannels;
this.engine.audioWorkerURL = this.audioWorkerURL;
// mime type
this.engine.mimeType = {
video: this.videoMimeType,
gif: 'image/gif'
};
if (this.audioMimeType !== null && this.audioMimeType !== AUTO) {
this.engine.mimeType.audio = this.audioMimeType;
}
// video/canvas settings
this.engine.video = {
width: this.videoFrameWidth,
height: this.videoFrameHeight
};
this.engine.canvas = {
width: this.videoFrameWidth,
height: this.videoFrameHeight
};
// animated GIF settings
this.engine.quality = this.animationQuality;
this.engine.frameRate = this.animationFrameRate;
// timeSlice
if (this.recordTimeSlice && this.recordTimeSlice > 0) {
this.engine.timeSlice = this.recordTimeSlice;
this.engine.onTimeStamp = this.onTimeStamp.bind(this);
}
// initialize recorder
this.engine.setup(this.stream, this.mediaType, this.debug);
// show elements that should never be hidden in animation,
// audio and/or video modus
var uiElements = [this.player.controlBar.currentTimeDisplay, this.player.controlBar.timeDivider, this.player.controlBar.durationDisplay];
uiElements.forEach(function (element) {
if (element !== undefined) {
element.el().style.display = 'block';
element.show();
}
});
// show record button
this.player.recordToggle.show();
} else {
// disable record indicator
this.player.recordIndicator.disable();
// setup UI for retrying snapshot (e.g. when stopDevice was
// used)
this.retrySnapshot();
// reset and show camera button
this.player.cameraButton.onStop();
this.player.cameraButton.show();
}
// setup preview
if (this.getRecordType() !== _recordMode.AUDIO_ONLY) {
// show live preview
this.mediaElement = this.player.el().firstChild;
this.mediaElement.controls = false;
// mute incoming audio for feedback loops
this.mediaElement.muted = true;
// hide the volume bar while it's muted
this.displayVolumeControl(false);
// load stream
this.load(this.stream);
// stream loading is async, so we wait until it's ready to play
// the stream
this.player.one('loadedmetadata', function () {
// start stream
_this3.mediaElement.play();
// forward to listeners
_this3.player.trigger('deviceReady');
});
} else {
// forward to listeners
this.player.trigger('deviceReady');
}
}
/**
* Invoked when an device error occurred.
* @private
*/
}, {
key: 'onDeviceError',
value: function onDeviceError(code) {
this._deviceActive = false;
// store code
this.player.deviceErrorCode = code;
// forward error to player
this.player.trigger('deviceError');
}
/**
* Start recording.
*/
}, {
key: 'start',
value: function start() {
var _this4 = this;
if (!this.isProcessing()) {
this._recording = true;
// hide play/pause control
this.player.controlBar.playToggle.hide();
// reset playback listeners
this.off(this.player, 'timeupdate', this.playbackTimeUpdate);
this.off(this.player, 'ended', this.playbackTimeUpdate);
// start preview
switch (this.getRecordType()) {
case _recordMode.AUDIO_ONLY:
// disable playback events
this.surfer.setupPlaybackEvents(false);
// start/resume live audio visualization
this.surfer.surfer.microphone.paused = false;
this.surfer.liveMode = true;
this.surfer.surfer.microphone.play();
break;
case _recordMode.VIDEO_ONLY:
case _recordMode.AUDIO_VIDEO:
// preview video stream in video element
this.startVideoPreview();
break;
case _recordMode.ANIMATION:
// hide the first frame
this.player.recordCanvas.hide();
// hide the animation
this.player.animationDisplay.hide();
// show preview video
this.mediaElement.style.display = 'block';
// for animations, capture the first frame
// that can be displayed as soon as recording
// is complete
this.captureFrame().then(function (result) {
// start video preview **after** capturing first frame
_this4.startVideoPreview();
});
break;
}
// start recording
switch (this.getRecordType()) {
case _recordMode.IMAGE_ONLY:
// create snapshot
this.createSnapshot();
// notify UI
this.player.trigger('startRecord');
break;
case _recordMode.VIDEO_ONLY:
case _recordMode.AUDIO_VIDEO:
case _recordMode.ANIMATION:
// wait for media stream on video element to actually load
this.player.one('loadedmetadata', function () {
// start actually recording process
_this4.startRecording();
});
break;
default:
// all resources have already loaded, so we can start
// recording right away
this.startRecording();
}
}
}
/**
* Start recording.
* @private
*/
}, {
key: 'startRecording',
value: function startRecording() {
// register starting point
this.paused = false;
this.pauseTime = this.pausedTime = 0;
this.startTime = new Date().getTime();
// start countdown
this.countDown = this.player.setInterval(this.onCountDown.bind(this), 100);
// cleanup previous recording
if (this.engine !== undefined) {
this.engine.dispose();
}
// start recording stream
this.engine.start();
// notify UI
this.player.trigger('startRecord');
}
/**
* Stop recording.
*/
}, {
key: 'stop',
value: function stop() {
if (!this.isProcessing()) {
this._recording = false;
this._processing = true;
if (this.getRecordType() !== _recordMode.IMAGE_ONLY) {
// notify UI
this.player.trigger('stopRecord');
// stop countdown
this.player.clearInterval(this.countDown);
// stop recording stream (result will be available async)
if (this.engine) {
this.engine.stop();
}
} else {
if (this.player.recordedData) {
// notify listeners that image data is (already) available
this.player.trigger('finishRecord');
}
}
}
}
/**
* Stop device(s) and recording if active.
*/
}, {
key: 'stopDevice',
value: function stopDevice() {
if (this.isRecording()) {
// stop stream once recorded data is available,
// otherwise it'll break recording
this.player.one('finishRecord', this.stopStream.bind(this));
// stop recording
this.stop();
} else {
// stop stream now, since there's no recorded data available
this.stopStream();
}
}
/**
* Stop stream and device.
*/
}, {
key: 'stopStream',
value: function stopStream() {
// stop stream and device
if (this.stream) {
this._deviceActive = false;
if (this.getRecordType() === _recordMode.AUDIO_ONLY) {
// make the microphone plugin stop it's device
this.surfer.surfer.microphone.stopDevice();
return;
}
this.stream.getTracks().forEach(function (stream) {
stream.stop();
});
}
}
/**
* Pause recording.
*/
}, {
key: 'pause',
value: function pause() {
if (!this.paused) {
this.pauseTime = new Date().getTime();
this.paused = true;
this.engine.pause();
}
}
/**
* Resume recording.
*/
}, {
key: 'resume',
value: function resume() {
if (this.paused) {
this.pausedTime += new Date().getTime() - this.pauseTime;
this.engine.resume();
this.paused = false;
}
}
/**
* Invoked when recording completed and the resulting stream is
* available.
* @private
*/
}, {
key: 'onRecordComplete',
value: function onRecordComplete() {
var _this5 = this;
// store reference to recorded stream data
this.player.recordedData = this.engine.recordedData;
// change the replay button back to a play button
this.player.controlBar.playToggle.removeClass('vjs-ended');
this.player.controlBar.playToggle.show();
// notify listeners that data is available
this.player.trigger('finishRecord');
switch (this.getRecordType()) {
case _recordMode.AUDIO_ONLY:
// pause player so user can start playback
this.surfer.pause();
// setup events for playback
this.surfer.setupPlaybackEvents(true);
// display loader
this.player.loadingSpinner.show();
// restore interaction with controls after waveform
// rendering is complete
this.surfer.surfer.once('ready', function () {
_this5._processing = false;
});
// visualize recorded stream
this.load(this.player.recordedData);
break;
case _recordMode.VIDEO_ONLY:
case _recordMode.AUDIO_VIDEO:
// pausing the player so we can visualize the recorded data
// will trigger an async video.js 'pause' event that we
// have to wait for.
this.player.one('pause', function () {
// video data is ready
_this5._processing = false;
// hide loader
_this5.player.loadingSpinner.hide();
// show stream total duration
_this5.setDuration(_this5.streamDuration);
// update time during playback and at end
_this5.on(_this5.player, 'timeupdate', _this5.playbackTimeUpdate);
_this5.on(_this5.player, 'ended', _this5.playbackTimeUpdate);
// unmute local audio during playback
if (_this5.getRecordType() === _recordMode.AUDIO_VIDEO) {
_this5.mediaElement.muted = false;
// show the volume bar when it's unmuted
_this5.displayVolumeControl(true);
}
// load recorded media
if ((0, _detectBrowser.isChrome)() && _this5.getRecordType() === _recordMode.AUDIO_VIDEO) {
// use video property on Chrome
_this5.load(_this5.player.recordedData.video);
} else {
_this5.load(_this5.player.recordedData);
}
});
// pause player so user can start playback
this.player.pause();
break;
case _recordMode.ANIMATION:
// animation data is ready
this._processing = false;
// hide loader
this.player.loadingSpinner.hide();
// show animation total duration
this.setDuration(this.streamDuration);
// hide preview video
this.mediaElement.style.display = 'none';
// show the first frame
this.player.recordCanvas.show();
// pause player so user can start playback
this.player.pause();
// show animation on play
this.on(this.player, 'play', this.showAnimation);
// hide animation on pause
this.on(this.player, 'pause', this.hideAnimation);
break;
}
}
/**
* Invoked during recording and displays the remaining time.
* @private
*/
}, {
key: 'onCountDown',
value: function onCountDown() {
if (!this.paused) {
var now = new Date().getTime();
var duration = this.maxLength;
var currentTime = (now - (this.startTime + this.pausedTime)) / 1000;
this.streamDuration = currentTime;
if (currentTime >= duration) {
// at the end
currentTime = duration;
// stop recording
this.stop();
}
// update duration
this.setDuration(duration);
// update current time
this.setCurrentTime(currentTime, duration);
// notify listeners
this.player.trigger('progressRecord');
}
}
/**
* Get the current time of the recorded stream during playback.
*
* Returns 0 if no recording is available (yet).
*/
}, {
key: 'getCurrentTime',
value: function getCurrentTime() {
var currentTime = isNaN(this.streamCurrentTime) ? 0 : this.streamCurrentTime;
if (this.getRecordType() === _recordMode.AUDIO_ONLY) {
currentTime = this.surfer.getCurrentTime();
}
return currentTime;
}
/**
* Updates the player's element displaying the current time.
*
* @private
* @param {number} [currentTime=0] - Current position of the
* playhead (in seconds).
* @param {number} [duration=0] - Duration in seconds.
*/
}, {
key: 'setCurrentTime',
value: function setCurrentTime(currentTime, duration) {
currentTime = isNaN(currentTime) ? 0 : currentTime;
duration = isNaN(duration) ? 0 : duration;
switch (this.getRecordType()) {
case _recordMode.AUDIO_ONLY:
this.surfer.setCurrentTime(currentTime, duration);
break;
case _recordMode.VIDEO_ONLY:
case _recordMode.AUDIO_VIDEO:
case _recordMode.ANIMATION:
this.streamCurrentTime = Math.min(currentTime, duration);
// update current time display component
this.player.controlBar.currentTimeDisplay.formattedTime_ = this.player.controlBar.currentTimeDisplay.contentEl().lastChild.textContent = (0, _formatTime2.default)(this.streamCurrentTime, duration, this.msDisplayMax);
break;
}
}
/**
* Get the length of the recorded stream in seconds.
*
* Returns 0 if no recording is available (yet).
*/
}, {
key: 'getDuration',
value: function getDuration() {
var duration = isNaN(this.streamDuration) ? 0 : this.streamDuration;
return duration;
}
/**
* Updates the player's element displaying the duration time.
*
* @param {number} [duration=0] - Duration in seconds.
* @private
*/
}, {
key: 'setDuration',
value: function setDuration(duration) {
duration = isNaN(duration) ? 0 : duration;
switch (this.getRecordType()) {
case _recordMode.AUDIO_ONLY:
this.surfer.setDuration(duration);
break;
case _recordMode.VIDEO_ONLY:
case _recordMode.AUDIO_VIDEO:
case _recordMode.ANIMATION:
// update duration display component
this.player.controlBar.durationDisplay.formattedTime_ = this.player.controlBar.durationDisplay.contentEl().lastChild.textContent = (0, _formatTime2.default)(duration, duration, this.msDisplayMax);
break;
}
}
/**
* Start loading data.
*
* @param {(string|blob|file)} url - Either the URL of the media file,
* a Blob, a File object or MediaStream.
*/
}, {
key: 'load',
value: function load(url) {
switch (this.getRecordType()) {
case _recordMode.AUDIO_ONLY:
// visualize recorded Blob stream
this.surfer.load(url);
break;
case _recordMode.IMAGE_ONLY:
case _recordMode.VIDEO_ONLY:
case _recordMode.AUDIO_VIDEO:
case _recordMode.ANIMATION:
if (url instanceof Blob || url instanceof File) {
// assign blob using createObjectURL
(0, _browserShim2.default)(url, this.mediaElement, false);
} else {
// assign stream without createObjectURL
(0, _browserShim2.default)(url, this.mediaElement, true);
}
break;
}
}
/**
* Show save as dialog in browser so the user can store the recorded media
* locally.
*
* @param {object} name - Object with one or more names for the particular
* blob(s) you want to save. File extensions are added automatically.
* For example: {'video': 'name-of-video-file'}. Supported keys are
* 'audio', 'video' and 'gif'.
*/
}, {
key: 'saveAs',
value: function saveAs(name) {
if (this.engine && name !== undefined) {
this.engine.saveAs(name);
}
}
/**
* Destroy plugin only.
*
* Use `destroy` to remove the plugin and the player.
*/
}, {
key: 'dispose',
value: function dispose() {
// disable common event listeners
this.player.off('ready');
this.player.off('userinactive');
this.player.off('loadedmetadata');
// prevent callbacks if recording is in progress
if (this.engine) {
this.engine.dispose();
this.engine.off('recordComplete', this.engineStopCallback);
}
// stop recording and device
this.stop();
this.stopDevice();
// stop countdown
this.player.clearInterval(this.countDown);
// dispose wavesurfer.js
if (this.getRecordType() == _recordMode.AUDIO_ONLY) {
if (this.surfer) {
// also disposes player
this.surfer.destroy();
}
}
this.resetState();
_get(Record.prototype.__proto__ || Object.getPrototypeOf(Record.prototype), 'dispose', this).call(this);
}
/**
* Destroy plugin and players and cleanup resources.
*/
}, {
key: 'destroy',
value: function destroy() {
this.player.dispose();
}
/**
* Reset the plugin.
*/
}, {
key: 'reset',
value: function reset() {
var _this6 = this;
// prevent callbacks if recording is in progress
if (this.engine) {
this.engine.dispose();
this.engine.off('recordComplete', this.engineStopCallback);
}
// stop recording and device
this.stop();
this.stopDevice();
// stop countdown
this.player.clearInterval(this.countDown);
// reset options
this.loadOptions();
// reset recorder state
this.resetState();
// reset record time
this.setDuration(this.maxLength);
this.setCurrentTime(0);
// reset player
this.player.reset();
switch (this.getRecordType()) {
case _recordMode.AUDIO_ONLY:
if (this.surfer && this.surfer.surfer) {
// empty last frame
this.surfer.surfer.empty();
}
break;
case _recordMode.IMAGE_ONLY:
case _recordMode.ANIMATION:
// reset UI
this.player.recordCanvas.hide();
this.player.cameraButton.hide();
break;
}
// hide play control
this.player.controlBar.playToggle.hide();
// show device selection button
this.player.deviceButton.show();
// hide record button
this.player.recordToggle.hide();
// loadedmetadata resets the durationDisplay for the
// first time
this.player.one('loadedmetadata', function () {
// display max record time
_this6.setDuration(_this6.maxLength);
});
}
/**
* Reset the plugin recorder state.
* @private
*/
}, {
key: 'resetState',
value: function resetState() {
this._recording = false;
this._processing = false;
this._deviceActive = false;
this.devices = [];
}
/**
* Get recorder type.
*/
}, {
key: 'getRecordType',
value: function getRecordType() {
return (0, _recordMode.getRecorderMode)(this.recordImage, this.recordAudio, this.recordVideo, this.recordAnimation);
}
/**
* Create and display snapshot image.
* @private
*/
}, {
key: 'createSnapshot',
value: function createSnapshot() {
var _this7 = this;
this.captureFrame().then(function (result) {
// turn the canvas data into base64 data with a PNG header
_this7.player.recordedData = result.toDataURL('image/png');
// hide preview video
_this7.mediaElement.style.display = 'none';
// show the snapshot
_this7.player.recordCanvas.show();
// stop recording
_this7.stop();
});
}
/**
* Reset UI for retrying a snapshot image.
* @private
*/
}, {
key: 'retrySnapshot',
value: function retrySnapshot() {
this._processing = false;
// retry: hide the snapshot
this.player.recordCanvas.hide();
// show preview video
this.player.el().firstChild.style.display = 'block';
}
/**
* Capture frame from camera and copy data to canvas.
* @private
*/
}, {
key: 'captureFrame',
value: function captureFrame() {
var _this8 = this;
var detected = (0, _detectBrowser.detectBrowser)();
var recordCanvas = this.player.recordCanvas.el().firstChild;
// set the canvas size to the dimensions of the camera,
// which also wipes the content of the canvas
recordCanvas.width = this.player.width();
recordCanvas.height = this.player.height();
return new Promise(function (resolve, reject) {
// MediaCapture is only supported on:
// - Chrome 60 and newer (see
// https://github.com/w3c/mediacapture-image/blob/gh-pages/implementation-status.md)
// - Firefox behind flag (https://bugzilla.mozilla.org/show_bug.cgi?id=888177)
//
// importing ImageCapture can fail when enabling chrome flag is still required.
// if so; ignore and continue
if (detected.browser === 'chrome' && detected.version >= 60 && (typeof ImageCapture === 'undefined' ? 'undefined' : _typeof(ImageCapture)) === (typeof Function === 'undefined' ? 'undefined' : _typeof(Function))) {
try {
var track = _this8.stream.getVideoTracks()[0];
var imageCapture = new ImageCapture(track);
var photoSettings = {
imageWidth: recordCanvas.width,
imageHeight: recordCanvas.height
};
// take picture
imageCapture.takePhoto(photoSettings).then(function (blob) {
return createImageBitmap(blob);
}).then(function (imageBitmap) {
// get a frame and copy it onto the canvas
_this8.drawCanvas(recordCanvas, imageBitmap);
// notify others
resolve(recordCanvas);
});
return;
} catch (err) {}
}
// no ImageCapture available: do it the oldskool way
// get a frame and copy it onto the canvas
_this8.drawCanvas(recordCanvas, _this8.mediaElement);
// notify others
resolve(recordCanvas);
});
}
/**
* Draw image frame on canvas element.
* @private
*/
}, {
key: 'drawCanvas',
value: function drawCanvas(canvas, element) {
canvas.getContext('2d').drawImage(element, 0, 0, canvas.width, canvas.height);
}
/**
* Start preview of video stream.
* @private
*/
}, {
key: 'startVideoPreview',
value: function startVideoPreview() {
// disable playback events
this.off('timeupdate');
this.off('durationchange');
this.off('loadedmetadata');
this.off('play');
// mute local audio
this.mediaElement.muted = true;
// hide volume control to prevent feedback
this.displayVolumeControl(false);
// start or resume live preview
this.load(this.stream);
this.mediaElement.play();
}
/**
* Show animated GIF.
* @private
*/
}, {
key: 'showAnimation',
value: function showAnimation() {
var animationDisplay = this.player.animationDisplay.el().firstChild;
// set the image size to the dimensions of the recorded animation
animationDisplay.width = this.player.width();
animationDisplay.height = this.player.height();
// hide the first frame
this.player.recordCanvas.hide();
// show the animation
(0, _browserShim2.default)(this.player.recordedData, animationDisplay, false);
this.player.animationDisplay.show();
}
/**
* Hide animated GIF.
* @private
*/
}, {
key: 'hideAnimation',
value: function hideAnimation() {
// show the first frame
this.player.recordCanvas.show();
// hide the animation
this.player.animationDisplay.hide();
}
/**
* Update time during playback.
* @private
*/
}, {
key: 'playbackTimeUpdate',
value: function playbackTimeUpdate() {
this.setCurrentTime(this.player.currentTime(), this.streamDuration);
}
/**
* Received new timestamp (when timeSlice option is enabled).
* @private
*/
}, {
key: 'onTimeStamp',
value: function onTimeStamp(current, all) {
this.player.currentTimestamp = current;
this.player.allTimestamps = all;
// get blob (only for MediaStreamRecorder)
var internal;
switch (this.getRecordType()) {
case _recordMode.AUDIO_ONLY:
internal = this.engine.engine.audioRecorder;
break;
case _recordMode.ANIMATION:
internal = this.engine.engine.gifRecorder;
break;
default:
internal = this.engine.engine.videoRecorder;
}
internal = internal.getInternalRecorder();
if (internal instanceof MediaStreamRecorder === true) {
this.player.recordedData = internal.getArrayOfBlobs();
// inject file info for newest blob
this.engine.addFileInfo(this.player.recordedData[this.player.recordedData.length - 1]);
}
// notify others
this.player.trigger('timestamp');
}
/**
* Collects information about the media input and output devices
* available on the system.
*
* Returns an array.
*/
}, {
key: 'enumerateDevices',
value: function enumerateDevices() {
var _this9 = this;
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
this.player.enumerateErrorCode = 'enumerateDevices() not supported.';
this.player.trigger('enumerateError');
return;
}
// List cameras and microphones.
navigator.mediaDevices.enumerateDevices(this).then(function (devices) {
_this9.devices = [];
devices.forEach(function (device) {
_this9.devices.push(device);
});
// notify listeners
_this9.player.trigger('enumerateReady');
}).catch(function (err) {
_this9.player.enumerateErrorCode = err;
_this9.player.trigger('enumerateError');
});
}
/**
* Change the audio output device.
*
* @param {string} deviceId - Id of audio output device.
*/
}, {
key: 'setAudioOutput',
value: function setAudioOutput(deviceId) {
var _this10 = this;
var errorMessage = void 0;
switch (this.getRecordType()) {
case _recordMode.AUDIO_ONLY:
// use wavesurfer
this.surfer.surfer.setSinkId(deviceId).then(function (result) {
// notify listeners
_this10.player.trigger('audioOutputReady');
}).catch(function (err) {
errorMessage = err;
});
break;
default:
var element = player.tech_.el_;
if (deviceId) {
if (typeof element.sinkId !== 'undefined') {
element.setSinkId(deviceId).then(function (result) {
// notify listeners
_this10.player.trigger('audioOutputReady');
}).catch(function (err) {
errorMessage = err;
});
} else {
errorMessage = 'Browser does not support audio output device selection.';
}
} else {
errorMessage = 'Invalid deviceId: ' + deviceId;
}
break;
}
// error if we get here: notify listeners
if (errorMessage) {
this.player.trigger('error', errorMessage);
}
}
/**
* Show or hide the volume menu.
*
* @private
* @param {boolean} display - Hide/show volume control.
*/
}, {
key: 'displayVolumeControl',
value: function displayVolumeControl(display) {
if (this.player.controlBar.volumePanel !== undefined) {
if (display === true) {
display = 'flex';
} else {
display = 'none';
}
this.player.controlBar.volumePanel.el().style.display = display;
}
}
}]);
return Record;
}(Plugin);
// version nr gets replaced during build
Record.VERSION = '2.1.3';
// register plugin
_video2.default.Record = Record;
_video2.default.registerPlugin('record', Record);
// export plugin
module.exports = {
Record: Record
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./controls/animation-display":1,"./controls/camera-button":2,"./controls/device-button":3,"./controls/record-canvas":4,"./controls/record-indicator":5,"./controls/record-toggle":6,"./defaults":7,"./engine/record-engine":8,"./engine/record-mode":9,"./engine/record-rtc":10,"./utils/browser-shim":11,"./utils/detect-browser":12,"./utils/format-time":13}]},{},[15])(15)
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJlczUvY29udHJvbHMvYW5pbWF0aW9uLWRpc3BsYXkuanMiLCJlczUvY29udHJvbHMvY2FtZXJhLWJ1dHRvbi5qcyIsImVzNS9jb250cm9scy9kZXZpY2UtYnV0dG9uLmpzIiwiZXM1L2NvbnRyb2xzL3JlY29yZC1jYW52YXMuanMiLCJlczUvY29udHJvbHMvcmVjb3JkLWluZGljYXRvci5qcyIsImVzNS9jb250cm9scy9yZWNvcmQtdG9nZ2xlLmpzIiwiZXM1L2RlZmF1bHRzLmpzIiwiZXM1L2VuZ2luZS9yZWNvcmQtZW5naW5lLmpzIiwiZXM1L2VuZ2luZS9yZWNvcmQtbW9kZS5qcyIsImVzNS9lbmdpbmUvcmVjb3JkLXJ0Yy5qcyIsImVzNS91dGlscy9icm93c2VyLXNoaW0uanMiLCJlczUvdXRpbHMvZGV0ZWN0LWJyb3dzZXIuanMiLCJlczUvdXRpbHMvZm9ybWF0LXRpbWUuanMiLCJub2RlX21vZHVsZXMvZ2xvYmFsL3dpbmRvdy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7O0FBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOURBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDbktBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOURBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDOUZBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDN0pBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ3ZGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pNQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQzNDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUM3T0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDM0JBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2xIQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7O0FDeEVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbigpe2Z1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfXJldHVybiBlfSkoKSIsIid1c2Ugc3RyaWN0JztcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcblxudmFyIF9jcmVhdGVDbGFzcyA9IGZ1bmN0aW9uICgpIHsgZnVuY3Rpb24gZGVmaW5lUHJvcGVydGllcyh0YXJnZXQsIHByb3BzKSB7IGZvciAodmFyIGkgPSAwOyBpIDwgcHJvcHMubGVuZ3RoOyBpKyspIHsgdmFyIGRlc2NyaXB0b3IgPSBwcm9wc1tpXTsgZGVzY3JpcHRvci5lbnVtZXJhYmxlID0gZGVzY3JpcHRvci5lbnVtZXJhYmxlIHx8IGZhbHNlOyBkZXNjcmlwdG9yLmNvbmZpZ3VyYWJsZSA9IHRydWU7IGlmIChcInZhbHVlXCIgaW4gZGVzY3JpcHRvcikgZGVzY3JpcHRvci53cml0YWJsZSA9IHRydWU7IE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGRlc2NyaXB0b3Iua2V5LCBkZXNjcmlwdG9yKTsgfSB9IHJldHVybiBmdW5jdGlvbiAoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7IGlmIChwcm90b1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7IGlmIChzdGF0aWNQcm9wcykgZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH07IH0oKTtcblxudmFyIF9nZXQgPSBmdW5jdGlvbiBnZXQob2JqZWN0LCBwcm9wZXJ0eSwgcmVjZWl2ZXIpIHsgaWYgKG9iamVjdCA9PT0gbnVsbCkgb2JqZWN0ID0gRnVuY3Rpb24ucHJvdG90eXBlOyB2YXIgZGVzYyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iob2JqZWN0LCBwcm9wZXJ0eSk7IGlmIChkZXNjID09PSB1bmRlZmluZWQpIHsgdmFyIHBhcmVudCA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihvYmplY3QpOyBpZiAocGFyZW50ID09PSBudWxsKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gZWxzZSB7IHJldHVybiBnZXQocGFyZW50LCBwcm9wZXJ0eSwgcmVjZWl2ZXIpOyB9IH0gZWxzZSBpZiAoXCJ2YWx1ZVwiIGluIGRlc2MpIHsgcmV0dXJuIGRlc2MudmFsdWU7IH0gZWxzZSB7IHZhciBnZXR0ZXIgPSBkZXNjLmdldDsgaWYgKGdldHRlciA9PT0gdW5kZWZpbmVkKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gcmV0dXJuIGdldHRlci5jYWxsKHJlY2VpdmVyKTsgfSB9O1xuXG5mdW5jdGlvbiBfY2xhc3NDYWxsQ2hlY2soaW5zdGFuY2UsIENvbnN0cnVjdG9yKSB7IGlmICghKGluc3RhbmNlIGluc3RhbmNlb2YgQ29uc3RydWN0b3IpKSB7IHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb25cIik7IH0gfVxuXG5mdW5jdGlvbiBfcG9zc2libGVDb25zdHJ1Y3RvclJldHVybihzZWxmLCBjYWxsKSB7IGlmICghc2VsZikgeyB0aHJvdyBuZXcgUmVmZXJlbmNlRXJyb3IoXCJ0aGlzIGhhc24ndCBiZWVuIGluaXRpYWxpc2VkIC0gc3VwZXIoKSBoYXNuJ3QgYmVlbiBjYWxsZWRcIik7IH0gcmV0dXJuIGNhbGwgJiYgKHR5cGVvZiBjYWxsID09PSBcIm9iamVjdFwiIHx8IHR5cGVvZiBjYWxsID09PSBcImZ1bmN0aW9uXCIpID8gY2FsbCA6IHNlbGY7IH1cblxuZnVuY3Rpb24gX2luaGVyaXRzKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IGlmICh0eXBlb2Ygc3VwZXJDbGFzcyAhPT0gXCJmdW5jdGlvblwiICYmIHN1cGVyQ2xhc3MgIT09IG51bGwpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlN1cGVyIGV4cHJlc3Npb24gbXVzdCBlaXRoZXIgYmUgbnVsbCBvciBhIGZ1bmN0aW9uLCBub3QgXCIgKyB0eXBlb2Ygc3VwZXJDbGFzcyk7IH0gc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzICYmIHN1cGVyQ2xhc3MucHJvdG90eXBlLCB7IGNvbnN0cnVjdG9yOiB7IHZhbHVlOiBzdWJDbGFzcywgZW51bWVyYWJsZTogZmFsc2UsIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSB9KTsgaWYgKHN1cGVyQ2xhc3MpIE9iamVjdC5zZXRQcm90b3R5cGVPZiA/IE9iamVjdC5zZXRQcm90b3R5cGVPZihzdWJDbGFzcywgc3VwZXJDbGFzcykgOiBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cbi8qKlxuICogQGZpbGUgYW5pbWF0aW9uLWRpc3BsYXkuanNcbiAqIEBzaW5jZSAyLjAuMFxuICovXG5cbnZhciBDb21wb25lbnQgPSB2aWRlb2pzLmdldENvbXBvbmVudCgnQ29tcG9uZW50Jyk7XG5cbi8qKlxuICogSW1hZ2UgZm9yIGRpc3BsYXlpbmcgYW5pbWF0ZWQgR0lGIGltYWdlLlxuICpcbiAqIEBjbGFzc1xuICogQGF1Z21lbnRzIHZpZGVvanMuQ29tcG9uZW50XG4qL1xuXG52YXIgQW5pbWF0aW9uRGlzcGxheSA9IGZ1bmN0aW9uIChfQ29tcG9uZW50KSB7XG4gIF9pbmhlcml0cyhBbmltYXRpb25EaXNwbGF5LCBfQ29tcG9uZW50KTtcblxuICBmdW5jdGlvbiBBbmltYXRpb25EaXNwbGF5KCkge1xuICAgIF9jbGFzc0NhbGxDaGVjayh0aGlzLCBBbmltYXRpb25EaXNwbGF5KTtcblxuICAgIHJldHVybiBfcG9zc2libGVDb25zdHJ1Y3RvclJldHVybih0aGlzLCAoQW5pbWF0aW9uRGlzcGxheS5fX3Byb3RvX18gfHwgT2JqZWN0LmdldFByb3RvdHlwZU9mKEFuaW1hdGlvbkRpc3BsYXkpKS5hcHBseSh0aGlzLCBhcmd1bWVudHMpKTtcbiAgfVxuXG4gIF9jcmVhdGVDbGFzcyhBbmltYXRpb25EaXNwbGF5LCBbe1xuICAgIGtleTogJ2NyZWF0ZUVsJyxcblxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlIHRoZSBgQW5pbWF0aW9uRGlzcGxheWBzIERPTSBlbGVtZW50LlxuICAgICAqXG4gICAgICogQHJldHVybiB7RWxlbWVudH1cbiAgICAgKiAgICAgICAgIFRoZSBkb20gZWxlbWVudCB0aGF0IGdldHMgY3JlYXRlZC5cbiAgICAgKi9cbiAgICB2YWx1ZTogZnVuY3Rpb24gY3JlYXRlRWwoKSB7XG4gICAgICByZXR1cm4gX2dldChBbmltYXRpb25EaXNwbGF5LnByb3RvdHlwZS5fX3Byb3RvX18gfHwgT2JqZWN0LmdldFByb3RvdHlwZU9mKEFuaW1hdGlvbkRpc3BsYXkucHJvdG90eXBlKSwgJ2NyZWF0ZUVsJywgdGhpcykuY2FsbCh0aGlzLCAnZGl2Jywge1xuICAgICAgICBjbGFzc05hbWU6ICd2anMtYW5pbWF0aW9uLWRpc3BsYXknLFxuICAgICAgICBpbm5lckhUTUw6ICc8aW1nIC8+J1xuICAgICAgfSk7XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIEFuaW1hdGlvbkRpc3BsYXk7XG59KENvbXBvbmVudCk7XG5cbkNvbXBvbmVudC5yZWdpc3RlckNvbXBvbmVudCgnQW5pbWF0aW9uRGlzcGxheScsIEFuaW1hdGlvbkRpc3BsYXkpO1xuXG5leHBvcnRzLmRlZmF1bHQgPSBBbmltYXRpb25EaXNwbGF5OyIsIid1c2Ugc3RyaWN0JztcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7XG4gIHZhbHVlOiB0cnVlXG59KTtcblxudmFyIF9jcmVhdGVDbGFzcyA9IGZ1bmN0aW9uICgpIHsgZnVuY3Rpb24gZGVmaW5lUHJvcGVydGllcyh0YXJnZXQsIHByb3BzKSB7IGZvciAodmFyIGkgPSAwOyBpIDwgcHJvcHMubGVuZ3RoOyBpKyspIHsgdmFyIGRlc2NyaXB0b3IgPSBwcm9wc1tpXTsgZGVzY3JpcHRvci5lbnVtZXJhYmxlID0gZGVzY3JpcHRvci5lbnVtZXJhYmxlIHx8IGZhbHNlOyBkZXNjcmlwdG9yLmNvbmZpZ3VyYWJsZSA9IHRydWU7IGlmIChcInZhbHVlXCIgaW4gZGVzY3JpcHRvcikgZGVzY3JpcHRvci53cml0YWJsZSA9IHRydWU7IE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGRlc2NyaXB0b3Iua2V5LCBkZXNjcmlwdG9yKTsgfSB9IHJldHVybiBmdW5jdGlvbiAoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7IGlmIChwcm90b1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7IGlmIChzdGF0aWNQcm9wcykgZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH07IH0oKTtcblxudmFyIF9nZXQgPSBmdW5jdGlvbiBnZXQob2JqZWN0LCBwcm9wZXJ0eSwgcmVjZWl2ZXIpIHsgaWYgKG9iamVjdCA9PT0gbnVsbCkgb2JqZWN0ID0gRnVuY3Rpb24ucHJvdG90eXBlOyB2YXIgZGVzYyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iob2JqZWN0LCBwcm9wZXJ0eSk7IGlmIChkZXNjID09PSB1bmRlZmluZWQpIHsgdmFyIHBhcmVudCA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihvYmplY3QpOyBpZiAocGFyZW50ID09PSBudWxsKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gZWxzZSB7IHJldHVybiBnZXQocGFyZW50LCBwcm9wZXJ0eSwgcmVjZWl2ZXIpOyB9IH0gZWxzZSBpZiAoXCJ2YWx1ZVwiIGluIGRlc2MpIHsgcmV0dXJuIGRlc2MudmFsdWU7IH0gZWxzZSB7IHZhciBnZXR0ZXIgPSBkZXNjLmdldDsgaWYgKGdldHRlciA9PT0gdW5kZWZpbmVkKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gcmV0dXJuIGdldHRlci5jYWxsKHJlY2VpdmVyKTsgfSB9O1xuXG5mdW5jdGlvbiBfY2xhc3NDYWxsQ2hlY2soaW5zdGFuY2UsIENvbnN0cnVjdG9yKSB7IGlmICghKGluc3RhbmNlIGluc3RhbmNlb2YgQ29uc3RydWN0b3IpKSB7IHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb25cIik7IH0gfVxuXG5mdW5jdGlvbiBfcG9zc2libGVDb25zdHJ1Y3RvclJldHVybihzZWxmLCBjYWxsKSB7IGlmICghc2VsZikgeyB0aHJvdyBuZXcgUmVmZXJlbmNlRXJyb3IoXCJ0aGlzIGhhc24ndCBiZWVuIGluaXRpYWxpc2VkIC0gc3VwZXIoKSBoYXNuJ3QgYmVlbiBjYWxsZWRcIik7IH0gcmV0dXJuIGNhbGwgJiYgKHR5cGVvZiBjYWxsID09PSBcIm9iamVjdFwiIHx8IHR5cGVvZiBjYWxsID09PSBcImZ1bmN0aW9uXCIpID8gY2FsbCA6IHNlbGY7IH1cblxuZnVuY3Rpb24gX2luaGVyaXRzKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IGlmICh0eXBlb2Ygc3VwZXJDbGFzcyAhPT0gXCJmdW5jdGlvblwiICYmIHN1cGVyQ2xhc3MgIT09IG51bGwpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlN1cGVyIGV4cHJlc3Npb24gbXVzdCBlaXRoZXIgYmUgbnVsbCBvciBhIGZ1bmN0aW9uLCBub3QgXCIgKyB0eXBlb2Ygc3VwZXJDbGFzcyk7IH0gc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzICYmIHN1cGVyQ2xhc3MucHJvdG90eXBlLCB7IGNvbnN0cnVjdG9yOiB7IHZhbHVlOiBzdWJDbGFzcywgZW51bWVyYWJsZTogZmFsc2UsIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSB9KTsgaWYgKHN1cGVyQ2xhc3MpIE9iamVjdC5zZXRQcm90b3R5cGVPZiA/IE9iamVjdC5zZXRQcm90b3R5cGVPZihzdWJDbGFzcywgc3VwZXJDbGFzcykgOiBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cbi8qKlxuICogQGZpbGUgY2FtZXJhLWJ1dHRvbi5qc1xuICogQHNpbmNlIDIuMC4wXG4gKi9cblxudmFyIEJ1dHRvbiA9IHZpZGVvanMuZ2V0Q29tcG9uZW50KCdCdXR0b24nKTtcbnZhciBDb21wb25lbnQgPSB2aWRlb2pzLmdldENvbXBvbmVudCgnQ29tcG9uZW50Jyk7XG5cbi8qKlxuICogQnV0dG9uIHRvIHRvZ2dsZSBiZXR3ZWVuIGNyZWF0ZSBhbmQgcmV0cnkgc25hcHNob3QgaW1hZ2UuXG4gKlxuICogQGNsYXNzXG4gKiBAYXVnbWVudHMgdmlkZW9qcy5CdXR0b25cbiovXG5cbnZhciBDYW1lcmFCdXR0b24gPSBmdW5jdGlvbiAoX0J1dHRvbikge1xuICBfaW5oZXJpdHMoQ2FtZXJhQnV0dG9uLCBfQnV0dG9uKTtcblxuICBmdW5jdGlvbiBDYW1lcmFCdXR0b24oKSB7XG4gICAgX2NsYXNzQ2FsbENoZWNrKHRoaXMsIENhbWVyYUJ1dHRvbik7XG5cbiAgICByZXR1cm4gX3Bvc3NpYmxlQ29uc3RydWN0b3JSZXR1cm4odGhpcywgKENhbWVyYUJ1dHRvbi5fX3Byb3RvX18gfHwgT2JqZWN0LmdldFByb3RvdHlwZU9mKENhbWVyYUJ1dHRvbikpLmFwcGx5KHRoaXMsIGFyZ3VtZW50cykpO1xuICB9XG5cbiAgX2NyZWF0ZUNsYXNzKENhbWVyYUJ1dHRvbiwgW3tcbiAgICBrZXk6ICdidWlsZENTU0NsYXNzJyxcblxuICAgIC8qKlxuICAgICAqIEJ1aWxkcyB0aGUgZGVmYXVsdCBET00gYGNsYXNzTmFtZWAuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9XG4gICAgICogICAgICAgICBUaGUgRE9NIGBjbGFzc05hbWVgIGZvciB0aGlzIG9iamVjdC5cbiAgICAgKi9cbiAgICB2YWx1ZTogZnVuY3Rpb24gYnVpbGRDU1NDbGFzcygpIHtcbiAgICAgIHJldHVybiAndmpzLWNhbWVyYS1idXR0b24gdmpzLWNvbnRyb2wgdmpzLWJ1dHRvbiB2anMtaWNvbi1waG90by1jYW1lcmEnO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEVuYWJsZSB0aGUgYENhbWVyYUJ1dHRvbmAgZWxlbWVudCBzbyB0aGF0IGl0IGNhbiBiZSBhY3RpdmF0ZWQgb3IgY2xpY2tlZC5cbiAgICAgKi9cblxuICB9LCB7XG4gICAga2V5OiAnZW5hYmxlJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gZW5hYmxlKCkge1xuICAgICAgX2dldChDYW1lcmFCdXR0b24ucHJvdG90eXBlLl9fcHJvdG9fXyB8fCBPYmplY3QuZ2V0UHJvdG90eXBlT2YoQ2FtZXJhQnV0dG9uLnByb3RvdHlwZSksICdlbmFibGUnLCB0aGlzKS5jYWxsKHRoaXMpO1xuXG4gICAgICB0aGlzLm9uKHRoaXMucGxheWVyXywgJ3N0YXJ0UmVjb3JkJywgdGhpcy5vblN0YXJ0KTtcbiAgICAgIHRoaXMub24odGhpcy5wbGF5ZXJfLCAnc3RvcFJlY29yZCcsIHRoaXMub25TdG9wKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEaXNhYmxlIHRoZSBgQ2FtZXJhQnV0dG9uYCBlbGVtZW50IHNvIHRoYXQgaXQgY2Fubm90IGJlIGFjdGl2YXRlZCBvciBjbGlja2VkLlxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6ICdkaXNhYmxlJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gZGlzYWJsZSgpIHtcbiAgICAgIF9nZXQoQ2FtZXJhQnV0dG9uLnByb3RvdHlwZS5fX3Byb3RvX18gfHwgT2JqZWN0LmdldFByb3RvdHlwZU9mKENhbWVyYUJ1dHRvbi5wcm90b3R5cGUpLCAnZGlzYWJsZScsIHRoaXMpLmNhbGwodGhpcyk7XG5cbiAgICAgIHRoaXMub2ZmKHRoaXMucGxheWVyXywgJ3N0YXJ0UmVjb3JkJywgdGhpcy5vblN0YXJ0KTtcbiAgICAgIHRoaXMub2ZmKHRoaXMucGxheWVyXywgJ3N0b3BSZWNvcmQnLCB0aGlzLm9uU3RvcCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVGhpcyBnZXRzIGNhbGxlZCB3aGVuIHRoZSBidXR0b24gaXMgY2xpY2tlZC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7RXZlbnRUYXJnZXR+RXZlbnR9IGV2ZW50XG4gICAgICogICAgICAgIFRoZSBgdGFwYCBvciBgY2xpY2tgIGV2ZW50IHRoYXQgY2F1c2VkIHRoaXMgZnVuY3Rpb24gdG8gYmVcbiAgICAgKiAgICAgICAgY2FsbGVkLlxuICAgICAqXG4gICAgICogQGxpc3RlbnMgdGFwXG4gICAgICogQGxpc3RlbnMgY2xpY2tcbiAgICAgKi9cblxuICB9LCB7XG4gICAga2V5OiAnaGFuZGxlQ2xpY2snLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBoYW5kbGVDbGljayhldmVudCkge1xuICAgICAgdmFyIHJlY29yZGVyID0gdGhpcy5wbGF5ZXJfLnJlY29yZCgpO1xuXG4gICAgICBpZiAoIXJlY29yZGVyLmlzUHJvY2Vzc2luZygpKSB7XG4gICAgICAgIC8vIGNyZWF0ZSBzbmFwc2hvdFxuICAgICAgICByZWNvcmRlci5zdGFydCgpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gcmV0cnlcbiAgICAgICAgcmVjb3JkZXIucmV0cnlTbmFwc2hvdCgpO1xuXG4gICAgICAgIC8vIHJlc2V0IGNhbWVyYSBidXR0b25cbiAgICAgICAgdGhpcy5vblN0b3AoKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBZGQgdGhlIHZqcy1pY29uLXJlcGxheSBjbGFzcyB0byB0aGUgZWxlbWVudCBzbyBpdCBjYW4gY2hhbmdlIGFwcGVhcmFuY2UuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0V2ZW50VGFyZ2V0fkV2ZW50fSBbZXZlbnRdXG4gICAgICogICAgICAgIFRoZSBldmVudCB0aGF0IGNhdXNlZCB0aGlzIGZ1bmN0aW9uIHRvIHJ1bi5cbiAgICAgKlxuICAgICAqIEBsaXN0ZW5zIFBsYXllciNzdGFydFJlY29yZFxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6ICdvblN0YXJ0JyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gb25TdGFydChldmVudCkge1xuICAgICAgLy8gcmVwbGFjZSBlbGVtZW50IGNsYXNzIHNvIGl0IGNhbiBjaGFuZ2UgYXBwZWFyYW5jZVxuICAgICAgdGhpcy5yZW1vdmVDbGFzcygndmpzLWljb24tcGhvdG8tY2FtZXJhJyk7XG4gICAgICB0aGlzLmFkZENsYXNzKCd2anMtaWNvbi1yZXBsYXknKTtcblxuICAgICAgLy8gY2hhbmdlIHRoZSBidXR0b24gdGV4dFxuICAgICAgdGhpcy5jb250cm9sVGV4dCgnUmV0cnknKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBBZGQgdGhlIHZqcy1pY29uLXBob3RvLWNhbWVyYSBjbGFzcyB0byB0aGUgZWxlbWVudCBzbyBpdCBjYW4gY2hhbmdlIGFwcGVhcmFuY2UuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0V2ZW50VGFyZ2V0fkV2ZW50fSBbZXZlbnRdXG4gICAgICogICAgICAgIFRoZSBldmVudCB0aGF0IGNhdXNlZCB0aGlzIGZ1bmN0aW9uIHRvIHJ1bi5cbiAgICAgKlxuICAgICAqIEBsaXN0ZW5zIFBsYXllciNzdG9wUmVjb3JkXG4gICAgICovXG5cbiAgfSwge1xuICAgIGtleTogJ29uU3RvcCcsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIG9uU3RvcChldmVudCkge1xuICAgICAgLy8gcmVwbGFjZSBlbGVtZW50IGNsYXNzIHNvIGl0IGNhbiBjaGFuZ2UgYXBwZWFyYW5jZVxuICAgICAgdGhpcy5yZW1vdmVDbGFzcygndmpzLWljb24tcmVwbGF5Jyk7XG4gICAgICB0aGlzLmFkZENsYXNzKCd2anMtaWNvbi1waG90by1jYW1lcmEnKTtcblxuICAgICAgLy8gY2hhbmdlIHRoZSBidXR0b24gdGV4dFxuICAgICAgdGhpcy5jb250cm9sVGV4dCgnSW1hZ2UnKTtcbiAgICB9XG4gIH1dKTtcblxuICByZXR1cm4gQ2FtZXJhQnV0dG9uO1xufShCdXR0b24pO1xuXG4vKipcbiAqIFRoZSB0ZXh0IHRoYXQgc2hvdWxkIGRpc3BsYXkgb3ZlciB0aGUgYENhbWVyYUJ1dHRvbmBzIGNvbnRyb2xzLiBBZGRlZCBmb3IgbG9jYWxpemF0aW9uLlxuICpcbiAqIEB0eXBlIHtzdHJpbmd9XG4gKiBAcHJpdmF0ZVxuICovXG5cblxuQ2FtZXJhQnV0dG9uLnByb3RvdHlwZS5jb250cm9sVGV4dF8gPSAnSW1hZ2UnO1xuXG5Db21wb25lbnQucmVnaXN0ZXJDb21wb25lbnQoJ0NhbWVyYUJ1dHRvbicsIENhbWVyYUJ1dHRvbik7XG5cbmV4cG9ydHMuZGVmYXVsdCA9IENhbWVyYUJ1dHRvbjsiLCIndXNlIHN0cmljdCc7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwge1xuICB2YWx1ZTogdHJ1ZVxufSk7XG5cbnZhciBfY3JlYXRlQ2xhc3MgPSBmdW5jdGlvbiAoKSB7IGZ1bmN0aW9uIGRlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykgeyBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7IHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07IGRlc2NyaXB0b3IuZW51bWVyYWJsZSA9IGRlc2NyaXB0b3IuZW51bWVyYWJsZSB8fCBmYWxzZTsgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlOyBpZiAoXCJ2YWx1ZVwiIGluIGRlc2NyaXB0b3IpIGRlc2NyaXB0b3Iud3JpdGFibGUgPSB0cnVlOyBPYmplY3QuZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBkZXNjcmlwdG9yLmtleSwgZGVzY3JpcHRvcik7IH0gfSByZXR1cm4gZnVuY3Rpb24gKENvbnN0cnVjdG9yLCBwcm90b1Byb3BzLCBzdGF0aWNQcm9wcykgeyBpZiAocHJvdG9Qcm9wcykgZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvci5wcm90b3R5cGUsIHByb3RvUHJvcHMpOyBpZiAoc3RhdGljUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IsIHN0YXRpY1Byb3BzKTsgcmV0dXJuIENvbnN0cnVjdG9yOyB9OyB9KCk7XG5cbmZ1bmN0aW9uIF9jbGFzc0NhbGxDaGVjayhpbnN0YW5jZSwgQ29uc3RydWN0b3IpIHsgaWYgKCEoaW5zdGFuY2UgaW5zdGFuY2VvZiBDb25zdHJ1Y3RvcikpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkNhbm5vdCBjYWxsIGEgY2xhc3MgYXMgYSBmdW5jdGlvblwiKTsgfSB9XG5cbmZ1bmN0aW9uIF9wb3NzaWJsZUNvbnN0cnVjdG9yUmV0dXJuKHNlbGYsIGNhbGwpIHsgaWYgKCFzZWxmKSB7IHRocm93IG5ldyBSZWZlcmVuY2VFcnJvcihcInRoaXMgaGFzbid0IGJlZW4gaW5pdGlhbGlzZWQgLSBzdXBlcigpIGhhc24ndCBiZWVuIGNhbGxlZFwiKTsgfSByZXR1cm4gY2FsbCAmJiAodHlwZW9mIGNhbGwgPT09IFwib2JqZWN0XCIgfHwgdHlwZW9mIGNhbGwgPT09IFwiZnVuY3Rpb25cIikgPyBjYWxsIDogc2VsZjsgfVxuXG5mdW5jdGlvbiBfaW5oZXJpdHMoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgaWYgKHR5cGVvZiBzdXBlckNsYXNzICE9PSBcImZ1bmN0aW9uXCIgJiYgc3VwZXJDbGFzcyAhPT0gbnVsbCkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKFwiU3VwZXIgZXhwcmVzc2lvbiBtdXN0IGVpdGhlciBiZSBudWxsIG9yIGEgZnVuY3Rpb24sIG5vdCBcIiArIHR5cGVvZiBzdXBlckNsYXNzKTsgfSBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MgJiYgc3VwZXJDbGFzcy5wcm90b3R5cGUsIHsgY29uc3RydWN0b3I6IHsgdmFsdWU6IHN1YkNsYXNzLCBlbnVtZXJhYmxlOiBmYWxzZSwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9IH0pOyBpZiAoc3VwZXJDbGFzcykgT2JqZWN0LnNldFByb3RvdHlwZU9mID8gT2JqZWN0LnNldFByb3RvdHlwZU9mKHN1YkNsYXNzLCBzdXBlckNsYXNzKSA6IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuLyoqXG4gKiBAZmlsZSBkZXZpY2UtYnV0dG9uLmpzXG4gKiBAc2luY2UgMi4wLjBcbiAqL1xuXG52YXIgQnV0dG9uID0gdmlkZW9qcy5nZXRDb21wb25lbnQoJ0J1dHRvbicpO1xudmFyIENvbXBvbmVudCA9IHZpZGVvanMuZ2V0Q29tcG9uZW50KCdDb21wb25lbnQnKTtcblxuLyoqXG4gKiBCdXR0b24gdG8gc2VsZWN0IHJlY29yZGluZyBkZXZpY2UuXG4gKlxuICogQGNsYXNzXG4gKiBAYXVnbWVudHMgdmlkZW9qcy5CdXR0b25cbiovXG5cbnZhciBEZXZpY2VCdXR0b24gPSBmdW5jdGlvbiAoX0J1dHRvbikge1xuICBfaW5oZXJpdHMoRGV2aWNlQnV0dG9uLCBfQnV0dG9uKTtcblxuICBmdW5jdGlvbiBEZXZpY2VCdXR0b24oKSB7XG4gICAgX2NsYXNzQ2FsbENoZWNrKHRoaXMsIERldmljZUJ1dHRvbik7XG5cbiAgICByZXR1cm4gX3Bvc3NpYmxlQ29uc3RydWN0b3JSZXR1cm4odGhpcywgKERldmljZUJ1dHRvbi5fX3Byb3RvX18gfHwgT2JqZWN0LmdldFByb3RvdHlwZU9mKERldmljZUJ1dHRvbikpLmFwcGx5KHRoaXMsIGFyZ3VtZW50cykpO1xuICB9XG5cbiAgX2NyZWF0ZUNsYXNzKERldmljZUJ1dHRvbiwgW3tcbiAgICBrZXk6ICdoYW5kbGVDbGljaycsXG5cbiAgICAvKipcbiAgICAgKiBUaGlzIGdldHMgY2FsbGVkIHdoZW4gdGhpcyBidXR0b24gZ2V0czpcbiAgICAgKlxuICAgICAqIC0gQ2xpY2tlZCAodmlhIHRoZSBgY2xpY2tgIGV2ZW50LCBsaXN0ZW5pbmcgc3RhcnRzIGluIHRoZSBjb25zdHJ1Y3RvcilcbiAgICAgKiAtIFRhcHBlZCAodmlhIHRoZSBgdGFwYCBldmVudCwgbGlzdGVuaW5nIHN0YXJ0cyBpbiB0aGUgY29uc3RydWN0b3IpXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0V2ZW50VGFyZ2V0fkV2ZW50fSBldmVudFxuICAgICAqICAgICAgICBUaGUgYGtleWRvd25gLCBgdGFwYCwgb3IgYGNsaWNrYCBldmVudCB0aGF0IGNhdXNlZCB0aGlzIGZ1bmN0aW9uIHRvIGJlXG4gICAgICogICAgICAgIGNhbGxlZC5cbiAgICAgKlxuICAgICAqIEBsaXN0ZW5zIHRhcFxuICAgICAqIEBsaXN0ZW5zIGNsaWNrXG4gICAgICovXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGhhbmRsZUNsaWNrKGV2ZW50KSB7XG4gICAgICAvLyBvcGVuIGRldmljZSBkaWFsb2dcbiAgICAgIHRoaXMucGxheWVyXy5yZWNvcmQoKS5nZXREZXZpY2UoKTtcbiAgICB9XG4gIH1dKTtcblxuICByZXR1cm4gRGV2aWNlQnV0dG9uO1xufShCdXR0b24pO1xuXG4vKipcbiAqIFRoZSB0ZXh0IHRoYXQgc2hvdWxkIGRpc3BsYXkgb3ZlciB0aGUgYERldmljZUJ1dHRvbmBzIGNvbnRyb2xzLiBBZGRlZCBmb3IgbG9jYWxpemF0aW9uLlxuICpcbiAqIEB0eXBlIHtzdHJpbmd9XG4gKiBAcHJpdmF0ZVxuICovXG5cblxuRGV2aWNlQnV0dG9uLnByb3RvdHlwZS5jb250cm9sVGV4dF8gPSAnRGV2aWNlJztcblxuQ29tcG9uZW50LnJlZ2lzdGVyQ29tcG9uZW50KCdEZXZpY2VCdXR0b24nLCBEZXZpY2VCdXR0b24pO1xuXG5leHBvcnRzLmRlZmF1bHQgPSBEZXZpY2VCdXR0b247IiwiJ3VzZSBzdHJpY3QnO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHtcbiAgdmFsdWU6IHRydWVcbn0pO1xuXG52YXIgX2NyZWF0ZUNsYXNzID0gZnVuY3Rpb24gKCkgeyBmdW5jdGlvbiBkZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH0gcmV0dXJuIGZ1bmN0aW9uIChDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTsgaWYgKHN0YXRpY1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfTsgfSgpO1xuXG52YXIgX2dldCA9IGZ1bmN0aW9uIGdldChvYmplY3QsIHByb3BlcnR5LCByZWNlaXZlcikgeyBpZiAob2JqZWN0ID09PSBudWxsKSBvYmplY3QgPSBGdW5jdGlvbi5wcm90b3R5cGU7IHZhciBkZXNjID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihvYmplY3QsIHByb3BlcnR5KTsgaWYgKGRlc2MgPT09IHVuZGVmaW5lZCkgeyB2YXIgcGFyZW50ID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKG9iamVjdCk7IGlmIChwYXJlbnQgPT09IG51bGwpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfSBlbHNlIHsgcmV0dXJuIGdldChwYXJlbnQsIHByb3BlcnR5LCByZWNlaXZlcik7IH0gfSBlbHNlIGlmIChcInZhbHVlXCIgaW4gZGVzYykgeyByZXR1cm4gZGVzYy52YWx1ZTsgfSBlbHNlIHsgdmFyIGdldHRlciA9IGRlc2MuZ2V0OyBpZiAoZ2V0dGVyID09PSB1bmRlZmluZWQpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfSByZXR1cm4gZ2V0dGVyLmNhbGwocmVjZWl2ZXIpOyB9IH07XG5cbmZ1bmN0aW9uIF9jbGFzc0NhbGxDaGVjayhpbnN0YW5jZSwgQ29uc3RydWN0b3IpIHsgaWYgKCEoaW5zdGFuY2UgaW5zdGFuY2VvZiBDb25zdHJ1Y3RvcikpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkNhbm5vdCBjYWxsIGEgY2xhc3MgYXMgYSBmdW5jdGlvblwiKTsgfSB9XG5cbmZ1bmN0aW9uIF9wb3NzaWJsZUNvbnN0cnVjdG9yUmV0dXJuKHNlbGYsIGNhbGwpIHsgaWYgKCFzZWxmKSB7IHRocm93IG5ldyBSZWZlcmVuY2VFcnJvcihcInRoaXMgaGFzbid0IGJlZW4gaW5pdGlhbGlzZWQgLSBzdXBlcigpIGhhc24ndCBiZWVuIGNhbGxlZFwiKTsgfSByZXR1cm4gY2FsbCAmJiAodHlwZW9mIGNhbGwgPT09IFwib2JqZWN0XCIgfHwgdHlwZW9mIGNhbGwgPT09IFwiZnVuY3Rpb25cIikgPyBjYWxsIDogc2VsZjsgfVxuXG5mdW5jdGlvbiBfaW5oZXJpdHMoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgaWYgKHR5cGVvZiBzdXBlckNsYXNzICE9PSBcImZ1bmN0aW9uXCIgJiYgc3VwZXJDbGFzcyAhPT0gbnVsbCkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKFwiU3VwZXIgZXhwcmVzc2lvbiBtdXN0IGVpdGhlciBiZSBudWxsIG9yIGEgZnVuY3Rpb24sIG5vdCBcIiArIHR5cGVvZiBzdXBlckNsYXNzKTsgfSBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MgJiYgc3VwZXJDbGFzcy5wcm90b3R5cGUsIHsgY29uc3RydWN0b3I6IHsgdmFsdWU6IHN1YkNsYXNzLCBlbnVtZXJhYmxlOiBmYWxzZSwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9IH0pOyBpZiAoc3VwZXJDbGFzcykgT2JqZWN0LnNldFByb3RvdHlwZU9mID8gT2JqZWN0LnNldFByb3RvdHlwZU9mKHN1YkNsYXNzLCBzdXBlckNsYXNzKSA6IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuLyoqXG4gKiBAZmlsZSByZWNvcmQtY2FudmFzXG4gKiBAc2luY2UgMi4wLjBcbiAqL1xuXG52YXIgQ29tcG9uZW50ID0gdmlkZW9qcy5nZXRDb21wb25lbnQoJ0NvbXBvbmVudCcpO1xuXG4vKipcbiAqIENhbnZhcyBmb3IgZGlzcGxheWluZyBzbmFwc2hvdCBpbWFnZS5cbiAqXG4gKiBAY2xhc3NcbiAqIEBhdWdtZW50cyB2aWRlb2pzLkNvbXBvbmVudFxuKi9cblxudmFyIFJlY29yZENhbnZhcyA9IGZ1bmN0aW9uIChfQ29tcG9uZW50KSB7XG4gIF9pbmhlcml0cyhSZWNvcmRDYW52YXMsIF9Db21wb25lbnQpO1xuXG4gIGZ1bmN0aW9uIFJlY29yZENhbnZhcygpIHtcbiAgICBfY2xhc3NDYWxsQ2hlY2sodGhpcywgUmVjb3JkQ2FudmFzKTtcblxuICAgIHJldHVybiBfcG9zc2libGVDb25zdHJ1Y3RvclJldHVybih0aGlzLCAoUmVjb3JkQ2FudmFzLl9fcHJvdG9fXyB8fCBPYmplY3QuZ2V0UHJvdG90eXBlT2YoUmVjb3JkQ2FudmFzKSkuYXBwbHkodGhpcywgYXJndW1lbnRzKSk7XG4gIH1cblxuICBfY3JlYXRlQ2xhc3MoUmVjb3JkQ2FudmFzLCBbe1xuICAgIGtleTogJ2NyZWF0ZUVsJyxcblxuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlIHRoZSBgUmVjb3JkQ2FudmFzYHMgRE9NIGVsZW1lbnQuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtFbGVtZW50fVxuICAgICAqICAgICAgICAgVGhlIGRvbSBlbGVtZW50IHRoYXQgZ2V0cyBjcmVhdGVkLlxuICAgICAqL1xuICAgIHZhbHVlOiBmdW5jdGlvbiBjcmVhdGVFbCgpIHtcbiAgICAgIHJldHVybiBfZ2V0KFJlY29yZENhbnZhcy5wcm90b3R5cGUuX19wcm90b19fIHx8IE9iamVjdC5nZXRQcm90b3R5cGVPZihSZWNvcmRDYW52YXMucHJvdG90eXBlKSwgJ2NyZWF0ZUVsJywgdGhpcykuY2FsbCh0aGlzLCAnZGl2Jywge1xuICAgICAgICBjbGFzc05hbWU6ICd2anMtcmVjb3JkLWNhbnZhcycsXG4gICAgICAgIGlubmVySFRNTDogJzxjYW52YXM+PC9jYW52YXM+J1xuICAgICAgfSk7XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIFJlY29yZENhbnZhcztcbn0oQ29tcG9uZW50KTtcblxuQ29tcG9uZW50LnJlZ2lzdGVyQ29tcG9uZW50KCdSZWNvcmRDYW52YXMnLCBSZWNvcmRDYW52YXMpO1xuXG5leHBvcnRzLmRlZmF1bHQgPSBSZWNvcmRDYW52YXM7IiwiJ3VzZSBzdHJpY3QnO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHtcbiAgdmFsdWU6IHRydWVcbn0pO1xuXG52YXIgX2NyZWF0ZUNsYXNzID0gZnVuY3Rpb24gKCkgeyBmdW5jdGlvbiBkZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH0gcmV0dXJuIGZ1bmN0aW9uIChDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTsgaWYgKHN0YXRpY1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfTsgfSgpO1xuXG52YXIgX2dldCA9IGZ1bmN0aW9uIGdldChvYmplY3QsIHByb3BlcnR5LCByZWNlaXZlcikgeyBpZiAob2JqZWN0ID09PSBudWxsKSBvYmplY3QgPSBGdW5jdGlvbi5wcm90b3R5cGU7IHZhciBkZXNjID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcihvYmplY3QsIHByb3BlcnR5KTsgaWYgKGRlc2MgPT09IHVuZGVmaW5lZCkgeyB2YXIgcGFyZW50ID0gT2JqZWN0LmdldFByb3RvdHlwZU9mKG9iamVjdCk7IGlmIChwYXJlbnQgPT09IG51bGwpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfSBlbHNlIHsgcmV0dXJuIGdldChwYXJlbnQsIHByb3BlcnR5LCByZWNlaXZlcik7IH0gfSBlbHNlIGlmIChcInZhbHVlXCIgaW4gZGVzYykgeyByZXR1cm4gZGVzYy52YWx1ZTsgfSBlbHNlIHsgdmFyIGdldHRlciA9IGRlc2MuZ2V0OyBpZiAoZ2V0dGVyID09PSB1bmRlZmluZWQpIHsgcmV0dXJuIHVuZGVmaW5lZDsgfSByZXR1cm4gZ2V0dGVyLmNhbGwocmVjZWl2ZXIpOyB9IH07XG5cbmZ1bmN0aW9uIF9jbGFzc0NhbGxDaGVjayhpbnN0YW5jZSwgQ29uc3RydWN0b3IpIHsgaWYgKCEoaW5zdGFuY2UgaW5zdGFuY2VvZiBDb25zdHJ1Y3RvcikpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkNhbm5vdCBjYWxsIGEgY2xhc3MgYXMgYSBmdW5jdGlvblwiKTsgfSB9XG5cbmZ1bmN0aW9uIF9wb3NzaWJsZUNvbnN0cnVjdG9yUmV0dXJuKHNlbGYsIGNhbGwpIHsgaWYgKCFzZWxmKSB7IHRocm93IG5ldyBSZWZlcmVuY2VFcnJvcihcInRoaXMgaGFzbid0IGJlZW4gaW5pdGlhbGlzZWQgLSBzdXBlcigpIGhhc24ndCBiZWVuIGNhbGxlZFwiKTsgfSByZXR1cm4gY2FsbCAmJiAodHlwZW9mIGNhbGwgPT09IFwib2JqZWN0XCIgfHwgdHlwZW9mIGNhbGwgPT09IFwiZnVuY3Rpb25cIikgPyBjYWxsIDogc2VsZjsgfVxuXG5mdW5jdGlvbiBfaW5oZXJpdHMoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgaWYgKHR5cGVvZiBzdXBlckNsYXNzICE9PSBcImZ1bmN0aW9uXCIgJiYgc3VwZXJDbGFzcyAhPT0gbnVsbCkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKFwiU3VwZXIgZXhwcmVzc2lvbiBtdXN0IGVpdGhlciBiZSBudWxsIG9yIGEgZnVuY3Rpb24sIG5vdCBcIiArIHR5cGVvZiBzdXBlckNsYXNzKTsgfSBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MgJiYgc3VwZXJDbGFzcy5wcm90b3R5cGUsIHsgY29uc3RydWN0b3I6IHsgdmFsdWU6IHN1YkNsYXNzLCBlbnVtZXJhYmxlOiBmYWxzZSwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9IH0pOyBpZiAoc3VwZXJDbGFzcykgT2JqZWN0LnNldFByb3RvdHlwZU9mID8gT2JqZWN0LnNldFByb3RvdHlwZU9mKHN1YkNsYXNzLCBzdXBlckNsYXNzKSA6IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH1cblxuLyoqXG4gKiBAZmlsZSByZWNvcmQtaW5kaWNhdG9yLmpzXG4gKiBAc2luY2UgMi4wLjBcbiAqL1xuXG52YXIgQ29tcG9uZW50ID0gdmlkZW9qcy5nZXRDb21wb25lbnQoJ0NvbXBvbmVudCcpO1xuXG4vKipcbiAqIEljb24gaW5kaWNhdGluZyByZWNvcmRpbmcgaXMgYWN0aXZlLlxuICpcbiAqIEBjbGFzc1xuICogQGF1Z21lbnRzIHZpZGVvanMuQ29tcG9uZW50XG4qL1xuXG52YXIgUmVjb3JkSW5kaWNhdG9yID0gZnVuY3Rpb24gKF9Db21wb25lbnQpIHtcbiAgX2luaGVyaXRzKFJlY29yZEluZGljYXRvciwgX0NvbXBvbmVudCk7XG5cbiAgLyoqXG4gICAqIFRoZSBjb25zdHJ1Y3RvciBmdW5jdGlvbiBmb3IgdGhlIGNsYXNzLlxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKiBAcGFyYW0geyh2aWRlb2pzLlBsYXllcnxPYmplY3QpfSBwbGF5ZXIgLSBWaWRlby5qcyBwbGF5ZXIgaW5zdGFuY2UuXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBvcHRpb25zIC0gUGxheWVyIG9wdGlvbnMuXG4gICAqL1xuICBmdW5jdGlvbiBSZWNvcmRJbmRpY2F0b3IocGxheWVyLCBvcHRpb25zKSB7XG4gICAgX2NsYXNzQ2FsbENoZWNrKHRoaXMsIFJlY29yZEluZGljYXRvcik7XG5cbiAgICB2YXIgX3RoaXMgPSBfcG9zc2libGVDb25zdHJ1Y3RvclJldHVybih0aGlzLCAoUmVjb3JkSW5kaWNhdG9yLl9fcHJvdG9fXyB8fCBPYmplY3QuZ2V0UHJvdG90eXBlT2YoUmVjb3JkSW5kaWNhdG9yKSkuY2FsbCh0aGlzLCBwbGF5ZXIsIG9wdGlvbnMpKTtcblxuICAgIF90aGlzLmVuYWJsZSgpO1xuICAgIHJldHVybiBfdGhpcztcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGUgdGhlIGBSZWNvcmRJbmRpY2F0b3JgcyBET00gZWxlbWVudC5cbiAgICpcbiAgICogQHJldHVybiB7RWxlbWVudH1cbiAgICogICAgICAgICBUaGUgZG9tIGVsZW1lbnQgdGhhdCBnZXRzIGNyZWF0ZWQuXG4gICAqL1xuXG5cbiAgX2NyZWF0ZUNsYXNzKFJlY29yZEluZGljYXRvciwgW3tcbiAgICBrZXk6ICdjcmVhdGVFbCcsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGNyZWF0ZUVsKCkge1xuICAgICAgcmV0dXJuIF9nZXQoUmVjb3JkSW5kaWNhdG9yLnByb3RvdHlwZS5fX3Byb3RvX18gfHwgT2JqZWN0LmdldFByb3RvdHlwZU9mKFJlY29yZEluZGljYXRvci5wcm90b3R5cGUpLCAnY3JlYXRlRWwnLCB0aGlzKS5jYWxsKHRoaXMsICdkaXYnLCB7XG4gICAgICAgIGNsYXNzTmFtZTogJ3Zqcy1yZWNvcmQtaW5kaWNhdG9yIHZqcy1jb250cm9sJyxcbiAgICAgICAgZGlyOiAnbHRyJ1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRW5hYmxlIGV2ZW50IGhhbmRsZXJzLlxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6ICdlbmFibGUnLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBlbmFibGUoKSB7XG4gICAgICB0aGlzLm9uKHRoaXMucGxheWVyXywgJ3N0YXJ0UmVjb3JkJywgdGhpcy5zaG93KTtcbiAgICAgIHRoaXMub24odGhpcy5wbGF5ZXJfLCAnc3RvcFJlY29yZCcsIHRoaXMuaGlkZSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogRGlzYWJsZSBldmVudCBoYW5kbGVycy5cbiAgICAgKi9cblxuICB9LCB7XG4gICAga2V5OiAnZGlzYWJsZScsXG4gICAgdmFsdWU6IGZ1bmN0aW9uIGRpc2FibGUoKSB7XG4gICAgICB0aGlzLm9mZih0aGlzLnBsYXllcl8sICdzdGFydFJlY29yZCcsIHRoaXMuc2hvdyk7XG4gICAgICB0aGlzLm9mZih0aGlzLnBsYXllcl8sICdzdG9wUmVjb3JkJywgdGhpcy5oaWRlKTtcbiAgICB9XG4gIH1dKTtcblxuICByZXR1cm4gUmVjb3JkSW5kaWNhdG9yO1xufShDb21wb25lbnQpO1xuXG5Db21wb25lbnQucmVnaXN0ZXJDb21wb25lbnQoJ1JlY29yZEluZGljYXRvcicsIFJlY29yZEluZGljYXRvcik7XG5cbmV4cG9ydHMuZGVmYXVsdCA9IFJlY29yZEluZGljYXRvcjsiLCIndXNlIHN0cmljdCc7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwge1xuICB2YWx1ZTogdHJ1ZVxufSk7XG5cbnZhciBfY3JlYXRlQ2xhc3MgPSBmdW5jdGlvbiAoKSB7IGZ1bmN0aW9uIGRlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykgeyBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7IHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07IGRlc2NyaXB0b3IuZW51bWVyYWJsZSA9IGRlc2NyaXB0b3IuZW51bWVyYWJsZSB8fCBmYWxzZTsgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlOyBpZiAoXCJ2YWx1ZVwiIGluIGRlc2NyaXB0b3IpIGRlc2NyaXB0b3Iud3JpdGFibGUgPSB0cnVlOyBPYmplY3QuZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBkZXNjcmlwdG9yLmtleSwgZGVzY3JpcHRvcik7IH0gfSByZXR1cm4gZnVuY3Rpb24gKENvbnN0cnVjdG9yLCBwcm90b1Byb3BzLCBzdGF0aWNQcm9wcykgeyBpZiAocHJvdG9Qcm9wcykgZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvci5wcm90b3R5cGUsIHByb3RvUHJvcHMpOyBpZiAoc3RhdGljUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IsIHN0YXRpY1Byb3BzKTsgcmV0dXJuIENvbnN0cnVjdG9yOyB9OyB9KCk7XG5cbnZhciBfZ2V0ID0gZnVuY3Rpb24gZ2V0KG9iamVjdCwgcHJvcGVydHksIHJlY2VpdmVyKSB7IGlmIChvYmplY3QgPT09IG51bGwpIG9iamVjdCA9IEZ1bmN0aW9uLnByb3RvdHlwZTsgdmFyIGRlc2MgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKG9iamVjdCwgcHJvcGVydHkpOyBpZiAoZGVzYyA9PT0gdW5kZWZpbmVkKSB7IHZhciBwYXJlbnQgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2Yob2JqZWN0KTsgaWYgKHBhcmVudCA9PT0gbnVsbCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9IGVsc2UgeyByZXR1cm4gZ2V0KHBhcmVudCwgcHJvcGVydHksIHJlY2VpdmVyKTsgfSB9IGVsc2UgaWYgKFwidmFsdWVcIiBpbiBkZXNjKSB7IHJldHVybiBkZXNjLnZhbHVlOyB9IGVsc2UgeyB2YXIgZ2V0dGVyID0gZGVzYy5nZXQ7IGlmIChnZXR0ZXIgPT09IHVuZGVmaW5lZCkgeyByZXR1cm4gdW5kZWZpbmVkOyB9IHJldHVybiBnZXR0ZXIuY2FsbChyZWNlaXZlcik7IH0gfTtcblxuZnVuY3Rpb24gX2NsYXNzQ2FsbENoZWNrKGluc3RhbmNlLCBDb25zdHJ1Y3RvcikgeyBpZiAoIShpbnN0YW5jZSBpbnN0YW5jZW9mIENvbnN0cnVjdG9yKSkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2Fubm90IGNhbGwgYSBjbGFzcyBhcyBhIGZ1bmN0aW9uXCIpOyB9IH1cblxuZnVuY3Rpb24gX3Bvc3NpYmxlQ29uc3RydWN0b3JSZXR1cm4oc2VsZiwgY2FsbCkgeyBpZiAoIXNlbGYpIHsgdGhyb3cgbmV3IFJlZmVyZW5jZUVycm9yKFwidGhpcyBoYXNuJ3QgYmVlbiBpbml0aWFsaXNlZCAtIHN1cGVyKCkgaGFzbid0IGJlZW4gY2FsbGVkXCIpOyB9IHJldHVybiBjYWxsICYmICh0eXBlb2YgY2FsbCA9PT0gXCJvYmplY3RcIiB8fCB0eXBlb2YgY2FsbCA9PT0gXCJmdW5jdGlvblwiKSA/IGNhbGwgOiBzZWxmOyB9XG5cbmZ1bmN0aW9uIF9pbmhlcml0cyhzdWJDbGFzcywgc3VwZXJDbGFzcykgeyBpZiAodHlwZW9mIHN1cGVyQ2xhc3MgIT09IFwiZnVuY3Rpb25cIiAmJiBzdXBlckNsYXNzICE9PSBudWxsKSB7IHRocm93IG5ldyBUeXBlRXJyb3IoXCJTdXBlciBleHByZXNzaW9uIG11c3QgZWl0aGVyIGJlIG51bGwgb3IgYSBmdW5jdGlvbiwgbm90IFwiICsgdHlwZW9mIHN1cGVyQ2xhc3MpOyB9IHN1YkNsYXNzLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoc3VwZXJDbGFzcyAmJiBzdXBlckNsYXNzLnByb3RvdHlwZSwgeyBjb25zdHJ1Y3RvcjogeyB2YWx1ZTogc3ViQ2xhc3MsIGVudW1lcmFibGU6IGZhbHNlLCB3cml0YWJsZTogdHJ1ZSwgY29uZmlndXJhYmxlOiB0cnVlIH0gfSk7IGlmIChzdXBlckNsYXNzKSBPYmplY3Quc2V0UHJvdG90eXBlT2YgPyBPYmplY3Quc2V0UHJvdG90eXBlT2Yoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIDogc3ViQ2xhc3MuX19wcm90b19fID0gc3VwZXJDbGFzczsgfVxuXG4vKipcbiAqIEBmaWxlIHJlY29yZC10b2dnbGUuanNcbiAqIEBzaW5jZSAyLjAuMFxuICovXG5cbnZhciBCdXR0b24gPSB2aWRlb2pzLmdldENvbXBvbmVudCgnQnV0dG9uJyk7XG52YXIgQ29tcG9uZW50ID0gdmlkZW9qcy5nZXRDb21wb25lbnQoJ0NvbXBvbmVudCcpO1xuXG4vKipcbiAqIEJ1dHRvbiB0byB0b2dnbGUgYmV0d2VlbiBzdGFydCBhbmQgc3RvcCByZWNvcmRpbmcuXG4gKlxuICogQGNsYXNzXG4gKiBAYXVnbWVudHMgdmlkZW9qcy5CdXR0b25cbiovXG5cbnZhciBSZWNvcmRUb2dnbGUgPSBmdW5jdGlvbiAoX0J1dHRvbikge1xuICBfaW5oZXJpdHMoUmVjb3JkVG9nZ2xlLCBfQnV0dG9uKTtcblxuICBmdW5jdGlvbiBSZWNvcmRUb2dnbGUoKSB7XG4gICAgX2NsYXNzQ2FsbENoZWNrKHRoaXMsIFJlY29yZFRvZ2dsZSk7XG5cbiAgICByZXR1cm4gX3Bvc3NpYmxlQ29uc3RydWN0b3JSZXR1cm4odGhpcywgKFJlY29yZFRvZ2dsZS5fX3Byb3RvX18gfHwgT2JqZWN0LmdldFByb3RvdHlwZU9mKFJlY29yZFRvZ2dsZSkpLmFwcGx5KHRoaXMsIGFyZ3VtZW50cykpO1xuICB9XG5cbiAgX2NyZWF0ZUNsYXNzKFJlY29yZFRvZ2dsZSwgW3tcbiAgICBrZXk6ICdidWlsZENTU0NsYXNzJyxcblxuICAgIC8qKlxuICAgICAqIEJ1aWxkcyB0aGUgZGVmYXVsdCBET00gYGNsYXNzTmFtZWAuXG4gICAgICpcbiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9XG4gICAgICogICAgICAgICBUaGUgRE9NIGBjbGFzc05hbWVgIGZvciB0aGlzIG9iamVjdC5cbiAgICAgKi9cbiAgICB2YWx1ZTogZnVuY3Rpb24gYnVpbGRDU1NDbGFzcygpIHtcbiAgICAgIHJldHVybiAndmpzLXJlY29yZC1idXR0b24gdmpzLWNvbnRyb2wgdmpzLWJ1dHRvbiB2anMtaWNvbi1yZWNvcmQtc3RhcnQnO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEVuYWJsZSB0aGUgYFJlY29yZFRvZ2dsZWAgZWxlbWVudCBzbyB0aGF0IGl0IGNhbiBiZSBhY3RpdmF0ZWQgb3IgY2xpY2tlZC5cbiAgICAgKi9cblxuICB9LCB7XG4gICAga2V5OiAnZW5hYmxlJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gZW5hYmxlKCkge1xuICAgICAgX2dldChSZWNvcmRUb2dnbGUucHJvdG90eXBlLl9fcHJvdG9fXyB8fCBPYmplY3QuZ2V0UHJvdG90eXBlT2YoUmVjb3JkVG9nZ2xlLnByb3RvdHlwZSksICdlbmFibGUnLCB0aGlzKS5jYWxsKHRoaXMpO1xuXG4gICAgICB0aGlzLm9uKHRoaXMucGxheWVyXywgJ3N0YXJ0UmVjb3JkJywgdGhpcy5vblN0YXJ0KTtcbiAgICAgIHRoaXMub24odGhpcy5wbGF5ZXJfLCAnc3RvcFJlY29yZCcsIHRoaXMub25TdG9wKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEaXNhYmxlIHRoZSBgUmVjb3JkVG9nZ2xlYCBlbGVtZW50IHNvIHRoYXQgaXQgY2Fubm90IGJlIGFjdGl2YXRlZCBvciBjbGlja2VkLlxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6ICdkaXNhYmxlJyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gZGlzYWJsZSgpIHtcbiAgICAgIF9nZXQoUmVjb3JkVG9nZ2xlLnByb3RvdHlwZS5fX3Byb3RvX18gfHwgT2JqZWN0LmdldFByb3RvdHlwZU9mKFJlY29yZFRvZ2dsZS5wcm90b3R5cGUpLCAnZGlzYWJsZScsIHRoaXMpLmNhbGwodGhpcyk7XG5cbiAgICAgIHRoaXMub2ZmKHRoaXMucGxheWVyXywgJ3N0YXJ0UmVjb3JkJywgdGhpcy5vblN0YXJ0KTtcbiAgICAgIHRoaXMub2ZmKHRoaXMucGxheWVyXywgJ3N0b3BSZWNvcmQnLCB0aGlzLm9uU3RvcCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogVGhpcyBnZXRzIGNhbGxlZCB3aGVuIHRoZSBidXR0b24gaXMgY2xpY2tlZC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7RXZlbnRUYXJnZXR+RXZlbnR9IGV2ZW50XG4gICAgICogICAgICAgIFRoZSBgdGFwYCBvciBgY2xpY2tgIGV2ZW50IHRoYXQgY2F1c2VkIHRoaXMgZnVuY3Rpb24gdG8gYmVcbiAgICAgKiAgICAgICAgY2FsbGVkLlxuICAgICAqXG4gICAgICogQGxpc3RlbnMgdGFwXG4gICAgICogQGxpc3RlbnMgY2xpY2tcbiAgICAgKi9cblxuICB9LCB7XG4gICAga2V5OiAnaGFuZGxlQ2xpY2snLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBoYW5kbGVDbGljayhldmVudCkge1xuICAgICAgdmFyIHJlY29yZGVyID0gdGhpcy5wbGF5ZXJfLnJlY29yZCgpO1xuICAgICAgaWYgKCFyZWNvcmRlci5pc1JlY29yZGluZygpKSB7XG4gICAgICAgIHJlY29yZGVyLnN0YXJ0KCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZWNvcmRlci5zdG9wKCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQWRkIHRoZSB2anMtaWNvbi1yZWNvcmQtc3RvcCBjbGFzcyB0byB0aGUgZWxlbWVudCBzbyBpdCBjYW4gY2hhbmdlIGFwcGVhcmFuY2UuXG4gICAgICpcbiAgICAgKiBAcGFyYW0ge0V2ZW50VGFyZ2V0fkV2ZW50fSBbZXZlbnRdXG4gICAgICogICAgICAgIFRoZSBldmVudCB0aGF0IGNhdXNlZCB0aGlzIGZ1bmN0aW9uIHRvIHJ1bi5cbiAgICAgKlxuICAgICAqIEBsaXN0ZW5zIFBsYXllciNzdGFydFJlY29yZFxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6ICdvblN0YXJ0JyxcbiAgICB2YWx1ZTogZnVuY3Rpb24gb25TdGFydChldmVudCkge1xuICAgICAgLy8gcmVwbGFjZSBlbGVtZW50IGNsYXNzIHNvIGl0IGNhbiBjaGFuZ2UgYXBwZWFyYW5jZVxuICAgICAgdGhpcy5yZW1vdmVDbGFzcygndmpzLWljb24tcmVjb3JkLXN0YXJ0Jyk7XG4gICAgICB0aGlzLmFkZENsYXNzKCd2anMtaWNvbi1yZWNvcmQtc3RvcCcpO1xuXG4gICAgICAvLyBjaGFuZ2UgdGhlIGJ1dHRvbiB0ZXh0XG4gICAgICB0aGlzLmNvbnRyb2xUZXh0KCdTdG9wJyk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQWRkIHRoZSB2anMtaWNvbi1yZWNvcmQtc3RhcnQgY2xhc3MgdG8gdGhlIGVsZW1lbnQgc28gaXQgY2FuIGNoYW5nZSBhcHBlYXJhbmNlLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtFdmVudFRhcmdldH5FdmVudH0gW2V2ZW50XVxuICAgICAqICAgICAgICBUaGUgZXZlbnQgdGhhdCBjYXVzZWQgdGhpcyBmdW5jdGlvbiB0byBydW4uXG4gICAgICpcbiAgICAgKiBAbGlzdGVucyBQbGF5ZXIjc3RvcFJlY29yZFxuICAgICAqL1xuXG4gIH0sIHtcbiAgICBrZXk6ICdvblN0b3AnLFxuICAgIHZhbHVlOiBmdW5jdGlvbiBvblN0b3AoZXZlbnQpIHtcbiAgICAgIC8vIHJlcGxhY2UgZWxlbWVudCBjbGFzcyBzbyBpdCBjYW4gY2hhbmdlIGFwcGVhcmFuY2VcbiAgICAgIHRoaXMucmVtb3ZlQ2xhc3MoJ3Zqcy1pY29uLXJlY29yZC1zdG9wJyk7XG4gICAgICB0aGlzLmFkZENsYXNzKCd2anMtaWNvbi1yZWNvcmQtc3RhcnQnKTtcblxuICAgICAgLy8gY2hhbmdlIHRoZSBidXR0b24gdGV4dFxuICAgICAgdGhpcy5jb250cm9sVGV4dCgnUmVjb3JkJyk7XG4gICAgfVxuICB9XSk7XG5cbiAgcmV0dXJuIFJlY29yZFRvZ2dsZTtcbn0oQnV0dG9uKTtcblxuLyoqXG4gKiBUaGUgdGV4dCB0aGF0IHNob3VsZCBkaXNwbGF5IG92ZXIgdGhlIGBSZWNvcmRUb2dnbGVgcyBjb250cm9scy4gQWRkZWQgZm9yIGxvY2FsaXphdGlvbi5cbiAqXG4gKiBAdHlwZSB7c3RyaW5nfVxuICogQHByaXZhdGVcbiAqL1xuXG5cblJlY29yZFRvZ2dsZS5wcm90b3R5cGUuY29udHJvbFRleHRfID0gJ1JlY29yZCc7XG5cbkNvbXBvbmVudC5yZWdpc3RlckNvbXBvbmVudCgnUmVjb3JkVG9nZ2xlJywgUmVjb3JkVG9nZ2xlKTtcblxuZXhwb3J0cy5kZWZhdWx0ID0gUmVjb3JkVG9nZ2xlOyIsIid1c2Ugc3RyaWN0JztcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7XG4gICAgdmFsdWU6IHRydWVcbn0pO1xuLyoqXG4gKiBAZmlsZSBkZWZhdWx0cy5qc1xuICogQHNpbmNlIDIuMC4wXG4gKi9cblxuLy9wbHVnaW4gZGVmYXVsdHNcbnZhciBwbHVnaW5EZWZhdWx0T3B0aW9ucyA9IHtcbiAgICAvLyBTaW5nbGUgc25hcHNob3QgaW1hZ2UuXG4gICAgaW1hZ2U6IGZhbHNlLFxuICAgIC8vIEluY2x1ZGUgYXVkaW8gaW4gdGhlIHJlY29yZGVkIGNsaXAuXG4gICAgYXVkaW86IGZhbHNlLFxuICAgIC8vIEluY2x1ZGUgdmlkZW8gaW4gdGhlIHJlY29yZGVkIGNsaXAuXG4gICAgdmlkZW86IGZhbHNlLFxuICAgIC8vIEFuaW1hdGVkIEdJRi5cbiAgICBhbmltYXRpb246IGZhbHNlLFxuICAgIC8vIE1heGltdW0gbGVuZ3RoIG9mIHRoZSByZWNvcmRlZCBjbGlwLlxuICAgIG1heExlbmd0aDogMTAsXG4gICAgLy8gV2lkdGggb2YgdGhlIHJlY29yZGVkIHZpZGVvIGZyYW1lcy5cbiAgICBmcmFtZVdpZHRoOiAzMjAsXG4gICAgLy8gSGVpZ2h0IG9mIHRoZSByZWNvcmRlZCB2aWRlbyBmcmFtZXMuXG4gICAgZnJhbWVIZWlnaHQ6IDI0MCxcbiAgICAvLyBFbmFibGVzIGNvbnNvbGUgbG9nZ2luZyBmb3IgZGVidWdnaW5nIHB1cnBvc2VzLlxuICAgIGRlYnVnOiBmYWxzZSxcbiAgICAvLyBUaGUgbWltZSB0eXBlIGZvciB0aGUgdmlkZW8gcmVjb3JkZXIuIERlZmF1bHQgdG8gJ3ZpZGVvL3dlYm0nLlxuICAgIC8vIFVzZSAndmlkZW8vbXA0JyAoRmlyZWZveCkgb3IgJ3ZpZGVvL3dlYm07Y29kZWNzPUgyNjQnIChDaHJvbWUgNTIgYW5kXG4gICAgLy8gbmV3ZXIpIGZvciBNUDQuXG4gICAgdmlkZW9NaW1lVHlwZTogJ3ZpZGVvL3dlYm0nLFxuICAgIC8vIFZpZGVvIHJlY29yZGVyIHR5cGUgdG8gdXNlLiBUaGlzIGFsbG93cyB5b3UgdG8gc3BlY2lmeSBhbiBhbHRlcm5hdGl2ZVxuICAgIC8vIHJlY29yZGVyIGNsYXNzLCBlLmcuIFdoYW1teVJlY29yZGVyLiBEZWZhdWx0cyB0byAnYXV0bycgd2hpY2ggbGV0J3NcbiAgICAvLyByZWNvcmRydGMgc3BlY2lmeSB0aGUgYmVzdCBhdmFpbGFibGUgcmVjb3JkZXIgdHlwZS5cbiAgICB2aWRlb1JlY29yZGVyVHlwZTogJ2F1dG8nLFxuICAgIC8vIEF1ZGlvIHJlY29yZGluZyBsaWJyYXJ5IHRvIHVzZS4gTGVnYWwgdmFsdWVzIGFyZSAncmVjb3JkcnRjJyxcbiAgICAvLyAnbGlidm9yYmlzLmpzJywgJ29wdXMtcmVjb3JkZXInLCAnbGFtZWpzJyBhbmQgJ3JlY29yZGVyLmpzJy5cbiAgICBhdWRpb0VuZ2luZTogJ3JlY29yZHJ0YycsXG4gICAgLy8gQXVkaW8gcmVjb3JkZXIgdHlwZSB0byB1c2UuIFRoaXMgYWxsb3dzIHlvdSB0byBzcGVjaWZ5IGFuIGFsdGVybmF0aXZlXG4gICAgLy8gcmVjb3JkZXIgY2xhc3MsIGUuZy4gU3RlcmVvQXVkaW9SZWNvcmRlci4gRGVmYXVsdHMgdG8gJ2F1dG8nIHdoaWNoIGxldCdzXG4gICAgLy8gcmVjb3JkcnRjIHNwZWNpZnkgdGhlIGJlc3QgYXZhaWxhYmxlIHJlY29yZGVyIHR5cGUuIEN1cnJlbnRseSB0aGlzXG4gICAgLy8gc2V0dGluZyBpcyBvbmx5IHVzZWQgd2l0aCB0aGUgJ3JlY29yZHJ0YycgYXVkaW9FbmdpbmUuXG4gICAgYXVkaW9SZWNvcmRlclR5cGU6ICdhdXRvJyxcbiAgICAvLyBUaGUgbWltZSB0eXBlIGZvciB0aGUgYXVkaW8gcmVjb3JkZXIuIERlZmF1bHRzIHRvICdhdXRvJyB3aGljaCB3aWxsIHBpY2tcbiAgICAvLyB0aGUgYmVzdCBvcHRpb24gYXZhaWxhYmxlIGluIHRoZSBicm93c2VyIChlLmcuIGVpdGhlciAnYXVkaW8vd2F2JyxcbiAgICAvLyAnYXVkaW8vb2dnJyBvciAnYXVkaW8vd2VibScpLlxuICAgIGF1ZGlvTWltZVR5cGU6ICdhdXRvJyxcbiAgICAvLyBUaGUgc2l6ZSBvZiB0aGUgYXVkaW8gYnVmZmVyIChpbiBzYW1wbGUtZnJhbWVzKSB3aGljaCBuZWVkcyB0b1xuICAgIC8vIGJlIHByb2Nlc3NlZCBlYWNoIHRpbWUgb25wcm9jZXNzYXVkaW8gaXMgY2FsbGVkLlxuICAgIC8vIEZyb20gdGhlIHNwZWM6IFRoaXMgdmFsdWUgY29udHJvbHMgaG93IGZyZXF1ZW50bHkgdGhlIGF1ZGlvcHJvY2VzcyBldmVudCBpc1xuICAgIC8vIGRpc3BhdGNoZWQgYW5kIGhvdyBtYW55IHNhbXBsZS1mcmFtZXMgbmVlZCB0byBiZSBwcm9jZXNzZWQgZWFjaCBjYWxsLlxuICAgIC8vIExvd2VyIHZhbHVlcyBmb3IgYnVmZmVyIHNpemUgd2lsbCByZXN1bHQgaW4gYSBsb3dlciAoYmV0dGVyKSBsYXRlbmN5LlxuICAgIC8vIEhpZ2hlciB2YWx1ZXMgd2lsbCBiZSBuZWNlc3NhcnkgdG8gYXZvaWQgYXVkaW8gYnJlYWt1cCBhbmQgZ2xpdGNoZXMuXG4gICAgLy8gTGVnYWwgdmFsdWVzIGFyZSAyNTYsIDUxMiwgMTAyNCwgMjA0OCwgNDA5NiwgODE5MiBvciAxNjM4NC5cbiAgICBhdWRpb0J1ZmZlclNpemU6IDQwOTYsXG4gICAgLy8gVGhlIGF1ZGlvIHNhbXBsZSByYXRlIChpbiBzYW1wbGUtZnJhbWVzIHBlciBzZWNvbmQpIGF0IHdoaWNoIHRoZVxuICAgIC8vIEF1ZGlvQ29udGV4dCBoYW5kbGVzIGF1ZGlvLiBJdCBpcyBhc3N1bWVkIHRoYXQgYWxsIEF1ZGlvTm9kZXNcbiAgICAvLyBpbiB0aGUgY29udGV4dCBydW4gYXQgdGhpcyByYXRlLiBJbiBtYWtpbmcgdGhpcyBhc3N1bXB0aW9uLFxuICAgIC8vIHNhbXBsZS1yYXRlIGNvbnZlcnRlcnMgb3IgXCJ2YXJpc3BlZWRcIiBwcm9jZXNzb3JzIGFyZSBub3Qgc3VwcG9ydGVkXG4gICAgLy8gaW4gcmVhbC10aW1lIHByb2Nlc3NpbmcuXG4gICAgLy8gVGhlIHNhbXBsZVJhdGUgcGFyYW1ldGVyIGRlc2NyaWJlcyB0aGUgc2FtcGxlLXJhdGUgb2YgdGhlXG4gICAgLy8gbGluZWFyIFBDTSBhdWRpbyBkYXRhIGluIHRoZSBidWZmZXIgaW4gc2FtcGxlLWZyYW1lcyBwZXIgc2Vjb25kLlxuICAgIC8vIEFuIGltcGxlbWVudGF0aW9uIG11c3Qgc3VwcG9ydCBzYW1wbGUtcmF0ZXMgaW4gYXQgbGVhc3RcbiAgICAvLyB0aGUgcmFuZ2UgMjIwNTAgdG8gOTYwMDAuXG4gICAgYXVkaW9TYW1wbGVSYXRlOiA0NDEwMCxcbiAgICAvLyBUaGUgYXVkaW8gYml0cmF0ZSBpbiBrYnBzIChvbmx5IHVzZWQgaW4gbGFtZWpzIHBsdWdpbikuXG4gICAgYXVkaW9CaXRSYXRlOiAxMjgsXG4gICAgLy8gQWxsb3dzIHlvdSB0byByZWNvcmQgc2luZ2xlLWNoYW5uZWwgYXVkaW8sIHdoaWNoIGNhbiByZWR1Y2UgdGhlXG4gICAgLy8gZmlsZXNpemUuXG4gICAgYXVkaW9DaGFubmVsczogMixcbiAgICAvLyBVUkwgZm9yIHRoZSBhdWRpbyB3b3JrZXIuXG4gICAgYXVkaW9Xb3JrZXJVUkw6ICcnLFxuICAgIC8vIEZyYW1lIHJhdGUgaW4gZnJhbWVzIHBlciBzZWNvbmQuXG4gICAgYW5pbWF0aW9uRnJhbWVSYXRlOiAyMDAsXG4gICAgLy8gU2V0cyBxdWFsaXR5IG9mIGNvbG9yIHF1YW50aXphdGlvbiAoY29udmVyc2lvbiBvZiBpbWFnZXMgdG8gdGhlXG4gICAgLy8gbWF4aW11bSAyNTYgY29sb3JzIGFsbG93ZWQgYnkgdGhlIEdJRiBzcGVjaWZpY2F0aW9uKS5cbiAgICAvLyBMb3dlciB2YWx1ZXMgKG1pbmltdW0gPSAxKSBwcm9kdWNlIGJldHRlciBjb2xvcnMsXG4gICAgLy8gYnV0IHNsb3cgcHJvY2Vzc2luZyBzaWduaWZpY2FudGx5LiAxMCBpcyB0aGUgZGVmYXVsdCxcbiAgICAvLyBhbmQgcHJvZHVjZXMgZ29vZCBjb2xvciBtYXBwaW5nIGF0IHJlYXNvbmFibGUgc3BlZWRzLlxuICAgIC8vIFZhbHVlcyBncmVhdGVyIHRoYW4gMjAgZG8gbm90IHlpZWxkIHNpZ25pZmljYW50IGltcHJvdmVtZW50c1xuICAgIC8vIGluIHNwZWVkLlxuICAgIGFuaW1hdGlvblF1YWxpdHk6IDEwLFxuICAgIC8vIEFjY2VwdHMgbnVtYmVycyBpbiBtaWxsaXNlY29uZHM7IHVzZSB0aGlzIHRvIGZvcmNlIGludGVydmFscy1iYXNlZCBibG9icy5cbiAgICB0aW1lU2xpY2U6IDBcbn07XG5cbmV4cG9ydHMuZGVmYXVsdCA9IHBsdWdpbkRlZmF1bHRPcHRpb25zOyIsIid1c2Ugc3RyaWN0JztcblxuT2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFwiX19lc01vZHVsZVwiLCB7XG4gICAgdmFsdWU6IHRydWVcbn0pO1xuXG52YXIgX2NyZWF0ZUNsYXNzID0gZnVuY3Rpb24gKCkgeyBmdW5jdGlvbiBkZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHsgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykgeyB2YXIgZGVzY3JpcHRvciA9IHByb3BzW2ldOyBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7IGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTsgaWYgKFwidmFsdWVcIiBpbiBkZXNjcmlwdG9yKSBkZXNjcmlwdG9yLndyaXRhYmxlID0gdHJ1ZTsgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpOyB9IH0gcmV0dXJuIGZ1bmN0aW9uIChDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHsgaWYgKHByb3RvUHJvcHMpIGRlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IucHJvdG90eXBlLCBwcm90b1Byb3BzKTsgaWYgKHN0YXRpY1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7IHJldHVybiBDb25zdHJ1Y3RvcjsgfTsgfSgpO1xuXG5mdW5jdGlvbiBfY2xhc3NDYWxsQ2hlY2soaW5zdGFuY2UsIENvbnN0cnVjdG9yKSB7IGlmICghKGluc3RhbmNlIGluc3RhbmNlb2YgQ29uc3RydWN0b3IpKSB7IHRocm93IG5ldyBUeXBlRXJyb3IoXCJDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb25cIik7IH0gfVxuXG5mdW5jdGlvbiBfcG9zc2libGVDb25zdHJ1Y3RvclJldHVybihzZWxmLCBjYWxsKSB7IGlmICghc2VsZikgeyB0aHJvdyBuZXcgUmVmZXJlbmNlRXJyb3IoXCJ0aGlzIGhhc24ndCBiZWVuIGluaXRpYWxpc2VkIC0gc3VwZXIoKSBoYXNuJ3QgYmVlbiBjYWxsZWRcIik7IH0gcmV0dXJuIGNhbGwgJiYgKHR5cGVvZiBjYWxsID09PSBcIm9iamVjdFwiIHx8IHR5cGVvZiBjYWxsID09PSBcImZ1bmN0aW9uXCIpID8gY2FsbCA6IHNlbGY7IH1cblxuZnVuY3Rpb24gX2luaGVyaXRzKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7IGlmICh0eXBlb2Ygc3VwZXJDbGFzcyAhPT0gXCJmdW5jdGlvblwiICYmIHN1cGVyQ2xhc3MgIT09IG51bGwpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcihcIlN1cGVyIGV4cHJlc3Npb24gbXVzdCBlaXRoZXIgYmUgbnVsbCBvciBhIGZ1bmN0aW9uLCBub3QgXCIgKyB0eXBlb2Ygc3VwZXJDbGFzcyk7IH0gc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzICYmIHN1cGVyQ2xhc3MucHJvdG90eXBlLCB7IGNvbnN0cnVjdG9yOiB7IHZhbHVlOiBzdWJDbGFzcywgZW51bWVyYWJsZTogZmFsc2UsIHdyaXRhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSB9KTsgaWYgKHN1cGVyQ2xhc3MpIE9iamVjdC5zZXRQcm90b3R5cGVPZiA/IE9iamVjdC5zZXRQcm90b3R5cGVPZihzdWJDbGFzcywgc3VwZXJDbGFzcykgOiBzdWJDbGFzcy5fX3Byb3RvX18gPSBzdXBlckNsYXNzOyB9XG5cbi8qKlxuICogQGZpbGUgcmVjb3JkLWVuZ2luZS5qc1xuICogQHNpbmNlIDIuMC4wXG4gKi9cblxudmFyIENvbXBvbmVudCA9IHZpZGVvanMuZ2V0Q29tcG9uZW50KCdDb21wb25lbnQnKTtcblxuLy8gc3VwcG9ydGVkIHJlY29yZGVyIHBsdWdpbiBlbmdpbmVzXG52YXIgUkVDT1JEUlRDID0gJ3JlY29yZHJ0Yyc7XG52YXIgTElCVk9SQklTSlMgPSAnbGlidm9yYmlzLmpzJztcbnZhciBSRUNPUkRFUkpTID0gJ3JlY29yZGVyLmpzJztcbnZhciBMQU1FSlMgPSAnbGFtZWpzJztcbnZhciBPUFVTUkVDT1JERVIgPSAnb3B1cy1yZWNvcmRlcic7XG5cbi8qKlxuICogQmFzZSBjbGFzcyBmb3IgcmVjb3JkZXIgYmFja2VuZHMuXG4gKiBAY2xhc3NcbiAqIEBhdWdtZW50cyB2aWRlb2pzLkNvbXBvbmVudFxuICovXG5cbnZhciBSZWNvcmRFbmdpbmUgPSBmdW5jdGlvbiAoX0NvbXBvbmVudCkge1xuICAgIF9pbmhlcml0cyhSZWNvcmRFbmdpbmUsIF9Db21wb25lbnQpO1xuXG4gICAgLyoqXG4gICAgICogQ3JlYXRlcyBhbiBpbnN0YW5jZSBvZiB0aGlzIGNsYXNzLlxuICAgICAqXG4gICAgICogQHBhcmFtICB7UGxheWVyfSBwbGF5ZXJcbiAgICAgKiAgICAgICAgIFRoZSBgUGxheWVyYCB0aGF0IHRoaXMgY2xhc3Mgc2hvdWxkIGJlIGF0dGFjaGVkIHRvLlxuICAgICAqXG4gICAgICogQHBhcmFtICB7T2JqZWN0fSBbb3B0aW9uc11cbiAgICAgKiAgICAgICAgIFRoZSBrZXkvdmFsdWUgc3RvcmUgb2YgcGxheWVyIG9wdGlvbnMuXG4gICAgICovXG4gICAgZnVuY3Rpb24gUmVjb3JkRW5naW5lKHBsYXllciwgb3B0aW9ucykge1xuICAgICAgICBfY2xhc3NDYWxsQ2hlY2sodGhpcywgUmVjb3JkRW5naW5lKTtcblxuICAgICAgICAvLyBhdXRvIG1peGluIHRoZSBldmVudGVkIG1peGluIChyZXF1aXJlZCBzaW5jZSB2aWRlby5qcyB2Ni42LjApXG4gICAgICAgIG9wdGlvbnMuZXZlbnRlZCA9IHRydWU7XG5cbiAgICAgICAgcmV0dXJuIF9wb3NzaWJsZUNvbnN0cnVjdG9yUmV0dXJuKHRoaXMsIChSZWNvcmRFbmdpbmUuX19wcm90b19fIHx8IE9iamVjdC5nZXRQcm90b3R5cGVPZihSZWNvcmRFbmdpbmUpKS5jYWxsKHRoaXMsIHBsYXllciwgb3B0aW9ucykpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbW92ZSBhbnkgdGVtcG9yYXJ5IGRhdGEgYW5kIHJlZmVyZW5jZXMgdG8gc3RyZWFtcy5cbiAgICAgKiBAcHJpdmF0ZVxuICAgICAqL1xuXG5cbiAgICBfY3JlYXRlQ2xhc3MoUmVjb3JkRW5naW5lLCBbe1xuICAgICAgICBrZXk6ICdkaXNwb3NlJyxcbiAgICAgICAgdmFsdWU6IGZ1bmN0aW9uIGRpc3Bvc2UoKSB7XG4gICAgICAgICAgICAvLyBkaXNwb3NlIHByZXZpb3VzIHJlY29yZGluZ1xuICAgICAgICAgICAgaWYgKHRoaXMucmVjb3JkZWREYXRhICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBVUkwucmV2b2tlT2JqZWN0VVJMKHRoaXMucmVjb3JkZWREYXRhKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBBZGQgZmlsZW5hbWUgYW5kIHRpbWVzdGFtcCB0byByZWNvcmRlZCBmaWxlIG9iamVjdC5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHsoYmxvYnxmaWxlKX0gZmlsZU9iaiAtIEJsb2Igb3IgRmlsZSBvYmplY3QuXG4gICAgICAgICAqL1xuXG4gICAgfSwge1xuICAgICAgICBrZXk6ICdhZGRGaWxlSW5mbycsXG4gICAgICAgIHZhbHVlOiBmdW5jdGlvbiBhZGRGaWxlSW5mbyhmaWxlT2JqKSB7XG4gICAgICAgICAgICBpZiAoZmlsZU9iaiBpbnN0YW5jZW9mIEJsb2IgfHwgZmlsZU9iaiBpbnN0YW5jZW9mIEZpbGUpIHtcbiAgICAgICAgICAgICAgICAvLyBzZXQgbW9kaWZpY2F0aW9uIGRhdGVcbiAgICAgICAgICAgICAgICB2YXIgbm93ID0gbmV3IERhdGUoKTtcbiAgICAgICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgICAgICBmaWxlT2JqLmxhc3RNb2RpZmllZCA9IG5vdy5nZXRUaW1lKCk7XG4gICAgICAgICAgICAgICAgICAgIGZpbGVPYmoubGFzdE1vZGlmaWVkRGF0ZSA9IG5vdztcbiAgICAgICAgICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChlIGluc3RhbmNlb2YgVHlwZUVycm9yKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBpZ25vcmU6IHNldHRpbmcgZ2V0dGVyLW9ubHkgcHJvcGVydHkgXCJsYXN0TW9kaWZpZWREYXRlXCJcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIHJlLXJhaXNlIGVycm9yXG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBlO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIC8vIGd1ZXNzIGV4dGVuc2lvbiBuYW1lIGZyb20gbWltZSB0eXBlLCBlLmcuIGF1ZGlvL29nZywgYnV0XG4gICAgICAgICAgICAgICAgLy8gYW55IGV4dGVuc2lvbiBpcyB2YWxpZCBoZXJlLiBDaHJvbWUgYWxzbyBhY2NlcHRzIGV4dGVuZGVkXG4gICAgICAgICAgICAgICAgLy8gbWltZSB0eXBlcyBsaWtlIHZpZGVvL3dlYm07Y29kZWNzPWgyNjQsdnA5LG9wdXNcbiAgICAgICAgICAgICAgICB2YXIgZmlsZUV4dGVuc2lvbiA9ICcuJyArIGZpbGVPYmoudHlwZS5zcGxpdCgnLycpWzFdO1xuICAgICAgICAgICAgICAgIGlmIChmaWxlRXh0ZW5zaW9uLmluZGV4T2YoJzsnKSA+IC0xKSB7XG4gICAgICAgICAgICAgICAgICAgIGZpbGVFeHRlbnNpb24gPSBmaWxlRXh0ZW5zaW9uLnNwbGl0KCc7JylbMF07XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgLy8gdXNlIHRpbWVzdGFtcCBpbiBmaWxlbmFtZSwgZS5nLiAxNDUxMTgwOTQxMzI2Lm9nZ1xuICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgIGZpbGVPYmoubmFtZSA9IG5vdy5nZXRUaW1lKCkgKyBmaWxlRXh0ZW5zaW9uO1xuICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKGUgaW5zdGFuY2VvZiBUeXBlRXJyb3IpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGlnbm9yZTogc2V0dGluZyBnZXR0ZXItb25seSBwcm9wZXJ0eSBcIm5hbWVcIlxuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gcmUtcmFpc2UgZXJyb3JcbiAgICAgICAgICAgICAgICAgICAgICAgIHRocm93IGU7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvKipcbiAgICAgICAgICogSW52b2tlZCB3aGVuIHJlY29yZGluZyBpcyBzdG9wcGVkIGFuZCByZXN1bHRpbmcgc3RyZWFtIGlzIGF2YWlsYWJsZS5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHtibG9ifSBkYXRhIC0gUmVmZXJlbmNlIHRvIHRoZSByZWNvcmRlZCBCbG9iLlxuICAgICAgICAgKi9cblxuICAgIH0sIHtcbiAgICAgICAga2V5OiAnb25TdG9wUmVjb3JkaW5nJyxcbiAgICAgICAgdmFsdWU6IGZ1bmN0aW9uIG9uU3RvcFJlY29yZGluZyhkYXRhKSB7XG4gICAgICAgICAgICB0aGlzLnJlY29yZGVkRGF0YSA9IGRhdGE7XG5cbiAgICAgICAgICAgIC8vIGFkZCBmaWxlbmFtZSBhbmQgdGltZXN0YW1wIHRvIHJlY29yZGVkIGZpbGUgb2JqZWN0XG4gICAgICAgICAgICB0aGlzLmFkZEZpbGVJbmZvKHRoaXMucmVjb3JkZWREYXRhKTtcblxuICAgICAgICAgICAgLy8gcmVtb3ZlIHJlZmVyZW5jZSB0byByZWNvcmRlZCBzdHJlYW1cbiAgICAgICAgICAgIHRoaXMuZGlzcG9zZSgpO1xuXG4gICAgICAgICAgICAvLyBub3RpZnkgbGlzdGVuZXJzXG4gICAgICAgICAgICB0aGlzLnRyaWdnZXIoJ3JlY29yZENvbXBsZXRlJyk7XG4gICAgICAgIH1cblxuICAgICAgICAvKipcbiAgICAgICAgICogU2hvdyBzYXZlIGFzIGRpYWxvZyBpbiBicm93c2VyIHNvIHRoZSB1c2VyIGNhbiBzdG9yZSB0aGUgcmVjb3JkZWQgbWVkaWFcbiAgICAgICAgICogbG9jYWxseS5cbiAgICAgICAgICpcbiAgICAgICAgICogQHBhcmFtIHtvYmplY3R9IG5hbWUgLSBPYmplY3Qgd2l0aCBuYW1lcyBmb3IgdGhlIHBhcnRpY3VsYXIgYmxvYihzKVxuICAgICAgICAgKiAgICAgeW91IHdhbnQgdG8gc2F2ZS4gRmlsZSBleHRlbnNpb25zIGFyZSBhZGRlZCBhdXRvbWF0aWNhbGx5LiBGb3JcbiAgICAgICAgICogICAgIGV4YW1wbGU6IHsndmlkZW8nOiAnbmFtZS1vZi12aWRlby1maWxlJ30uIFN1cHBvcnRlZCBrZXlzIGFyZVxuICAgICAgICAgKiAgICAgJ2F1ZGlvJywgJ3ZpZGVvJyBhbmQgJ2dpZicuXG4gICAgICAgICAqL1xuXG4gICAgfSwge1xuICAgICAgICBrZXk6ICdzYXZlQXMnLFxuICAgICAgICB2YWx1ZTogZnVuY3Rpb24gc2F2ZUFzKG5hbWUpIHtcbiAgICAgICAgICAgIHZhciBmaWxlTmFtZSA9IG5hbWVbT2JqZWN0LmtleXMobmFtZSlbMF1dO1xuXG4gICAgICAgICAgICBpZiAodHlwZW9mIG5hdmlnYXRvci5tc1NhdmVPck9wZW5CbG9iICE9PSAndW5kZWZpbmVkJykge1xuICAgICAgICAgICAgICAgIHJldHVybiBuYXZpZ2F0b3IubXNTYXZlT3JPcGVuQmxvYih0aGlzLnJlY29yZGVkRGF0YSwgZmlsZU5hbWUpO1xuICAgICAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgbmF2aWdhdG9yLm1zU2F2ZUJsb2IgIT09ICd1bmRlZmluZWQnKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5hdmlnYXRvci5tc1NhdmVCbG9iKHRoaXMucmVjb3JkZWREYXRhLCBmaWxlTmFtZSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHZhciBoeXBlcmxpbmsgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdhJyk7XG4gICAgICAgICAgICBoeXBlcmxpbmsuaHJlZiA9IFVSTC5jcmVhdGVPYmplY3RVUkwodGhpcy5yZWNvcmRlZERhdGEpO1xuICAgICAgICAgICAgaHlwZXJsaW5rLmRvd25sb2FkID0gZmlsZU5hbWU7XG5cbiAgICAgICAgICAgIGh5cGVybGluay5zdHlsZSA9ICdkaXNwbGF5Om5vbmU7b3BhY2l0eTowO2NvbG9yOnRyYW5zcGFyZW50Oyc7XG4gICAgICAgICAgICAoZG9jdW1lbnQuYm9keSB8fCBkb2N1bWVudC5kb2N1bWVudEVsZW1lbnQpLmFwcGVuZENoaWxkKGh5cGVybGluayk7XG5cbiAgICAgICAgICAgIGlmICh0eXBlb2YgaHlwZXJsaW5rLmNsaWNrID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgICAgICAgICAgaHlwZXJsaW5rLmNsaWNrKCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGh5cGVybGluay50YXJnZXQgPSAnX2JsYW5rJztcbiAgICAgICAgICAgICAgICBoeXBlcmxpbmsuZGlzcGF0Y2hFdmVudChuZXcgTW91c2VFdmVudCgnY2xpY2snLCB7XG4gICAgICAgICAgICAgICAgICAgIHZpZXc6IHdpbmRvdyxcbiAgICAgICAgICAgICAgICAgICAgYnViYmxlczogdHJ1ZSxcbiAgICAgICAgICAgICAgICAgICAgY2FuY2VsYWJsZTogdHJ1ZVxuICAgICAgICAgICAgICAgIH0pKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgVVJMLnJldm9rZU9iamVjdFVSTChoeXBlcmxpbmsuaHJlZik7XG4gICAgICAgIH1cbiAgICB9XSk7XG5cbiAgICByZXR1cm4gUmVjb3JkRW5naW5lO1xufShDb21wb25lbnQpO1xuXG4vLyBleHBvc2UgY29tcG9uZW50IGZvciBleHRlcm5hbCBwbHVnaW5zXG5cblxudmlkZW9qcy5SZWNvcmRFbmdpbmUgPSBSZWNvcmRFbmdpbmU7XG5Db21wb25lbnQucmVnaXN0ZXJDb21wb25lbnQoJ1JlY29yZEVuZ2luZScsIFJlY29yZEVuZ2luZSk7XG5cbmV4cG9ydHMuUmVjb3JkRW5naW5lID0gUmVjb3JkRW5naW5lO1xuZXhwb3J0cy5SRUNPUkRSVEMgPSBSRUNPUkRSVEM7XG5leHBvcnRzLkxJQlZPUkJJU0pTID0gTElCVk9SQklTSlM7XG5leHBvcnRzLlJFQ09SREVSSlMgPSBSRUNPUkRFUkpTO1xuZXhwb3J0cy5MQU1FSlMgPSBMQU1FSlM7XG5leHBvcnRzLk9QVVNSRUNPUkRFUiA9IE9QVVNSRUNPUkRFUjsiLCIndXNlIHN0cmljdCc7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwge1xuICAgIHZhbHVlOiB0cnVlXG59KTtcbi8qKlxuICogQGZpbGUgcmVjb3JkLW1vZGUuanNcbiAqIEBzaW5jZSAyLjAuMFxuICovXG5cbi8vIHJlY29yZGVyIG1vZGVzXG52YXIgSU1BR0VfT05MWSA9ICdpbWFnZV9vbmx5JztcbnZhciBBVURJT19PTkxZID0gJ2F1ZGlvX29ubHknO1xudmFyIFZJREVPX09OTFkgPSAndmlkZW9fb25seSc7XG52YXIgQVVESU9fVklERU8gPSAnYXVkaW9fdmlkZW8nO1xudmFyIEFOSU1BVElPTiA9ICdhbmltYXRpb24nO1xuXG52YXIgZ2V0UmVjb3JkZXJNb2RlID0gZnVuY3Rpb24gZ2V0UmVjb3JkZXJNb2RlKGltYWdlLCBhdWRpbywgdmlkZW8sIGFuaW1hdGlvbikge1xuICAgIGlmIChpc01vZGVFbmFibGVkKGltYWdlKSkge1xuICAgICAgICByZXR1cm4gSU1BR0VfT05MWTtcbiAgICB9IGVsc2UgaWYgKGlzTW9kZUVuYWJsZWQoYW5pbWF0aW9uKSkge1xuICAgICAgICByZXR1cm4gQU5JTUFUSU9OO1xuICAgIH0gZWxzZSBpZiAoaXNNb2RlRW5hYmxlZChhdWRpbykgJiYgIWlzTW9kZUVuYWJsZWQodmlkZW8pKSB7XG4gICAgICAgIHJldHVybiBBVURJT19PTkxZO1xuICAgIH0gZWxzZSBpZiAoaXNNb2RlRW5hYmxlZChhdWRpbykgJiYgaXNNb2RlRW5hYmxlZCh2aWRlbykpIHtcbiAgICAgICAgcmV0dXJuIEFVRElPX1ZJREVPO1xuICAgIH0gZWxzZSBpZiAoIWlzTW9kZUVuYWJsZWQoYXVkaW8pICYmIGlzTW9kZUVuYWJsZWQodmlkZW8pKSB7XG4gICAgICAgIHJldHVybiBWSURFT19PTkxZO1xuICAgIH1cbn07XG5cbi8qKlxuICogUmV0dXJuIGJvb2xlYW4gaW5kaWNhdGluZyB3aGV0aGVyIG1vZGUgaXMgZW5hYmxlZCBvciBub3QuXG4gKi9cbnZhciBpc01vZGVFbmFibGVkID0gZnVuY3Rpb24gaXNNb2RlRW5hYmxlZChtb2RlKSB7XG4gICAgcmV0dXJuIG1vZGUgPT09IE9iamVjdChtb2RlKSB8fCBtb2RlID09PSB0cnVlO1xufTtcblxuZXhwb3J0cy5nZXRSZWNvcmRlck1vZGUgPSBnZXRSZWNvcmRlck1vZGU7XG5leHBvcnRzLklNQUdFX09OTFkgPSBJTUFHRV9PTkxZO1xuZXhwb3J0cy5BVURJT19PTkxZID0gQVVESU9fT05MWTtcbmV4cG9ydHMuVklERU9fT05MWSA9IFZJREVPX09OTFk7XG5leHBvcnRzLkFVRElPX1ZJREVPID0gQVVESU9fVklERU87XG5leHBvcnRzLkFOSU1BVElPTiA9IEFOSU1BVElPTjsiLCIndXNlIHN0cmljdCc7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwge1xuICAgIHZhbHVlOiB0cnVlXG59KTtcblxudmFyIF9jcmVhdGVDbGFzcyA9IGZ1bmN0aW9uICgpIHsgZnVuY3Rpb24gZGVmaW5lUHJvcGVydGllcyh0YXJnZXQsIHByb3BzKSB7IGZvciAodmFyIGkgPSAwOyBpIDwgcHJvcHMubGVuZ3RoOyBpKyspIHsgdmFyIGRlc2NyaXB0b3IgPSBwcm9wc1tpXTsgZGVzY3JpcHRvci5lbnVtZXJhYmxlID0gZGVzY3JpcHRvci5lbnVtZXJhYmxlIHx8IGZhbHNlOyBkZXNjcmlwdG9yLmNvbmZpZ3VyYWJsZSA9IHRydWU7IGlmIChcInZhbHVlXCIgaW4gZGVzY3JpcHRvcikgZGVzY3JpcHRvci53cml0YWJsZSA9IHRydWU7IE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGRlc2NyaXB0b3Iua2V5LCBkZXNjcmlwdG9yKTsgfSB9IHJldHVybiBmdW5jdGlvbiAoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7IGlmIChwcm90b1Byb3BzKSBkZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7IGlmIChzdGF0aWNQcm9wcykgZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvciwgc3RhdGljUHJvcHMpOyByZXR1cm4gQ29uc3RydWN0b3I7IH07IH0oKTtcblxudmFyIF9nZXQgPSBmdW5jdGlvbiBnZXQob2JqZWN0LCBwcm9wZXJ0eSwgcmVjZWl2ZXIpIHsgaWYgKG9iamVjdCA9PT0gbnVsbCkgb2JqZWN0ID0gRnVuY3Rpb24ucHJvdG90eXBlOyB2YXIgZGVzYyA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3Iob2JqZWN0LCBwcm9wZXJ0eSk7IGlmIChkZXNjID09PSB1bmRlZmluZWQpIHsgdmFyIHBhcmVudCA9IE9iamVjdC5nZXRQcm90b3R5cGVPZihvYmplY3QpOyBpZiAocGFyZW50ID09PSBudWxsKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gZWxzZSB7IHJldHVybiBnZXQocGFyZW50LCBwcm9wZXJ0eSwgcmVjZWl2ZXIpOyB9IH0gZWxzZSBpZiAoXCJ2YWx1ZVwiIGluIGRlc2MpIHsgcmV0dXJuIGRlc2MudmFsdWU7IH0gZWxzZSB7IHZhciBnZXR0ZXIgPSBkZXNjLmdldDsgaWYgKGdldHRlciA9PT0gdW5kZWZpbmVkKSB7IHJldHVybiB1bmRlZmluZWQ7IH0gcmV0dXJuIGdldHRlci5jYWxsKHJlY2VpdmVyKTsgfSB9O1xuXG52YXIgX3JlY29yZEVuZ2luZSA9IHJlcXVpcmUoJy4vcmVjb3JkLWVuZ2luZScpO1xuXG52YXIgX2RldGVjdEJyb3dzZXIgPSByZXF1aXJlKCcuLi91dGlscy9kZXRlY3QtYnJvd3NlcicpO1xuXG52YXIgX3JlY29yZE1vZGUgPSByZXF1aXJlKCcuL3JlY29yZC1tb2RlJyk7XG5cbmZ1bmN0aW9uIF9jbGFzc0NhbGxDaGVjayhpbnN0YW5jZSwgQ29uc3RydWN0b3IpIHsgaWYgKCEoaW5zdGFuY2UgaW5zdGFuY2VvZiBDb25zdHJ1Y3RvcikpIHsgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkNhbm5vdCBjYWxsIGEgY2xhc3MgYXMgYSBmdW5jdGlvblwiKTsgfSB9XG5cbmZ1bmN0aW9uIF9wb3NzaWJsZUNvbnN0cnVjdG9yUmV0dXJuKHNlbGYsIGNhbGwpIHsgaWYgKCFzZWxmKSB7IHRocm93IG5ldyBSZWZlcmVuY2VFcnJvcihcInRoaXMgaGFzbid0IGJlZW4gaW5pdGlhbGlzZWQgLSBzdXBlcigpIGhhc24ndCBiZWVuIGNhbGxlZFwiKTsgfSByZXR1cm4gY2FsbCAmJiAodHlwZW9mIGNhbGwgPT09IFwib2JqZWN0XCIgfHwgdHlwZW9mIGNhbGwgPT09IFwiZnVuY3Rpb25cIikgPyBjYWxsIDogc2VsZjsgfVxuXG5mdW5jdGlvbiBfaW5oZXJpdHMoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHsgaWYgKHR5cGVvZiBzdXBlckNsYXNzICE9PSBcImZ1bmN0aW9uXCIgJiYgc3VwZXJDbGFzcyAhPT0gbnVsbCkgeyB0aHJvdyBuZXcgVHlwZUVycm9yKFwiU3VwZXIgZXhwcmVzc2lvbiBtdXN0IGVpdGhlciBiZSBudWxsIG9yIGEgZnVuY3Rpb24sIG5vdCBcIiArIHR5cGVvZiBzdXBlckNsYXNzKTsgfSBzdWJDbGFzcy5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKHN1cGVyQ2xhc3MgJiYgc3VwZXJDbGFzcy5wcm90b3R5cGUsIHsgY29uc3RydWN0b3I6IHsgdmFsdWU6IHN1YkNsYXNzLCBlbnVtZXJhYmxlOiBmYWxzZSwgd3JpdGFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9IH0pOyBpZiAoc3VwZXJDbGFzcykgT2JqZWN0LnNldFByb3RvdHlwZU9mID8gT2JqZWN0LnNldFByb3RvdHlwZU9mKHN1YkNsYXNzLCBzdXBlckNsYXNzKSA6IHN1YkNsYXNzLl9fcHJvdG9fXyA9IHN1cGVyQ2xhc3M7IH0gLyoqXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBAZmlsZSByZWNvcmQtcnRjLmpzXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBAc2luY2UgMi4wLjBcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqL1xuXG52YXIgQ29tcG9uZW50ID0gdmlkZW9qcy5nZXRDb21wb25lbnQoJ0NvbXBvbmVudCcpO1xuXG4vKipcbiAqIEVuZ2luZSB1c2VkIHdpdGggdGhlIE1SZWNvcmRSVEMgY2xhc3MgaW4gdGhlIFJlY29yZFJUQyBsaWJyYXJ5LlxuICpcbiAqIEBjbGFzc1xuICogQGF1Z21lbnRzIHZpZGVvanMuUmVjb3JkRW5naW5lXG4gKi9cblxudmFyIFJlY29yZFJUQ0VuZ2luZSA9IGZ1bmN0aW9uIChfUmVjb3JkRW5naW5lKSB7XG4gICAgX2luaGVyaXRzKFJlY29yZFJUQ0VuZ2luZSwgX1JlY29yZEVuZ2luZSk7XG5cbiAgICBmdW5jdGlvbiBSZWNvcmRSVENFbmdpbmUoKSB7XG4gICAgICAgIF9jbGFzc0NhbGxDaGVjayh0aGlzLCBSZWNvcmRSVENFbmdpbmUpO1xuXG4gICAgICAgIHJldHVybiBfcG9zc2libGVDb25zdHJ1Y3RvclJldHVybih0aGlzLCAoUmVjb3JkUlRDRW5naW5lLl9fcHJvdG9fXyB8fCBPYmplY3QuZ2V0UHJvdG90eXBlT2YoUmVjb3JkUlRDRW5naW5lKSkuYXBwbHkodGhpcywgYXJndW1lbnRzKSk7XG4gICAgfVxuXG4gICAgX2NyZWF0ZUNsYXNzKFJlY29yZFJUQ0VuZ2luZSwgW3tcbiAgICAgICAga2V5OiAnc2V0dXAnLFxuXG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFNldHVwIHJlY29yZGluZyBlbmdpbmUuXG4gICAgICAgICAqL1xuICAgICAgICB2YWx1ZTogZnVuY3Rpb24gc2V0dXAoc3RyZWFtLCBtZWRpYVR5cGUsIGRlYnVnKSB7XG4gICAgICAgICAgICB0aGlzLmlucHV0U3RyZWFtID0gc3RyZWFtO1xuICAgICAgICAgICAgdGhpcy5tZWRpYVR5cGUgPSBtZWRpYVR5cGU7XG4gICAgICAgICAgICB0aGlzLmRlYnVnID0gZGVidWc7XG5cbiAgICAgICAgICAgIC8vIHNldHVwIFJlY29yZFJUQ1xuICAgICAgICAgICAgdGhpcy5lbmdpbmUgPSBuZXcgUmVjb3JkUlRDLk1SZWNvcmRSVEMoKTtcbiAgICAgICAgICAgIHRoaXMuZW5naW5lLm1lZGlhVHlwZSA9IHRoaXMubWVkaWFUeXBlO1xuICAgICAgICAgICAgdGhpcy5lbmdpbmUuZGlzYWJsZUxvZ3MgPSAhdGhpcy5kZWJ1ZztcbiAgICAgICAgICAgIHRoaXMuZW5naW5lLm1pbWVUeXBlID0gdGhpcy5taW1lVHlwZTtcblxuICAgICAgICAgICAgLy8gYXVkaW8gc2V0dGluZ3NcbiAgICAgICAgICAgIHRoaXMuZW5naW5lLmJ1ZmZlclNpemUgPSB0aGlzLmJ1ZmZlclNpemU7XG4gICAgICAgICAgICB0aGlzLmVuZ2luZS5zYW1wbGVSYXRlID0gdGhpcy5zYW1wbGVSYXRlO1xuICAgICAgICAgICAgdGhpcy5lbmdpbmUubnVtYmVyT2ZBdWRpb0NoYW5uZWxzID0gdGhpcy5hdWRpb0NoYW5uZWxzO1xuXG4gICAgICAgICAgICAvLyB2aWRlby9jYW52YXMgc2V0dGluZ3NcbiAgICAgICAgICAgIHRoaXMuZW5naW5lLnZpZGVvID0gdGhpcy52aWRlbztcbiAgICAgICAgICAgIHRoaXMuZW5naW5lLmNhbnZhcyA9IHRoaXMuY2FudmFzO1xuXG4gICAgICAgICAgICAvLyBhbmltYXRlZCBnaWYgc2V0dGluZ3NcbiAgICAgICAgICAgIHRoaXMuZW5naW5lLnF1YWxpdHkgPSB0aGlzLnF1YWxpdHk7XG4gICAgICAgICAgICB0aGlzLmVuZ2luZS5mcmFtZVJhdGUgPSB0aGlzLmZyYW1lUmF0ZTtcbiAgICAgICAgICAgIGlmICh0aGlzLm9uVGltZVN0YW1wICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmVuZ2luZS50aW1lU2xpY2UgPSB0aGlzLnRpbWVTbGljZTtcbiAgICAgICAgICAgICAgICB0aGlzLmVuZ2luZS5vblRpbWVTdGFtcCA9IHRoaXMub25UaW1lU3RhbXA7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIGNvbm5lY3Qgc3RyZWFtIHRvIHJlY29yZGluZyBlbmdpbmVcbiAgICAgICAgICAgIHRoaXMuZW5naW5lLmFkZFN0cmVhbSh0aGlzLmlucHV0U3RyZWFtKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBSZW1vdmUgYW55IHRlbXBvcmFyeSBkYXRhIGFuZCByZWZlcmVuY2VzIHRvIHN0cmVhbXMuXG4gICAgICAgICAqL1xuXG4gICAgfSwge1xuICAgICAgICBrZXk6ICdkaXNwb3NlJyxcbiAgICAgICAgdmFsdWU6IGZ1bmN0aW9uIGRpc3Bvc2UoKSB7XG4gICAgICAgICAgICBfZ2V0KFJlY29yZFJUQ0VuZ2luZS5wcm90b3R5cGUuX19wcm90b19fIHx8IE9iamVjdC5nZXRQcm90b3R5cGVPZihSZWNvcmRSVENFbmdpbmUucHJvdG90eXBlKSwgJ2Rpc3Bvc2UnLCB0aGlzKS5jYWxsKHRoaXMpO1xuXG4gICAgICAgICAgICBpZiAodHlwZW9mIHRoaXMuZW5naW5lLmRlc3Ryb3kgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgICAgICAgICB0aGlzLmVuZ2luZS5kZXN0cm95KCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvKipcbiAgICAgICAgICogU3RhcnQgcmVjb3JkaW5nLlxuICAgICAgICAgKi9cblxuICAgIH0sIHtcbiAgICAgICAga2V5OiAnc3RhcnQnLFxuICAgICAgICB2YWx1ZTogZnVuY3Rpb24gc3RhcnQoKSB7XG4gICAgICAgICAgICB0aGlzLmVuZ2luZS5zdGFydFJlY29yZGluZygpO1xuICAgICAgICB9XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqIFN0b3AgcmVjb3JkaW5nLiBSZXN1bHQgd2lsbCBiZSBhdmFpbGFibGUgYXN5bmMgd2hlbiBvblN0b3BSZWNvcmRpbmdcbiAgICAgICAgICogaXMgY2FsbGVkLlxuICAgICAgICAgKi9cblxuICAgIH0sIHtcbiAgICAgICAga2V5OiAnc3RvcCcsXG4gICAgICAgIHZhbHVlOiBmdW5jdGlvbiBzdG9wKCkge1xuICAgICAgICAgICAgdGhpcy5lbmdpbmUuc3RvcFJlY29yZGluZyh0aGlzLm9uU3RvcFJlY29yZGluZy5iaW5kKHRoaXMpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBQYXVzZSByZWNvcmRpbmcuXG4gICAgICAgICAqL1xuXG4gICAgfSwge1xuICAgICAgICBrZXk6ICdwYXVzZScsXG4gICAgICAgIHZhbHVlOiBmdW5jdGlvbiBwYXVzZSgpIHtcbiAgICAgICAgICAgIHRoaXMuZW5naW5lLnBhdXNlUmVjb3JkaW5nKCk7XG4gICAgICAgIH1cblxuICAgICAgICAvKipcbiAgICAgICAgICogUmVzdW1lIHJlY29yZGluZy5cbiAgICAgICAgICovXG5cbiAgICB9LCB7XG4gICAgICAgIGtleTogJ3Jlc3VtZScsXG4gICAgICAgIHZhbHVlOiBmdW5jdGlvbiByZXN1bWUoKSB7XG4gICAgICAgICAgICB0aGlzLmVuZ2luZS5yZXN1bWVSZWNvcmRpbmcoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBTaG93IHNhdmUgYXMgZGlhbG9nIGluIGJyb3dzZXIgc28gdGhlIHVzZXIgY2FuIHN0b3JlIHRoZSByZWNvcmRlZCBtZWRpYVxuICAgICAgICAgKiBsb2NhbGx5LlxuICAgICAgICAgKlxuICAgICAgICAgKiBAcGFyYW0ge29iamVjdH0gbmFtZSAtIE9iamVjdCB3aXRoIG5hbWVzIGZvciB0aGUgcGFydGljdWxhciBibG9iKHMpXG4gICAgICAgICAqICAgICB5b3Ugd2FudCB0byBzYXZlLiBGaWxlIGV4dGVuc2lvbnMgYXJlIGFkZGVkIGF1dG9tYXRpY2FsbHkuIEZvclxuICAgICAgICAgKiAgICAgZXhhbXBsZTogeyd2aWRlbyc6ICduYW1lLW9mLXZpZGVvLWZpbGUnfS4gU3VwcG9ydGVkIGtleXMgYXJlXG4gICAgICAgICAqICAgICAnYXVkaW8nLCAndmlkZW8nIGFuZCAnZ2lmJy5cbiAgICAgICAgICovXG5cbiAgICB9LCB7XG4gICAgICAgIGtleTogJ3NhdmVBcycsXG4gICAgICAgIHZhbHVlOiBmdW5jdGlvbiBzYXZlQXMobmFtZSkge1xuICAgICAgICAgICAgaWYgKHRoaXMuZW5naW5lICYmIG5hbWUgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHRoaXMuZW5naW5lLnNhdmUobmFtZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvKipcbiAgICAgICAgICogSW52b2tlZCB3aGVuIHJlY29yZGluZyBpcyBzdG9wcGVkIGFuZCByZXN1bHRpbmcgc3RyZWFtIGlzIGF2YWlsYWJsZS5cbiAgICAgICAgICpcbiAgICAgICAgICogQHByaXZhdGVcbiAgICAgICAgICogQHBhcmFtIHtzdHJpbmd9IGF1ZGlvVmlkZW9VUkwgLSBSZWZlcmVuY2UgdG8gdGhlIHJlY29yZGVkIEJsb2JcbiAgICAgICAgICogICAgIG9iamVjdCwgZS5nLiAnYmxvYjpodHRwOi8vbG9jYWxob3N0OjgwODAvMTAxMDAwMTYtNDI0OC05OTQ5LWIwZDYtMGJiNDBkYjU2ZWJhJ1xuICAgICAgICAgKiBAcGFyYW0ge3N0cmluZ30gdHlwZSAtIE1lZGlhIHR5cGUsIGVnLiAndmlkZW8nIG9yICdhdWRpbycuXG4gICAgICAgICAqL1xuXG4gICAgfSwge1xuICAgICAgICBrZXk6ICdvblN0b3BSZWNvcmRpbmcnLFxuICAgICAgICB2YWx1ZTogZnVuY3Rpb24gb25TdG9wUmVjb3JkaW5nKGF1ZGlvVmlkZW9VUkwsIHR5cGUpIHtcbiAgICAgICAgICAgIHZhciBfdGhpczIgPSB0aGlzO1xuXG4gICAgICAgICAgICAvLyBzdG9yZSByZWZlcmVuY2UgdG8gcmVjb3JkZWQgc3RyZWFtIFVSTFxuICAgICAgICAgICAgdGhpcy5tZWRpYVVSTCA9IGF1ZGlvVmlkZW9VUkw7XG5cbiAgICAgICAgICAgIC8vIHN0b3JlIHJlZmVyZW5jZSB0byByZWNvcmRlZCBzdHJlYW0gZGF0YVxuICAgICAgICAgICAgdmFyIHJlY29yZFR5cGUgPSB0aGlzLnBsYXllcigpLnJlY29yZCgpLmdldFJlY29yZFR5cGUoKTtcbiAgICAgICAgICAgIHRoaXMuZW5naW5lLmdldEJsb2IoZnVuY3Rpb24gKHJlY29yZGluZykge1xuICAgICAgICAgICAgICAgIHN3aXRjaCAocmVjb3JkVHlwZSkge1xuICAgICAgICAgICAgICAgICAgICBjYXNlIF9yZWNvcmRNb2RlLkFVRElPX09OTFk6XG4gICAgICAgICAgICAgICAgICAgICAgICBfdGhpczIucmVjb3JkZWREYXRhID0gcmVjb3JkaW5nLmF1ZGlvO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBfdGhpczIuYWRkRmlsZUluZm8oX3RoaXMyLnJlY29yZGVkRGF0YSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIG5vdGlmeSBsaXN0ZW5lcnNcbiAgICAgICAgICAgICAgICAgICAgICAgIF90aGlzMi50cmlnZ2VyKCdyZWNvcmRDb21wbGV0ZScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICAgICAgICAgICAgY2FzZSBfcmVjb3JkTW9kZS5WSURFT19PTkxZOlxuICAgICAgICAgICAgICAgICAgICBjYXNlIF9yZWNvcmRNb2RlLkFVRElPX1ZJREVPOlxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gd2hlbiByZWNvcmRpbmcgYm90aCBhdWRpbyBhbmQgdmlkZW8sIHJlY29yZHJ0Y1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gY2FsbHMgdGhpcyB0d2ljZSBvbiBjaHJvbWUsIGZpcnN0IHdpdGggYXVkaW8gZGF0YVxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gYW5kIHRoZW4gd2l0aCB2aWRlbyBkYXRhLlxuICAgICAgICAgICAgICAgICAgICAgICAgLy8gb24gZmlyZWZveCBpdCdzIGNhbGxlZCBvbmNlIGJ1dCB3aXRoIGEgc2luZ2xlXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBibG9iIHRoYXQgaW5jbHVkZXMgYm90aCBhdWRpbyBhbmQgdmlkZW8gZGF0YS5cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChyZWNvcmRpbmcudmlkZW8gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGRhdGEgaXMgdmlkZW8tb25seSBidXQgb24gZmlyZWZveCBhdWRpbyt2aWRlb1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIF90aGlzMi5yZWNvcmRlZERhdGEgPSByZWNvcmRpbmcudmlkZW87XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBvbiB0aGUgY2hyb21lIGJyb3dzZXIgdHdvIGJsb2JzIGFyZSBjcmVhdGVkXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gY29udGFpbmluZyB0aGUgc2VwYXJhdGUgYXVkaW8vdmlkZW8gc3RyZWFtcy5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocmVjb3JkVHlwZSA9PT0gX3JlY29yZE1vZGUuQVVESU9fVklERU8gJiYgKDAsIF9kZXRlY3RCcm93c2VyLmlzQ2hyb21lKSgpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHN0b3JlIGJvdGggYXVkaW8gYW5kIHZpZGVvXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIF90aGlzMi5yZWNvcmRlZERhdGEgPSByZWNvcmRpbmc7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yICh2YXIgbXR5cGUgaW4gX3RoaXMyLnJlY29yZGVkRGF0YSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgX3RoaXMyLmFkZEZpbGVJbmZvKF90aGlzMi5yZWNvcmRlZERhdGFbbXR5cGVdKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIF90aGlzMi5hZGRGaWxlSW5mbyhfdGhpczIucmVjb3JkZWREYXRhKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBub3RpZnkgbGlzdGVuZXJzXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgX3RoaXMyLnRyaWdnZXIoJ3JlY29yZENvbXBsZXRlJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgICAgICAgICAgICBjYXNlIF9yZWNvcmRNb2RlLkFOSU1BVElPTjpcbiAgICAgICAgICAgICAgICAgICAgICAgIF90aGlzMi5yZWNvcmRlZERhdGEgPSByZWNvcmRpbmcuZ2lmO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICBfdGhpczIuYWRkRmlsZUluZm8oX3RoaXMyLnJlY29yZGVkRGF0YSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIG5vdGlmeSBsaXN0ZW5lcnNcbiAgICAgICAgICAgICAgICAgICAgICAgIF90aGlzMi50cmlnZ2VyKCdyZWNvcmRDb21wbGV0ZScpO1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9XSk7XG5cbiAgICByZXR1cm4gUmVjb3JkUlRDRW5naW5lO1xufShfcmVjb3JkRW5naW5lLlJlY29yZEVuZ2luZSk7XG5cbi8vIGV4cG9zZSBwbHVnaW5cblxuXG52aWRlb2pzLlJlY29yZFJUQ0VuZ2luZSA9IFJlY29yZFJUQ0VuZ2luZTtcblxuQ29tcG9uZW50LnJlZ2lzdGVyQ29tcG9uZW50KCdSZWNvcmRSVENFbmdpbmUnLCBSZWNvcmRSVENFbmdpbmUpO1xuXG5leHBvcnRzLmRlZmF1bHQgPSBSZWNvcmRSVENFbmdpbmU7IiwiJ3VzZSBzdHJpY3QnO1xuXG5PYmplY3QuZGVmaW5lUHJvcGVydHkoZXhwb3J0cywgXCJfX2VzTW9kdWxlXCIsIHtcbiAgICB2YWx1ZTogdHJ1ZVxufSk7XG4vKipcbiAqIEBmaWxlIGJyb3dzZXItc2hpbS5qc1xuICogQHNpbmNlIDIuMC4wXG4gKi9cblxudmFyIHNldFNyY09iamVjdCA9IGZ1bmN0aW9uIHNldFNyY09iamVjdChzdHJlYW0sIGVsZW1lbnQsIGlnbm9yZUNyZWF0ZU9iamVjdFVSTCkge1xuICAgIGlmICgnY3JlYXRlT2JqZWN0VVJMJyBpbiBVUkwgJiYgIWlnbm9yZUNyZWF0ZU9iamVjdFVSTCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgZWxlbWVudC5zcmMgPSBVUkwuY3JlYXRlT2JqZWN0VVJMKHN0cmVhbSk7XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHNldFNyY09iamVjdChzdHJlYW0sIGVsZW1lbnQsIHRydWUpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgfSBlbHNlIGlmICgnc3JjT2JqZWN0JyBpbiBlbGVtZW50KSB7XG4gICAgICAgIGVsZW1lbnQuc3JjT2JqZWN0ID0gc3RyZWFtO1xuICAgIH0gZWxzZSBpZiAoJ21velNyY09iamVjdCcgaW4gZWxlbWVudCkge1xuICAgICAgICBlbGVtZW50Lm1velNyY09iamVjdCA9IHN0cmVhbTtcbiAgICB9IGVsc2Uge1xuICAgICAgICBjb25zb2xlLmxvZygnY3JlYXRlT2JqZWN0VVJML3NyY09iamVjdCBib3RoIGFyZSBub3Qgc3VwcG9ydGVkLicpO1xuICAgIH1cbn07XG5cbmV4cG9ydHMuZGVmYXVsdCA9IHNldFNyY09iamVjdDsiLCIndXNlIHN0cmljdCc7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwge1xuICAgIHZhbHVlOiB0cnVlXG59KTtcbmV4cG9ydHMuaXNTYWZhcmkgPSBleHBvcnRzLmlzQ2hyb21lID0gZXhwb3J0cy5pc09wZXJhID0gZXhwb3J0cy5pc0VkZ2UgPSBleHBvcnRzLmRldGVjdEJyb3dzZXIgPSB1bmRlZmluZWQ7XG5cbnZhciBfd2luZG93ID0gcmVxdWlyZSgnZ2xvYmFsL3dpbmRvdycpO1xuXG52YXIgX3dpbmRvdzIgPSBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KF93aW5kb3cpO1xuXG5mdW5jdGlvbiBfaW50ZXJvcFJlcXVpcmVEZWZhdWx0KG9iaikgeyByZXR1cm4gb2JqICYmIG9iai5fX2VzTW9kdWxlID8gb2JqIDogeyBkZWZhdWx0OiBvYmogfTsgfVxuXG4vKipcbiAqIEJyb3dzZXIgZGV0ZWN0b3IuXG4gKlxuICogQHByaXZhdGVcbiAqIEByZXR1cm4ge29iamVjdH0gcmVzdWx0IGNvbnRhaW5pbmcgYnJvd3NlciwgdmVyc2lvbiBhbmQgbWluVmVyc2lvblxuICogICAgIHByb3BlcnRpZXMuXG4gKi9cbnZhciBkZXRlY3RCcm93c2VyID0gZnVuY3Rpb24gZGV0ZWN0QnJvd3NlcigpIHtcbiAgICAvLyByZXR1cm5lZCByZXN1bHQgb2JqZWN0XG4gICAgdmFyIHJlc3VsdCA9IHt9O1xuICAgIHJlc3VsdC5icm93c2VyID0gbnVsbDtcbiAgICByZXN1bHQudmVyc2lvbiA9IG51bGw7XG4gICAgcmVzdWx0Lm1pblZlcnNpb24gPSBudWxsO1xuXG4gICAgLy8gZmFpbCBlYXJseSBpZiBpdCdzIG5vdCBhIGJyb3dzZXJcbiAgICBpZiAodHlwZW9mIF93aW5kb3cyLmRlZmF1bHQgPT09ICd1bmRlZmluZWQnIHx8ICFfd2luZG93Mi5kZWZhdWx0Lm5hdmlnYXRvcikge1xuICAgICAgICByZXN1bHQuYnJvd3NlciA9ICdOb3QgYSBzdXBwb3J0ZWQgYnJvd3Nlci4nO1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cblxuICAgIC8vIEZpcmVmb3hcbiAgICBpZiAobmF2aWdhdG9yLm1vekdldFVzZXJNZWRpYSkge1xuICAgICAgICByZXN1bHQuYnJvd3NlciA9ICdmaXJlZm94JztcbiAgICAgICAgcmVzdWx0LnZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihuYXZpZ2F0b3IudXNlckFnZW50LCAvRmlyZWZveFxcLyhcXGQrKVxcLi8sIDEpO1xuICAgICAgICByZXN1bHQubWluVmVyc2lvbiA9IDMxO1xuICAgIH0gZWxzZSBpZiAobmF2aWdhdG9yLndlYmtpdEdldFVzZXJNZWRpYSkge1xuICAgICAgICAvLyBDaHJvbWUsIENocm9taXVtLCBXZWJ2aWV3LCBPcGVyYVxuICAgICAgICBpZiAoX3dpbmRvdzIuZGVmYXVsdC53ZWJraXRSVENQZWVyQ29ubmVjdGlvbikge1xuICAgICAgICAgICAgcmVzdWx0LmJyb3dzZXIgPSAnY2hyb21lJztcbiAgICAgICAgICAgIHJlc3VsdC52ZXJzaW9uID0gZXh0cmFjdFZlcnNpb24obmF2aWdhdG9yLnVzZXJBZ2VudCwgL0Nocm9tKGV8aXVtKVxcLyhcXGQrKVxcLi8sIDIpO1xuICAgICAgICAgICAgcmVzdWx0Lm1pblZlcnNpb24gPSAzODtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIFNhZmFyaSAoaW4gYW4gdW5wdWJsaXNoZWQgdmVyc2lvbikgb3IgdW5rbm93biB3ZWJraXQtYmFzZWQuXG4gICAgICAgICAgICBpZiAobmF2aWdhdG9yLnVzZXJBZ2VudC5tYXRjaCgvVmVyc2lvblxcLyhcXGQrKS4oXFxkKykvKSkge1xuICAgICAgICAgICAgICAgIHJlc3VsdC5icm93c2VyID0gJ3NhZmFyaSc7XG4gICAgICAgICAgICAgICAgcmVzdWx0LnZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihuYXZpZ2F0b3IudXNlckFnZW50LCAvQXBwbGVXZWJLaXRcXC8oXFxkKylcXC4vLCAxKTtcbiAgICAgICAgICAgICAgICByZXN1bHQubWluVmVyc2lvbiA9IDExO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAvLyB1bmtub3duIHdlYmtpdC1iYXNlZCBicm93c2VyLlxuICAgICAgICAgICAgICAgIHJlc3VsdC5icm93c2VyID0gJ1Vuc3VwcG9ydGVkIHdlYmtpdC1iYXNlZCBicm93c2VyICcgKyAnd2l0aCBHVU0gc3VwcG9ydCBidXQgbm8gV2ViUlRDIHN1cHBvcnQuJztcbiAgICAgICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIC8vIEVkZ2VcbiAgICB9IGVsc2UgaWYgKG5hdmlnYXRvci5tZWRpYURldmljZXMgJiYgbmF2aWdhdG9yLnVzZXJBZ2VudC5tYXRjaCgvRWRnZVxcLyhcXGQrKS4oXFxkKykkLykpIHtcbiAgICAgICAgcmVzdWx0LmJyb3dzZXIgPSAnZWRnZSc7XG4gICAgICAgIHJlc3VsdC52ZXJzaW9uID0gZXh0cmFjdFZlcnNpb24obmF2aWdhdG9yLnVzZXJBZ2VudCwgL0VkZ2VcXC8oXFxkKykuKFxcZCspJC8sIDIpO1xuICAgICAgICByZXN1bHQubWluVmVyc2lvbiA9IDEwNTQ3O1xuICAgIH0gZWxzZSBpZiAobmF2aWdhdG9yLm1lZGlhRGV2aWNlcyAmJiBuYXZpZ2F0b3IudXNlckFnZW50Lm1hdGNoKC9BcHBsZVdlYktpdFxcLyhcXGQrKVxcLi8pKSB7XG4gICAgICAgIC8vIFNhZmFyaSwgd2l0aCB3ZWJraXRHZXRVc2VyTWVkaWEgcmVtb3ZlZC5cbiAgICAgICAgcmVzdWx0LmJyb3dzZXIgPSAnc2FmYXJpJztcbiAgICAgICAgcmVzdWx0LnZlcnNpb24gPSBleHRyYWN0VmVyc2lvbihuYXZpZ2F0b3IudXNlckFnZW50LCAvQXBwbGVXZWJLaXRcXC8oXFxkKylcXC4vLCAxKTtcbiAgICB9IGVsc2Uge1xuICAgICAgICAvLyBkZWZhdWx0IGZhbGx0aHJvdWdoOiBub3Qgc3VwcG9ydGVkLlxuICAgICAgICByZXN1bHQuYnJvd3NlciA9ICdOb3QgYSBzdXBwb3J0ZWQgYnJvd3Nlci4nO1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cblxuICAgIHJldHVybiByZXN1bHQ7XG59O1xuXG4vKipcbiAqIEV4dHJhY3QgYnJvd3NlciB2ZXJzaW9uIG91dCBvZiB0aGUgcHJvdmlkZWQgdXNlciBhZ2VudCBzdHJpbmcuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7IXN0cmluZ30gdWFzdHJpbmcgLSB1c2VyQWdlbnQgc3RyaW5nLlxuICogQHBhcmFtIHshc3RyaW5nfSBleHByIC0gUmVndWxhciBleHByZXNzaW9uIHVzZWQgYXMgbWF0Y2ggY3JpdGVyaWEuXG4gKiBAcGFyYW0geyFudW1iZXJ9IHBvcyAtIHBvc2l0aW9uIGluIHRoZSB2ZXJzaW9uIHN0cmluZyB0byBiZVxuICogICAgIHJldHVybmVkLlxuICogQHJldHVybiB7IW51bWJlcn0gYnJvd3NlciB2ZXJzaW9uLlxuICovXG4vKipcbiAqIEBmaWxlIGRldGVjdC1icm93c2VyLmpzXG4gKiBAc2luY2UgMi4wLjBcbiAqL1xuXG52YXIgZXh0cmFjdFZlcnNpb24gPSBmdW5jdGlvbiBleHRyYWN0VmVyc2lvbih1YXN0cmluZywgZXhwciwgcG9zKSB7XG4gICAgdmFyIG1hdGNoID0gdWFzdHJpbmcubWF0Y2goZXhwcik7XG4gICAgcmV0dXJuIG1hdGNoICYmIG1hdGNoLmxlbmd0aCA+PSBwb3MgJiYgcGFyc2VJbnQobWF0Y2hbcG9zXSwgMTApO1xufTtcblxudmFyIGlzRWRnZSA9IGZ1bmN0aW9uIGlzRWRnZSgpIHtcbiAgICByZXR1cm4gZGV0ZWN0QnJvd3NlcigpLmJyb3dzZXIgPT09ICdlZGdlJztcbn07XG5cbnZhciBpc1NhZmFyaSA9IGZ1bmN0aW9uIGlzU2FmYXJpKCkge1xuICAgIHJldHVybiBkZXRlY3RCcm93c2VyKCkuYnJvd3NlciA9PT0gJ3NhZmFyaSc7XG59O1xuXG52YXIgaXNPcGVyYSA9IGZ1bmN0aW9uIGlzT3BlcmEoKSB7XG4gICAgcmV0dXJuICEhX3dpbmRvdzIuZGVmYXVsdC5vcGVyYSB8fCBuYXZpZ2F0b3IudXNlckFnZW50LmluZGV4T2YoJ09QUi8nKSAhPT0gLTE7XG59O1xuXG52YXIgaXNDaHJvbWUgPSBmdW5jdGlvbiBpc0Nocm9tZSgpIHtcbiAgICByZXR1cm4gZGV0ZWN0QnJvd3NlcigpLmJyb3dzZXIgPT09ICdjaHJvbWUnO1xufTtcblxuZXhwb3J0cy5kZXRlY3RCcm93c2VyID0gZGV0ZWN0QnJvd3NlcjtcbmV4cG9ydHMuaXNFZGdlID0gaXNFZGdlO1xuZXhwb3J0cy5pc09wZXJhID0gaXNPcGVyYTtcbmV4cG9ydHMuaXNDaHJvbWUgPSBpc0Nocm9tZTtcbmV4cG9ydHMuaXNTYWZhcmkgPSBpc1NhZmFyaTsiLCIndXNlIHN0cmljdCc7XG5cbk9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBcIl9fZXNNb2R1bGVcIiwge1xuICAgIHZhbHVlOiB0cnVlXG59KTtcbi8qKlxuICogQGZpbGUgZm9ybWF0LXRpbWUuanNcbiAqIEBzaW5jZSAyLjAuMFxuICovXG5cbi8qKlxuICogRm9ybWF0IHNlY29uZHMgYXMgYSB0aW1lIHN0cmluZywgSDpNTTpTUywgTTpTUyBvciBNOlNTOk1NTS5cbiAqXG4gKiBTdXBwbHlpbmcgYSBndWlkZSAoaW4gc2Vjb25kcykgd2lsbCBmb3JjZSBhIG51bWJlciBvZiBsZWFkaW5nIHplcm9zXG4gKiB0byBjb3ZlciB0aGUgbGVuZ3RoIG9mIHRoZSBndWlkZS5cbiAqXG4gKiBAcGFyYW0ge251bWJlcn0gc2Vjb25kcyAtIE51bWJlciBvZiBzZWNvbmRzIHRvIGJlIHR1cm5lZCBpbnRvIGFcbiAqICAgICBzdHJpbmcuXG4gKiBAcGFyYW0ge251bWJlcn0gZ3VpZGUgLSBOdW1iZXIgKGluIHNlY29uZHMpIHRvIG1vZGVsIHRoZSBzdHJpbmdcbiAqICAgICBhZnRlci5cbiAqIEBwYXJhbSB7bnVtYmVyfSBtc0Rpc3BsYXlNYXggLSBOdW1iZXIgKGluIG1pbGxpc2Vjb25kcykgdG8gbW9kZWwgdGhlIHN0cmluZ1xuICogICAgIGFmdGVyLlxuICogQHJldHVybiB7c3RyaW5nfSBUaW1lIGZvcm1hdHRlZCBhcyBIOk1NOlNTLCBNOlNTIG9yIE06U1M6TU1NLCBlLmcuXG4gKiAgICAgMDowMDoxMi5cbiAqIEBwcml2YXRlXG4gKi9cbnZhciBmb3JtYXRUaW1lID0gZnVuY3Rpb24gZm9ybWF0VGltZShzZWNvbmRzLCBndWlkZSwgbXNEaXNwbGF5TWF4KSB7XG4gICAgLy8gRGVmYXVsdCB0byB1c2luZyBzZWNvbmRzIGFzIGd1aWRlXG4gICAgc2Vjb25kcyA9IHNlY29uZHMgPCAwID8gMCA6IHNlY29uZHM7XG4gICAgZ3VpZGUgPSBndWlkZSB8fCBzZWNvbmRzO1xuICAgIHZhciBzID0gTWF0aC5mbG9vcihzZWNvbmRzICUgNjApLFxuICAgICAgICBtID0gTWF0aC5mbG9vcihzZWNvbmRzIC8gNjAgJSA2MCksXG4gICAgICAgIGggPSBNYXRoLmZsb29yKHNlY29uZHMgLyAzNjAwKSxcbiAgICAgICAgZ20gPSBNYXRoLmZsb29yKGd1aWRlIC8gNjAgJSA2MCksXG4gICAgICAgIGdoID0gTWF0aC5mbG9vcihndWlkZSAvIDM2MDApLFxuICAgICAgICBtcyA9IE1hdGguZmxvb3IoKHNlY29uZHMgLSBzKSAqIDEwMDApO1xuXG4gICAgLy8gaGFuZGxlIGludmFsaWQgdGltZXNcbiAgICBpZiAoaXNOYU4oc2Vjb25kcykgfHwgc2Vjb25kcyA9PT0gSW5maW5pdHkpIHtcbiAgICAgICAgLy8gJy0nIGlzIGZhbHNlIGZvciBhbGwgcmVsYXRpb25hbCBvcGVyYXRvcnMgKGUuZy4gPCwgPj0pIHNvIHRoaXNcbiAgICAgICAgLy8gc2V0dGluZyB3aWxsIGFkZCB0aGUgbWluaW11bSBudW1iZXIgb2YgZmllbGRzIHNwZWNpZmllZCBieSB0aGVcbiAgICAgICAgLy8gZ3VpZGVcbiAgICAgICAgaCA9IG0gPSBzID0gbXMgPSAnLSc7XG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgaWYgd2UgbmVlZCB0byBzaG93IG1pbGxpc2Vjb25kc1xuICAgIGlmIChndWlkZSA+IDAgJiYgZ3VpZGUgPCBtc0Rpc3BsYXlNYXgpIHtcbiAgICAgICAgaWYgKG1zIDwgMTAwKSB7XG4gICAgICAgICAgICBpZiAobXMgPCAxMCkge1xuICAgICAgICAgICAgICAgIG1zID0gJzAwJyArIG1zO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBtcyA9ICcwJyArIG1zO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIG1zID0gJzonICsgbXM7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgbXMgPSAnJztcbiAgICB9XG5cbiAgICAvLyBDaGVjayBpZiB3ZSBuZWVkIHRvIHNob3cgaG91cnNcbiAgICBoID0gaCA+IDAgfHwgZ2ggPiAwID8gaCArICc6JyA6ICcnO1xuXG4gICAgLy8gSWYgaG91cnMgYXJlIHNob3dpbmcsIHdlIG1heSBuZWVkIHRvIGFkZCBhIGxlYWRpbmcgemVyby5cbiAgICAvLyBBbHdheXMgc2hvdyBhdCBsZWFzdCBvbmUgZGlnaXQgb2YgbWludXRlcy5cbiAgICBtID0gKChoIHx8IGdtID49IDEwKSAmJiBtIDwgMTAgPyAnMCcgKyBtIDogbSkgKyAnOic7XG5cbiAgICAvLyBDaGVjayBpZiBsZWFkaW5nIHplcm8gaXMgbmVlZCBmb3Igc2Vjb25kc1xuICAgIHMgPSBzIDwgMTAgPyAnMCcgKyBzIDogcztcblxuICAgIHJldHVybiBoICsgbSArIHMgKyBtcztcbn07XG5cbmV4cG9ydHMuZGVmYXVsdCA9IGZvcm1hdFRpbWU7IiwidmFyIHdpbjtcblxuaWYgKHR5cGVvZiB3aW5kb3cgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICB3aW4gPSB3aW5kb3c7XG59IGVsc2UgaWYgKHR5cGVvZiBnbG9iYWwgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICB3aW4gPSBnbG9iYWw7XG59IGVsc2UgaWYgKHR5cGVvZiBzZWxmICE9PSBcInVuZGVmaW5lZFwiKXtcbiAgICB3aW4gPSBzZWxmO1xufSBlbHNlIHtcbiAgICB3aW4gPSB7fTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB3aW47XG4iXX0=