tr-email-templates
Version:
Create, preview, and send custom email templates for Node.js. Highly configurable and supports automatic inline CSS, stylesheets, embedded images and fonts, and much more! Made for sending beautiful emails with Lad.
382 lines (319 loc) • 55.7 kB
JavaScript
"use strict";
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const fs = require('fs');
const path = require('path');
const I18N = require('@ladjs/i18n');
const _ = require('lodash');
const consolidate = require('consolidate');
const debug = require('debug')('email-templates');
const getPaths = require('get-paths');
const htmlToText = require('html-to-text');
const is = require('@sindresorhus/is');
const juice = require('juice');
const nodemailer = require('nodemailer');
const pify = require('pify');
const previewEmail = require('preview-email'); // promise version of `juice.juiceResources`
const juiceResources = (html, options) => {
return new Promise((resolve, reject) => {
juice.juiceResources(html, options, (err, html) => {
if (err) return reject(err);
resolve(html);
});
});
};
const env = (process.env.NODE_ENV || 'development').toLowerCase();
const stat = pify(fs.stat);
const readFile = pify(fs.readFile);
class Email {
constructor(config = {}) {
debug('config passed %O', config);
console.log('config passed', config); // 2.x backwards compatible support
if (config.juiceOptions) {
config.juiceResources = config.juiceOptions;
delete config.juiceOptions;
}
debug('config.juiceResources %O', config.juiceResources);
console.log('config.juiceResources', config.juiceResources);
if (config.disableJuice) {
config.juice = false;
delete config.disableJuice;
}
debug('config.juice %O', config.juice);
console.log('config.juice', config.juice);
if (config.render) {
config.customRender = true;
}
debug('config.customRender %O', config.customRender);
console.log('config.customRender', config.customRender);
this.config = _.merge({
views: {
// directory where email templates reside
root: path.resolve('emails'),
options: {
// default file extension for template
extension: 'pug',
map: {
hbs: 'handlebars',
njk: 'nunjucks'
},
engineSource: consolidate
},
// locals to pass to templates for rendering
locals: {
// turn on caching for non-development environments
cache: !['development', 'test'].includes(env),
// pretty is automatically set to `false` for subject/text
pretty: true
}
},
// <https://nodemailer.com/message/>
message: {},
send: !['development', 'test'].includes(env),
preview: env === 'development',
// <https://github.com/ladjs/i18n>
// set to an object to configure and enable it
i18n: false,
// pass a custom render function if necessary
render: this.render.bind(this),
customRender: false,
// force text-only rendering of template (disregards template folder)
textOnly: false,
// <https://github.com/werk85/node-html-to-text>
htmlToText: {
ignoreImage: true
},
subjectPrefix: false,
// <https://github.com/Automattic/juice>
juice: true,
juiceResources: {
preserveImportant: true,
webResources: {
relativeTo: path.resolve('build'),
images: false
}
},
// pass a transport configuration object or a transport instance
// (e.g. an instance is created via `nodemailer.createTransport`)
// <https://nodemailer.com/transports/>
transport: {},
// last locale field name (also used by @ladjs/i18n)
lastLocaleField: 'last_locale',
getPath(type, template) {
return path.join(template, type);
}
}, config);
debug('merged config %O', this.config);
console.log('merged config', this.config); // override existing method
this.render = this.config.render;
debug('config.transport %O', this.config.transport);
console.log('config.transport', this.config.transport);
if (!_.isFunction(this.config.transport.sendMail)) this.config.transport = nodemailer.createTransport(this.config.transport);
debug('transport created');
console.log('transport created');
debug('transformed config %O', this.config);
console.log('transformed config', this.config);
this.juiceResources = this.juiceResources.bind(this);
this.getTemplatePath = this.getTemplatePath.bind(this);
this.templateExists = this.templateExists.bind(this);
this.checkAndRender = this.checkAndRender.bind(this);
this.render = this.render.bind(this);
this.renderAll = this.renderAll.bind(this);
this.send = this.send.bind(this);
} // shorthand use of `juiceResources` with the config
// (mainly for custom renders like from a database)
juiceResources(html) {
debug('Juicing resources for HTML');
console.log('Juicing resources for HTML');
return juiceResources(html, this.config.juiceResources);
} // a simple helper function that gets the actual file path for the template
async getTemplatePath(template) {
try {
debug('Getting template path for %s', template);
console.log('Getting template path for', template);
const [root, view] = path.isAbsolute(template) ? [path.dirname(template), path.basename(template)] : [this.config.views.root, template];
const paths = await getPaths(root, view, this.config.views.options.extension);
const filePath = path.resolve(root, paths.rel);
debug('Template path resolved to %s', filePath);
console.log('Template path resolved to', filePath);
return {
filePath,
paths
};
} catch (err) {
debug('getTemplatePath error: %O', err);
console.log('getTemplatePath error:', err);
throw err;
}
} // returns true or false if a template exists
// (uses same look-up approach as `render` function)
async templateExists(view) {
try {
debug('Checking if template exists for %s', view);
console.log('Checking if template exists for', view);
const {
filePath
} = await this.getTemplatePath(view);
const stats = await stat(filePath);
if (!stats.isFile()) throw new Error(`${filePath} was not a file`);
debug('Template exists for %s', view);
console.log('Template exists for', view);
return true;
} catch (err) {
debug('templateExists error: %O', err);
console.log('templateExists error:', err);
return false;
}
}
async checkAndRender(type, template, locals) {
try {
debug('Checking and rendering type %s for template %s', type, template);
console.log('Checking and rendering type', type, 'for template', template);
const str = this.config.getPath(type, template, locals);
if (!this.config.customRender) {
const exists = await this.templateExists(str);
if (!exists) return;
}
debug('Rendering template %s with locals %O', str, locals);
console.log('Rendering template', str, 'with locals', locals);
return this.render(str, _objectSpread({}, locals, {}, type === 'html' ? {} : {
pretty: false
}));
} catch (err) {
debug('checkAndRender error: %O', err);
console.log('checkAndRender error:', err);
throw err;
}
} // promise version of consolidate's render
// inspired by koa-views and re-uses the same config
// <https://github.com/queckezz/koa-views>
async render(view, locals = {}) {
try {
debug('Rendering view %s with locals %O', view, locals);
console.log('Rendering view', view, 'with locals', locals);
const {
map,
engineSource
} = this.config.views.options;
const {
filePath,
paths
} = await this.getTemplatePath(view);
if (paths.ext === 'html' && !map) {
const res = await readFile(filePath, 'utf8');
debug('Rendered HTML view %s', view);
console.log('Rendered HTML view', view);
return res;
}
const engineName = map && map[paths.ext] ? map[paths.ext] : paths.ext;
const renderFn = engineSource[engineName];
if (!engineName || !renderFn) throw new Error(`Engine not found for the ".${paths.ext}" file extension`);
if (_.isObject(this.config.i18n)) {
if (this.config.i18n.lastLocaleField && this.config.lastLocaleField && this.config.i18n.lastLocaleField !== this.config.lastLocaleField) throw new Error(`The 'lastLocaleField' (String) option for @ladjs/i18n and email-templates do not match, i18n value was ${this.config.i18n.lastLocaleField} and email-templates value was ${this.config.lastLocaleField}`);
const i18n = new I18N(_objectSpread({}, this.config.i18n, {
register: locals
})); // support `locals.user.last_locale` (variable based name lastLocaleField)
// (e.g. for <https://lad.js.org>)
if (_.isObject(locals.user) && _.isString(locals.user[this.config.lastLocaleField])) locals.locale = locals.user[this.config.lastLocaleField];
if (_.isString(locals.locale)) i18n.setLocale(locals.locale);
}
const res = await pify(renderFn)(filePath, locals);
debug('Rendered view %s with engine %s', view, engineName);
console.log('Rendered view', view, 'with engine', engineName); // transform the html with juice using remote paths
// google now supports media queries
// https://developers.google.com/gmail/design/reference/supported_css
if (!this.config.juice) return res;
const html = await this.juiceResources(res);
debug('Juiced HTML for view %s', view);
console.log('Juiced HTML for view', view);
return html;
} catch (err) {
debug('render error: %O', err);
console.log('render error:', err);
throw err;
}
} // eslint-disable-next-line complexity
async renderAll(template, locals = {}, nodemailerMessage = {}) {
try {
debug('Rendering all parts of template %s', template);
console.log('Rendering all parts of template', template);
const message = _objectSpread({}, nodemailerMessage);
if (template && (!message.subject || !message.html || !message.text)) {
const [subject, html, text] = await Promise.all(['subject', 'html', 'text'].map(type => this.checkAndRender(type, template, locals)));
if (subject && !message.subject) message.subject = subject;
if (html && !message.html) message.html = html;
if (text && !message.text) message.text = text;
}
if (message.subject && this.config.subjectPrefix) message.subject = this.config.subjectPrefix + message.subject; // trim subject
if (message.subject) message.subject = message.subject.trim();
if (this.config.htmlToText && message.html && !message.text) // we'd use nodemailer-html-to-text plugin
// but we really don't need to support cid
// <https://github.com/andris9/nodemailer-html-to-text>
message.text = htmlToText.fromString(message.html, this.config.htmlToText); // if we only want a text-based version of the email
if (this.config.textOnly) delete message.html; // if no subject, html, or text content exists then we should
// throw an error that says at least one must be found
// otherwise the email would be blank (defeats purpose of email-templates)
if ((!is.string(message.subject) || is.emptyStringOrWhitespace(message.subject)) && (!is.string(message.text) || is.emptyStringOrWhitespace(message.text)) && (!is.string(message.html) || is.emptyStringOrWhitespace(message.html)) && _.isArray(message.attachments) && _.isEmpty(message.attachments)) throw new Error(`No content was passed for subject, html, text, nor attachments message props. Check that the files for the template "${template}" exist.`);
debug('Rendered all parts of template %s', template);
console.log('Rendered all parts of template', template);
return message;
} catch (err) {
debug('renderAll error: %O', err);
console.log('renderAll error:', err);
throw err;
}
}
async send(options = {}) {
try {
debug('Sending email with options %O', options);
console.log('Sending email with options', options);
options = _objectSpread({
template: '',
message: {},
locals: {}
}, options);
let {
template,
message,
locals
} = options;
const attachments = message.attachments || this.config.message.attachments || [];
message = _.defaultsDeep({}, _.omit(message, 'attachments'), _.omit(this.config.message, 'attachments'));
locals = _.defaultsDeep({}, this.config.views.locals, locals);
if (attachments) message.attachments = attachments;
debug('template %s', template);
console.log('template', template);
debug('message %O', message);
console.log('message', message);
debug('locals (keys only): %O', Object.keys(locals));
console.log('locals (keys only):', Object.keys(locals)); // get all available templates
const obj = await this.renderAll(template, locals, message); // assign the object variables over to the message
Object.assign(message, obj);
if (this.config.preview) {
debug('using `preview-email` to preview email');
console.log('using `preview-email` to preview email');
if (_.isObject(this.config.preview)) await previewEmail(message, this.config.preview);else await previewEmail(message);
}
if (!this.config.send) {
debug('send disabled so we are ensuring JSONTransport');
console.log('send disabled so we are ensuring JSONTransport'); // <https://github.com/nodemailer/nodemailer/issues/798>
// if (this.config.transport.name !== 'JSONTransport')
this.config.transport = nodemailer.createTransport({
jsonTransport: true
});
}
const res = await this.config.transport.sendMail(message);
debug('message sent');
console.log('message sent');
res.originalMessage = message;
return res;
} catch (err) {
debug('send error: %O', err);
console.log('send error:', err);
throw err;
}
}
}
module.exports = Email;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC5qcyJdLCJuYW1lcyI6WyJmcyIsInJlcXVpcmUiLCJwYXRoIiwiSTE4TiIsIl8iLCJjb25zb2xpZGF0ZSIsImRlYnVnIiwiZ2V0UGF0aHMiLCJodG1sVG9UZXh0IiwiaXMiLCJqdWljZSIsIm5vZGVtYWlsZXIiLCJwaWZ5IiwicHJldmlld0VtYWlsIiwianVpY2VSZXNvdXJjZXMiLCJodG1sIiwib3B0aW9ucyIsIlByb21pc2UiLCJyZXNvbHZlIiwicmVqZWN0IiwiZXJyIiwiZW52IiwicHJvY2VzcyIsIk5PREVfRU5WIiwidG9Mb3dlckNhc2UiLCJzdGF0IiwicmVhZEZpbGUiLCJFbWFpbCIsImNvbnN0cnVjdG9yIiwiY29uZmlnIiwiY29uc29sZSIsImxvZyIsImp1aWNlT3B0aW9ucyIsImRpc2FibGVKdWljZSIsInJlbmRlciIsImN1c3RvbVJlbmRlciIsIm1lcmdlIiwidmlld3MiLCJyb290IiwiZXh0ZW5zaW9uIiwibWFwIiwiaGJzIiwibmprIiwiZW5naW5lU291cmNlIiwibG9jYWxzIiwiY2FjaGUiLCJpbmNsdWRlcyIsInByZXR0eSIsIm1lc3NhZ2UiLCJzZW5kIiwicHJldmlldyIsImkxOG4iLCJiaW5kIiwidGV4dE9ubHkiLCJpZ25vcmVJbWFnZSIsInN1YmplY3RQcmVmaXgiLCJwcmVzZXJ2ZUltcG9ydGFudCIsIndlYlJlc291cmNlcyIsInJlbGF0aXZlVG8iLCJpbWFnZXMiLCJ0cmFuc3BvcnQiLCJsYXN0TG9jYWxlRmllbGQiLCJnZXRQYXRoIiwidHlwZSIsInRlbXBsYXRlIiwiam9pbiIsImlzRnVuY3Rpb24iLCJzZW5kTWFpbCIsImNyZWF0ZVRyYW5zcG9ydCIsImdldFRlbXBsYXRlUGF0aCIsInRlbXBsYXRlRXhpc3RzIiwiY2hlY2tBbmRSZW5kZXIiLCJyZW5kZXJBbGwiLCJ2aWV3IiwiaXNBYnNvbHV0ZSIsImRpcm5hbWUiLCJiYXNlbmFtZSIsInBhdGhzIiwiZmlsZVBhdGgiLCJyZWwiLCJzdGF0cyIsImlzRmlsZSIsIkVycm9yIiwic3RyIiwiZXhpc3RzIiwiZXh0IiwicmVzIiwiZW5naW5lTmFtZSIsInJlbmRlckZuIiwiaXNPYmplY3QiLCJyZWdpc3RlciIsInVzZXIiLCJpc1N0cmluZyIsImxvY2FsZSIsInNldExvY2FsZSIsIm5vZGVtYWlsZXJNZXNzYWdlIiwic3ViamVjdCIsInRleHQiLCJhbGwiLCJ0cmltIiwiZnJvbVN0cmluZyIsInN0cmluZyIsImVtcHR5U3RyaW5nT3JXaGl0ZXNwYWNlIiwiaXNBcnJheSIsImF0dGFjaG1lbnRzIiwiaXNFbXB0eSIsImRlZmF1bHRzRGVlcCIsIm9taXQiLCJPYmplY3QiLCJrZXlzIiwib2JqIiwiYXNzaWduIiwianNvblRyYW5zcG9ydCIsIm9yaWdpbmFsTWVzc2FnZSIsIm1vZHVsZSIsImV4cG9ydHMiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBQUEsTUFBTUEsRUFBRSxHQUFHQyxPQUFPLENBQUMsSUFBRCxDQUFsQjs7QUFDQSxNQUFNQyxJQUFJLEdBQUdELE9BQU8sQ0FBQyxNQUFELENBQXBCOztBQUVBLE1BQU1FLElBQUksR0FBR0YsT0FBTyxDQUFDLGFBQUQsQ0FBcEI7O0FBQ0EsTUFBTUcsQ0FBQyxHQUFHSCxPQUFPLENBQUMsUUFBRCxDQUFqQjs7QUFDQSxNQUFNSSxXQUFXLEdBQUdKLE9BQU8sQ0FBQyxhQUFELENBQTNCOztBQUNBLE1BQU1LLEtBQUssR0FBR0wsT0FBTyxDQUFDLE9BQUQsQ0FBUCxDQUFpQixpQkFBakIsQ0FBZDs7QUFDQSxNQUFNTSxRQUFRLEdBQUdOLE9BQU8sQ0FBQyxXQUFELENBQXhCOztBQUNBLE1BQU1PLFVBQVUsR0FBR1AsT0FBTyxDQUFDLGNBQUQsQ0FBMUI7O0FBQ0EsTUFBTVEsRUFBRSxHQUFHUixPQUFPLENBQUMsa0JBQUQsQ0FBbEI7O0FBQ0EsTUFBTVMsS0FBSyxHQUFHVCxPQUFPLENBQUMsT0FBRCxDQUFyQjs7QUFDQSxNQUFNVSxVQUFVLEdBQUdWLE9BQU8sQ0FBQyxZQUFELENBQTFCOztBQUNBLE1BQU1XLElB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QixDQUFsQixDQURGLEtBRUssTUFBTXJDLFlBQVksQ0FBQ21DLE9BQUQsQ0FBbEI7QUFDTjs7QUFFRCxVQUFJLENBQUMsS0FBS25CLE1BQUwsQ0FBWW9CLElBQWpCLEVBQXVCO0FBQ3JCM0MsUUFBQUEsS0FBSyxDQUFDLGdEQUFELENBQUw7QUFDQXdCLFFBQUFBLE9BQU8sQ0FBQ0MsR0FBUixDQUFZLGdEQUFaLEVBRnFCLENBR3JCO0FBQ0E7O0FBQ0EsYUFBS0YsTUFBTCxDQUFZK0IsU0FBWixHQUF3QmpELFVBQVUsQ0FBQ3lELGVBQVgsQ0FBMkI7QUFDakQ0QyxVQUFBQSxhQUFhLEVBQUU7QUFEa0MsU0FBM0IsQ0FBeEI7QUFHRDs7QUFFRCxZQUFNMUIsR0FBRyxHQUFHLE1BQU0sS0FBS3pELE1BQUwsQ0FBWStCLFNBQVosQ0FBc0JPLFFBQXRCLENBQStCbkIsT0FBL0IsQ0FBbEI7QUFDQTFDLE1BQUFBLEtBQUssQ0FBQyxjQUFELENBQUw7QUFDQXdCLE1BQUFBLE9BQU8sQ0FBQ0MsR0FBUixDQUFZLGNBQVo7QUFDQXVELE1BQUFBLEdBQUcsQ0FBQzJCLGVBQUosR0FBc0JqRSxPQUF0QjtBQUNBLGFBQU9zQyxHQUFQO0FBQ0QsS0E1REQsQ0E0REUsT0FBT2xFLEdBQVAsRUFBWTtBQUNaZCxNQUFBQSxLQUFLLENBQUMsZ0JBQUQsRUFBbUJjLEdBQW5CLENBQUw7QUFDQVUsTUFBQUEsT0FBTyxDQUFDQyxHQUFSLENBQVksYUFBWixFQUEyQlgsR0FBM0I7QUFDQSxZQUFNQSxHQUFOO0FBQ0Q7QUFDRjs7QUF0WVM7O0FBeVlaOEYsTUFBTSxDQUFDQyxPQUFQLEdBQWlCeEYsS0FBakIiLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBmcyA9IHJlcXVpcmUoJ2ZzJyk7XG5jb25zdCBwYXRoID0gcmVxdWlyZSgncGF0aCcpO1xuXG5jb25zdCBJMThOID0gcmVxdWlyZSgnQGxhZGpzL2kxOG4nKTtcbmNvbnN0IF8gPSByZXF1aXJlKCdsb2Rhc2gnKTtcbmNvbnN0IGNvbnNvbGlkYXRlID0gcmVxdWlyZSgnY29uc29saWRhdGUnKTtcbmNvbnN0IGRlYnVnID0gcmVxdWlyZSgnZGVidWcnKSgnZW1haWwtdGVtcGxhdGVzJyk7XG5jb25zdCBnZXRQYXRocyA9IHJlcXVpcmUoJ2dldC1wYXRocycpO1xuY29uc3QgaHRtbFRvVGV4dCA9IHJlcXVpcmUoJ2h0bWwtdG8tdGV4dCcpO1xuY29uc3QgaXMgPSByZXF1aXJlKCdAc2luZHJlc29yaHVzL2lzJyk7XG5jb25zdCBqdWljZSA9IHJlcXVpcmUoJ2p1aWNlJyk7XG5jb25zdCBub2RlbWFpbGVyID0gcmVxdWlyZSgnbm9kZW1haWxlcicpO1xuY29uc3QgcGlmeSA9IHJlcXVpcmUoJ3BpZnknKTtcbmNvbnN0IHByZXZpZXdFbWFpbCA9IHJlcXVpcmUoJ3ByZXZpZXctZW1haWwnKTtcblxuLy8gcHJvbWlzZSB2ZXJzaW9uIG9mIGBqdWljZS5qdWljZVJlc291cmNlc2BcbmNvbnN0IGp1aWNlUmVzb3VyY2VzID0gKGh0bWwsIG9wdGlvbnMpID0+IHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBqdWljZS5qdWljZVJlc291cmNlcyhodG1sLCBvcHRpb25zLCAoZXJyLCBodG1sKSA9PiB7XG4gICAgICBpZiAoZXJyKSByZXR1cm4gcmVqZWN0KGVycik7XG4gICAgICByZXNvbHZlKGh0bWwpO1xuICAgIH0pO1xuICB9KTtcbn07XG5cbmNvbnN0IGVudiA9IChwcm9jZXNzLmVudi5OT0RFX0VOViB8fCAnZGV2ZWxvcG1lbnQnKS50b0xvd2VyQ2FzZSgpO1xuY29uc3Qgc3RhdCA9IHBpZnkoZnMuc3RhdCk7XG5jb25zdCByZWFkRmlsZSA9IHBpZnkoZnMucmVhZEZpbGUpO1xuXG5jbGFzcyBFbWFpbCB7XG4gIGNvbnN0cnVjdG9yKGNvbmZpZyA9IHt9KSB7XG4gICAgZGVidWcoJ2NvbmZpZyBwYXNzZWQgJU8nLCBjb25maWcpO1xuICAgIGNvbnNvbGUubG9nKCdjb25maWcgcGFzc2VkJywgY29uZmlnKTtcblxuICAgIC8vIDIueCBiYWNrd2FyZHMgY29tcGF0aWJsZSBzdXBwb3J0XG4gICAgaWYgKGNvbmZpZy5qdWljZU9wdGlvbnMpIHtcbiAgICAgIGNvbmZpZy5qdWljZVJlc291cmNlcyA9IGNvbmZpZy5qdWljZU9wdGlvbnM7XG4gICAgICBkZWxldGUgY29uZmlnLmp1aWNlT3B0aW9ucztcbiAgICB9XG5cbiAgICBkZWJ1ZygnY29uZmlnLmp1aWNlUmVzb3VyY2VzICVPJywgY29uZmlnLmp1aWNlUmVzb3VyY2VzKTtcbiAgICBjb25zb2xlLmxvZygnY29uZmlnLmp1aWNlUmVzb3VyY2VzJywgY29uZmlnLmp1aWNlUmVzb3VyY2VzKTtcblxuICAgIGlmIChjb25maWcuZGlzYWJsZUp1aWNlKSB7XG4gICAgICBjb25maWcuanVpY2UgPSBmYWxzZTtcbiAgICAgIGRlbGV0ZSBjb25maWcuZGlzYWJsZUp1aWNlO1xuICAgIH1cblxuICAgIGRlYnVnKCdjb25maWcuanVpY2UgJU8nLCBjb25maWcuanVpY2UpO1xuICAgIGNvbnNvbGUubG9nKCdjb25maWcuanVpY2UnLCBjb25maWcuanVpY2UpO1xuXG4gICAgaWYgKGNvbmZpZy5yZW5kZXIpIHtcbiAgICAgIGNvbmZpZy5jdXN0b21SZW5kZXIgPSB0cnVlO1xuICAgIH1cblxuICAgIGRlYnVnKCdjb25maWcuY3VzdG9tUmVuZGVyICVPJywgY29uZmlnLmN1c3RvbVJlbmRlcik7XG4gICAgY29uc29sZS5sb2coJ2NvbmZpZy5jdXN0b21SZW5kZXInLCBjb25maWcuY3VzdG9tUmVuZGVyKTtcblxuICAgIHRoaXMuY29uZmlnID0gXy5tZXJnZShcbiAgICAgIHtcbiAgICAgICAgdmlld3M6IHtcbiAgICAgICAgICAvLyBkaXJlY3Rvcnkgd2hlcmUgZW1haWwgdGVtcGxhdGVzIHJlc2lkZVxuICAgICAgICAgIHJvb3Q6IHBhdGgucmVzb2x2ZSgnZW1haWxzJyksXG4gICAgICAgICAgb3B0aW9uczoge1xuICAgICAgICAgICAgLy8gZGVmYXVsdCBmaWxlIGV4dGVuc2lvbiBmb3IgdGVtcGxhdGVcbiAgICAgICAgICAgIGV4dGVuc2lvbjogJ3B1ZycsXG4gICAgICAgICAgICBtYXA6IHtcbiAgICAgICAgICAgICAgaGJzOiAnaGFuZGxlYmFycycsXG4gICAgICAgICAgICAgIG5qazogJ251bmp1Y2tzJ1xuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIGVuZ2luZVNvdXJjZTogY29uc29saWRhdGVcbiAgICAgICAgICB9LFxuICAgICAgICAgIC8vIGxvY2FscyB0byBwYXNzIHRvIHRlbXBsYXRlcyBmb3IgcmVuZGVyaW5nXG4gICAgICAgICAgbG9jYWxzOiB7XG4gICAgICAgICAgICAvLyB0dXJuIG9uIGNhY2hpbmcgZm9yIG5vbi1kZXZlbG9wbWVudCBlbnZpcm9ubWVudHNcbiAgICAgICAgICAgIGNhY2hlOiAhWydkZXZlbG9wbWVudCcsICd0ZXN0J10uaW5jbHVkZXMoZW52KSxcbiAgICAgICAgICAgIC8vIHByZXR0eSBpcyBhdXRvbWF0aWNhbGx5IHNldCB0byBgZmFsc2VgIGZvciBzdWJqZWN0L3RleHRcbiAgICAgICAgICAgIHByZXR0eTogdHJ1ZVxuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgLy8gPGh0dHBzOi8vbm9kZW1haWxlci5jb20vbWVzc2FnZS8+XG4gICAgICAgIG1lc3NhZ2U6IHt9LFxuICAgICAgICBzZW5kOiAhWydkZXZlbG9wbWVudCcsICd0ZXN0J10uaW5jbHVkZXMoZW52KSxcbiAgICAgICAgcHJldmlldzogZW52ID09PSAnZGV2ZWxvcG1lbnQnLFxuICAgICAgICAvLyA8aHR0cHM6Ly9naXRodWIuY29tL2xhZGpzL2kxOG4+XG4gICAgICAgIC8vIHNldCB0byBhbiBvYmplY3QgdG8gY29uZmlndXJlIGFuZCBlbmFibGUgaXRcbiAgICAgICAgaTE4bjogZmFsc2UsXG4gICAgICAgIC8vIHBhc3MgYSBjdXN0b20gcmVuZGVyIGZ1bmN0aW9uIGlmIG5lY2Vzc2FyeVxuICAgICAgICByZW5kZXI6IHRoaXMucmVuZGVyLmJpbmQodGhpcyksXG4gICAgICAgIGN1c3RvbVJlbmRlcjogZmFsc2UsXG4gICAgICAgIC8vIGZvcmNlIHRleHQtb25seSByZW5kZXJpbmcgb2YgdGVtcGxhdGUgKGRpc3JlZ2FyZHMgdGVtcGxhdGUgZm9sZGVyKVxuICAgICAgICB0ZXh0T25seTogZmFsc2UsXG4gICAgICAgIC8vIDxodHRwczovL2dpdGh1Yi5jb20vd2Vyazg1L25vZGUtaHRtbC10by10ZXh0PlxuICAgICAgICBodG1sVG9UZXh0OiB7XG4gICAgICAgICAgaWdub3JlSW1hZ2U6IHRydWVcbiAgICAgICAgfSxcbiAgICAgICAgc3ViamVjdFByZWZpeDogZmFsc2UsXG4gICAgICAgIC8vIDxodHRwczovL2dpdGh1Yi5jb20vQXV0b21hdHRpYy9qdWljZT5cbiAgICAgICAganVpY2U6IHRydWUsXG4gICAgICAgIGp1aWNlUmVzb3VyY2VzOiB7XG4gICAgICAgICAgcHJlc2VydmVJbXBvcnRhbnQ6IHRydWUsXG4gICAgICAgICAgd2ViUmVzb3VyY2VzOiB7XG4gICAgICAgICAgICByZWxhdGl2ZVRvOiBwYXRoLnJlc29sdmUoJ2J1aWxkJyksXG4gICAgICAgICAgICBpbWFnZXM6IGZhbHNlXG4gICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICAvLyBwYXNzIGEgdHJhbnNwb3J0IGNvbmZpZ3VyYXRpb24gb2JqZWN0IG9yIGEgdHJhbnNwb3J0IGluc3RhbmNlXG4gICAgICAgIC8vIChlLmcuIGFuIGluc3RhbmNlIGlzIGNyZWF0ZWQgdmlhIGBub2RlbWFpbGVyLmNyZWF0ZVRyYW5zcG9ydGApXG4gICAgICAgIC8vIDxodHRwczovL25vZGVtYWlsZXIuY29tL3RyYW5zcG9ydHMvPlxuICAgICAgICB0cmFuc3BvcnQ6IHt9LFxuICAgICAgICAvLyBsYXN0IGxvY2FsZSBmaWVsZCBuYW1lIChhbHNvIHVzZWQgYnkgQGxhZGpzL2kxOG4pXG4gICAgICAgIGxhc3RMb2NhbGVGaWVsZDogJ2xhc3RfbG9jYWxlJyxcbiAgICAgICAgZ2V0UGF0aCh0eXBlLCB0ZW1wbGF0ZSkge1xuICAgICAgICAgIHJldHVybiBwYXRoLmpvaW4odGVtcGxhdGUsIHR5cGUpO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgY29uZmlnXG4gICAgKTtcblxuICAgIGRlYnVnKCdtZXJnZWQgY29uZmlnICVPJywgdGhpcy5jb25maWcpO1xuICAgIGNvbnNvbGUubG9nKCdtZXJnZWQgY29uZmlnJywgdGhpcy5jb25maWcpO1xuXG4gICAgLy8gb3ZlcnJpZGUgZXhpc3RpbmcgbWV0aG9kXG4gICAgdGhpcy5yZW5kZXIgPSB0aGlzLmNvbmZpZy5yZW5kZXI7XG5cbiAgICBkZWJ1ZygnY29uZmlnLnRyYW5zcG9ydCAlTycsIHRoaXMuY29uZmlnLnRyYW5zcG9ydCk7XG4gICAgY29uc29sZS5sb2coJ2NvbmZpZy50cmFuc3BvcnQnLCB0aGlzLmNvbmZpZy50cmFuc3BvcnQpO1xuXG4gICAgaWYgKCFfLmlzRnVuY3Rpb24odGhpcy5jb25maWcudHJhbnNwb3J0LnNlbmRNYWlsKSlcbiAgICAgIHRoaXMuY29uZmlnLnRyYW5zcG9ydCA9IG5vZGVtYWlsZXIuY3JlYXRlVHJhbnNwb3J0KHRoaXMuY29uZmlnLnRyYW5zcG9ydCk7XG5cbiAgICBkZWJ1ZygndHJhbnNwb3J0IGNyZWF0ZWQnKTtcbiAgICBjb25zb2xlLmxvZygndHJhbnNwb3J0IGNyZWF0ZWQnKTtcblxuICAgIGRlYnVnKCd0cmFuc2Zvcm1lZCBjb25maWcgJU8nLCB0aGlzLmNvbmZpZyk7XG4gICAgY29uc29sZS5sb2coJ3RyYW5zZm9ybWVkIGNvbmZpZycsIHRoaXMuY29uZmlnKTtcblxuICAgIHRoaXMuanVpY2VSZXNvdXJjZXMgPSB0aGlzLmp1aWNlUmVzb3VyY2VzLmJpbmQodGhpcyk7XG4gICAgdGhpcy5nZXRUZW1wbGF0ZVBhdGggPSB0aGlzLmdldFRlbXBsYXRlUGF0aC5iaW5kKHRoaXMpO1xuICAgIHRoaXMudGVtcGxhdGVFeGlzdHMgPSB0aGlzLnRlbXBsYXRlRXhpc3RzLmJpbmQodGhpcyk7XG4gICAgdGhpcy5jaGVja0FuZFJlbmRlciA9IHRoaXMuY2hlY2tBbmRSZW5kZXIuYmluZCh0aGlzKTtcbiAgICB0aGlzLnJlbmRlciA9IHRoaXMucmVuZGVyLmJpbmQodGhpcyk7XG4gICAgdGhpcy5yZW5kZXJBbGwgPSB0aGlzLnJlbmRlckFsbC5iaW5kKHRoaXMpO1xuICAgIHRoaXMuc2VuZCA9IHRoaXMuc2VuZC5iaW5kKHRoaXMpO1xuICB9XG5cbiAgLy8gc2hvcnRoYW5kIHVzZSBvZiBganVpY2VSZXNvdXJjZXNgIHdpdGggdGhlIGNvbmZpZ1xuICAvLyAobWFpbmx5IGZvciBjdXN0b20gcmVuZGVycyBsaWtlIGZyb20gYSBkYXRhYmFzZSlcbiAganVpY2VSZXNvdXJjZXMoaHRtbCkge1xuICAgIGRlYnVnKCdKdWljaW5nIHJlc291cmNlcyBmb3IgSFRNTCcpO1xuICAgIGNvbnNvbGUubG9nKCdKdWljaW5nIHJlc291cmNlcyBmb3IgSFRNTCcpO1xuICAgIHJldHVybiBqdWljZVJlc291cmNlcyhodG1sLCB0aGlzLmNvbmZpZy5qdWljZVJlc291cmNlcyk7XG4gIH1cblxuICAvLyBhIHNpbXBsZSBoZWxwZXIgZnVuY3Rpb24gdGhhdCBnZXRzIHRoZSBhY3R1YWwgZmlsZSBwYXRoIGZvciB0aGUgdGVtcGxhdGVcbiAgYXN5bmMgZ2V0VGVtcGxhdGVQYXRoKHRlbXBsYXRlKSB7XG4gICAgdHJ5IHtcbiAgICAgIGRlYnVnKCdHZXR0aW5nIHRlbXBsYXRlIHBhdGggZm9yICVzJywgdGVtcGxhdGUpO1xuICAgICAgY29uc29sZS5sb2coJ0dldHRpbmcgdGVtcGxhdGUgcGF0aCBmb3InLCB0ZW1wbGF0ZSk7XG4gICAgICBjb25zdCBbcm9vdCwgdmlld10gPSBwYXRoLmlzQWJzb2x1dGUodGVtcGxhdGUpXG4gICAgICAgID8gW3BhdGguZGlybmFtZSh0ZW1wbGF0ZSksIHBhdGguYmFzZW5hbWUodGVtcGxhdGUpXVxuICAgICAgICA6IFt0aGlzLmNvbmZpZy52aWV3cy5yb290LCB0ZW1wbGF0ZV07XG4gICAgICBjb25zdCBwYXRocyA9IGF3YWl0IGdldFBhdGhzKFxuICAgICAgICByb290LFxuICAgICAgICB2aWV3LFxuICAgICAgICB0aGlzLmNvbmZpZy52aWV3cy5vcHRpb25zLmV4dGVuc2lvblxuICAgICAgKTtcbiAgICAgIGNvbnN0IGZpbGVQYXRoID0gcGF0aC5yZXNvbHZlKHJvb3QsIHBhdGhzLnJlbCk7XG4gICAgICBkZWJ1ZygnVGVtcGxhdGUgcGF0aCByZXNvbHZlZCB0byAlcycsIGZpbGVQYXRoKTtcbiAgICAgIGNvbnNvbGUubG9nKCdUZW1wbGF0ZSBwYXRoIHJlc29sdmVkIHRvJywgZmlsZVBhdGgpO1xuICAgICAgcmV0dXJuIHsgZmlsZVBhdGgsIHBhdGhzIH07XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBkZWJ1ZygnZ2V0VGVtcGxhdGVQYXRoIGVycm9yOiAlTycsIGVycik7XG4gICAgICBjb25zb2xlLmxvZygnZ2V0VGVtcGxhdGVQYXRoIGVycm9yOicsIGVycik7XG4gICAgICB0aHJvdyBlcnI7XG4gICAgfVxuICB9XG5cbiAgLy8gcmV0dXJucyB0cnVlIG9yIGZhbHNlIGlmIGEgdGVtcGxhdGUgZXhpc3RzXG4gIC8vICh1c2VzIHNhbWUgbG9vay11cCBhcHByb2FjaCBhcyBgcmVuZGVyYCBmdW5jdGlvbilcbiAgYXN5bmMgdGVtcGxhdGVFeGlzdHModmlldykge1xuICAgIHRyeSB7XG4gICAgICBkZWJ1ZygnQ2hlY2tpbmcgaWYgdGVtcGxhdGUgZXhpc3RzIGZvciAlcycsIHZpZXcpO1xuICAgICAgY29uc29sZS5sb2coJ0NoZWNraW5nIGlmIHRlbXBsYXRlIGV4aXN0cyBmb3InLCB2aWV3KTtcbiAgICAgIGNvbnN0IHsgZmlsZVBhdGggfSA9IGF3YWl0IHRoaXMuZ2V0VGVtcGxhdGVQYXRoKHZpZXcpO1xuICAgICAgY29uc3Qgc3RhdHMgPSBhd2FpdCBzdGF0KGZpbGVQYXRoKTtcbiAgICAgIGlmICghc3RhdHMuaXNGaWxlKCkpIHRocm93IG5ldyBFcnJvcihgJHtmaWxlUGF0aH0gd2FzIG5vdCBhIGZpbGVgKTtcbiAgICAgIGRlYnVnKCdUZW1wbGF0ZSBleGlzdHMgZm9yICVzJywgdmlldyk7XG4gICAgICBjb25zb2xlLmxvZygnVGVtcGxhdGUgZXhpc3RzIGZvcicsIHZpZXcpO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBkZWJ1ZygndGVtcGxhdGVFeGlzdHMgZXJyb3I6ICVPJywgZXJyKTtcbiAgICAgIGNvbnNvbGUubG9nKCd0ZW1wbGF0ZUV4aXN0cyBlcnJvcjonLCBlcnIpO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGNoZWNrQW5kUmVuZGVyKHR5cGUsIHRlbXBsYXRlLCBsb2NhbHMpIHtcbiAgICB0cnkge1xuICAgICAgZGVidWcoJ0NoZWNraW5nIGFuZCByZW5kZXJpbmcgdHlwZSAlcyBmb3IgdGVtcGxhdGUgJXMnLCB0eXBlLCB0ZW1wbGF0ZSk7XG4gICAgICBjb25zb2xlLmxvZyhcbiAgICAgICAgJ0NoZWNraW5nIGFuZCByZW5kZXJpbmcgdHlwZScsXG4gICAgICAgIHR5cGUsXG4gICAgICAgICdmb3IgdGVtcGxhdGUnLFxuICAgICAgICB0ZW1wbGF0ZVxuICAgICAgKTtcbiAgICAgIGNvbnN0IHN0ciA9IHRoaXMuY29uZmlnLmdldFBhdGgodHlwZSwgdGVtcGxhdGUsIGxvY2Fscyk7XG4gICAgICBpZiAoIXRoaXMuY29uZmlnLmN1c3RvbVJlbmRlcikge1xuICAgICAgICBjb25zdCBleGlzdHMgPSBhd2FpdCB0aGlzLnRlbXBsYXRlRXhpc3RzKHN0cik7XG4gICAgICAgIGlmICghZXhpc3RzKSByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGRlYnVnKCdSZW5kZXJpbmcgdGVtcGxhdGUgJXMgd2l0aCBsb2NhbHMgJU8nLCBzdHIsIGxvY2Fscyk7XG4gICAgICBjb25zb2xlLmxvZygnUmVuZGVyaW5nIHRlbXBsYXRlJywgc3RyLCAnd2l0aCBsb2NhbHMnLCBsb2NhbHMpO1xuICAgICAgcmV0dXJuIHRoaXMucmVuZGVyKHN0ciwge1xuICAgICAgICAuLi5sb2NhbHMsXG4gICAgICAgIC4uLih0eXBlID09PSAnaHRtbCcgPyB7fSA6IHsgcHJldHR5OiBmYWxzZSB9KVxuICAgICAgfSk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBkZWJ1ZygnY2hlY2tBbmRSZW5kZXIgZXJyb3I6ICVPJywgZXJyKTtcbiAgICAgIGNvbnNvbGUubG9nKCdjaGVja0FuZFJlbmRlciBlcnJvcjonLCBlcnIpO1xuICAgICAgdGhyb3cgZXJyO1xuICAgIH1cbiAgfVxuXG4gIC8vIHByb21pc2UgdmVyc2lvbiBvZiBjb25zb2xpZGF0ZSdzIHJlbmRlclxuICAvLyBpbnNwaXJlZCBieSBrb2Etdmlld3MgYW5kIHJlLXVzZXMgdGhlIHNhbWUgY29uZmlnXG4gIC8vIDxodHRwczovL2dpdGh1Yi5jb20vcXVlY2tlenova29hLXZpZXdzPlxuICBhc3luYyByZW5kZXIodmlldywgbG9jYWxzID0ge30pIHtcbiAgICB0cnkge1xuICAgICAgZGVidWcoJ1JlbmRlcmluZyB2aWV3ICVzIHdpdGggbG9jYWxzICVPJywgdmlldywgbG9jYWxzKTtcbiAgICAgIGNvbnNvbGUubG9nKCdSZW5kZXJpbmcgdmlldycsIHZpZXcsICd3aXRoIGxvY2FscycsIGxvY2Fscyk7XG4gICAgICBjb25zdCB7IG1hcCwgZW5naW5lU291cmNlIH0gPSB0aGlzLmNvbmZpZy52aWV3cy5vcHRpb25zO1xuICAgICAgY29uc3QgeyBmaWxlUGF0aCwgcGF0aHMgfSA9IGF3YWl0IHRoaXMuZ2V0VGVtcGxhdGVQYXRoKHZpZXcpO1xuICAgICAgaWYgKHBhdGhzLmV4dCA9PT0gJ2h0bWwnICYmICFtYXApIHtcbiAgICAgICAgY29uc3QgcmVzID0gYXdhaXQgcmVhZEZpbGUoZmlsZVBhdGgsICd1dGY4Jyk7XG4gICAgICAgIGRlYnVnKCdSZW5kZXJlZCBIVE1MIHZpZXcgJXMnLCB2aWV3KTtcbiAgICAgICAgY29uc29sZS5sb2coJ1JlbmRlcmVkIEhUTUwgdmlldycsIHZpZXcpO1xuICAgICAgICByZXR1cm4gcmVzO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBlbmdpbmVOYW1lID0gbWFwICYmIG1hcFtwYXRocy5leHRdID8gbWFwW3BhdGhzLmV4dF0gOiBwYXRocy5leHQ7XG4gICAgICBjb25zdCByZW5kZXJGbiA9IGVuZ2luZVNvdXJjZVtlbmdpbmVOYW1lXTtcbiAgICAgIGlmICghZW5naW5lTmFtZSB8fCAhcmVuZGVyRm4pXG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICBgRW5naW5lIG5vdCBmb3VuZCBmb3IgdGhlIFwiLiR7cGF0aHMuZXh0fVwiIGZpbGUgZXh0ZW5zaW9uYFxuICAgICAgICApO1xuXG4gICAgICBpZiAoXy5pc09iamVjdCh0aGlzLmNvbmZpZy5pMThuKSkge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgdGhpcy5jb25maWcuaTE4bi5sYXN0TG9jYWxlRmllbGQgJiZcbiAgICAgICAgICB0aGlzLmNvbmZpZy5sYXN0TG9jYWxlRmllbGQgJiZcbiAgICAgICAgICB0aGlzLmNvbmZpZy5pMThuLmxhc3RMb2NhbGVGaWVsZCAhPT0gdGhpcy5jb25maWcubGFzdExvY2FsZUZpZWxkXG4gICAgICAgIClcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgVGhlICdsYXN0TG9jYWxlRmllbGQnIChTdHJpbmcpIG9wdGlvbiBmb3IgQGxhZGpzL2kxOG4gYW5kIGVtYWlsLXRlbXBsYXRlcyBkbyBub3QgbWF0Y2gsIGkxOG4gdmFsdWUgd2FzICR7dGhpcy5jb25maWcuaTE4bi5sYXN0TG9jYWxlRmllbGR9IGFuZCBlbWFpbC10ZW1wbGF0ZXMgdmFsdWUgd2FzICR7dGhpcy5jb25maWcubGFzdExvY2FsZUZpZWxkfWBcbiAgICAgICAgICApO1xuXG4gICAgICAgIGNvbnN0IGkxOG4gPSBuZXcgSTE4Tih7IC4uLnRoaXMuY29uZmlnLmkxOG4sIHJlZ2lzdGVyOiBsb2NhbHMgfSk7XG5cbiAgICAgICAgLy8gc3VwcG9ydCBgbG9jYWxzLnVzZXIubGFzdF9sb2NhbGVgICh2YXJpYWJsZSBiYXNlZCBuYW1lIGxhc3RMb2NhbGVGaWVsZClcbiAgICAgICAgLy8gKGUuZy4gZm9yIDxodHRwczovL2xhZC5qcy5vcmc+KVxuICAgICAgICBpZiAoXG4gICAgICAgICAgXy5pc09iamVjdChsb2NhbHMudXNlcikgJiZcbiAgICAgICAgICBfLmlzU3RyaW5nKGxvY2Fscy51c2VyW3RoaXMuY29uZmlnLmxhc3RMb2NhbGVGaWVsZF0pXG4gICAgICAgIClcbiAgICAgICAgICBsb2NhbHMubG9jYWxlID0gbG9jYWxzLnVzZXJbdGhpcy5jb25maWcubGFzdExvY2FsZUZpZWxkXTtcblxuICAgICAgICBpZiAoXy5pc1N0cmluZyhsb2NhbHMubG9jYWxlKSkgaTE4bi5zZXRMb2NhbGUobG9jYWxzLmxvY2FsZSk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHJlcyA9IGF3YWl0IHBpZnkocmVuZGVyRm4pKGZpbGVQYXRoLCBsb2NhbHMpO1xuICAgICAgZGVidWcoJ1JlbmRlcmVkIHZpZXcgJXMgd2l0aCBlbmdpbmUgJXMnLCB2aWV3LCBlbmdpbmVOYW1lKTtcbiAgICAgIGNvbnNvbGUubG9nKCdSZW5kZXJlZCB2aWV3JywgdmlldywgJ3dpdGggZW5naW5lJywgZW5naW5lTmFtZSk7XG4gICAgICAvLyB0cmFuc2Zvcm0gdGhlIGh0bWwgd2l0aCBqdWljZSB1c2luZyByZW1vdGUgcGF0aHNcbiAgICAgIC8vIGdvb2dsZSBub3cgc3VwcG9ydHMgbWVkaWEgcXVlcmllc1xuICAgICAgLy8gaHR0cHM6Ly9kZXZlbG9wZXJzLmdvb2dsZS5jb20vZ21haWwvZGVzaWduL3JlZmVyZW5jZS9zdXBwb3J0ZWRfY3NzXG4gICAgICBpZiAoIXRoaXMuY29uZmlnLmp1aWNlKSByZXR1cm4gcmVzO1xuICAgICAgY29uc3QgaHRtbCA9IGF3YWl0IHRoaXMuanVpY2VSZXNvdXJjZXMocmVzKTtcbiAgICAgIGRlYnVnKCdKdWljZWQgSFRNTCBmb3IgdmlldyAlcycsIHZpZXcpO1xuICAgICAgY29uc29sZS5sb2coJ0p1aWNlZCBIVE1MIGZvciB2aWV3Jywgdmlldyk7XG4gICAgICByZXR1cm4gaHRtbDtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGRlYnVnKCdyZW5kZXIgZXJyb3I6ICVPJywgZXJyKTtcbiAgICAgIGNvbnNvbGUubG9nKCdyZW5kZXIgZXJyb3I6JywgZXJyKTtcbiAgICAgIHRocm93IGVycjtcbiAgICB9XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBhc3luYyByZW5kZXJBbGwodGVtcGxhdGUsIGxvY2FscyA9IHt9LCBub2RlbWFpbGVyTWVzc2FnZSA9IHt9KSB7XG4gICAgdHJ5IHtcbiAgICAgIGRlYnVnKCdSZW5kZXJpbmcgYWxsIHBhcnRzIG9mIHRlbXBsYXRlICVzJywgdGVtcGxhdGUpO1xuICAgICAgY29uc29sZS5sb2coJ1JlbmRlcmluZyBhbGwgcGFydHMgb2YgdGVtcGxhdGUnLCB0ZW1wbGF0ZSk7XG4gICAgICBjb25zdCBtZXNzYWdlID0geyAuLi5ub2RlbWFpbGVyTWVzc2FnZSB9O1xuXG4gICAgICBpZiAodGVtcGxhdGUgJiYgKCFtZXNzYWdlLnN1YmplY3QgfHwgIW1lc3NhZ2UuaHRtbCB8fCAhbWVzc2FnZS50ZXh0KSkge1xuICAgICAgICBjb25zdCBbc3ViamVjdCwgaHRtbCwgdGV4dF0gPSBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgICAgICBbJ3N1YmplY3QnLCAnaHRtbCcsICd0ZXh0J10ubWFwKCh0eXBlKSA9PlxuICAgICAgICAgICAgdGhpcy5jaGVja0FuZFJlbmRlcih0eXBlLCB0ZW1wbGF0ZSwgbG9jYWxzKVxuICAgICAgICAgIClcbiAgICAgICAgKTtcblxuICAgICAgICBpZiAoc3ViamVjdCAm