@nota/nativescript-webview-ext
Version:
Extended WebView for NativeScript which adds 'x-local' scheme for local-files. events between WebView and native-layer, javascript execution, injecting CSS and JS-files.
682 lines • 82.8 kB
JavaScript
import "@nativescript/core";
import { File, knownFolders, Trace } from "@nativescript/core";
import { isEnabledProperty } from "@nativescript/core/ui/core/view";
import { builtInZoomControlsProperty, cacheModeProperty, databaseStorageProperty, debugModeProperty, displayZoomControlsProperty, domStorageProperty, supportZoomProperty, UnsupportedSDKError, WebViewExtBase, } from "./webview-ext-common";
export * from "./webview-ext-common";
const extToMimeType = new Map([
["html", "text/html"],
["htm", "text/html"],
["xhtml", "text/html"],
["xhtm", "text/html"],
["css", "text/css"],
["gif", "image/gif"],
["jpeg", "image/jpeg"],
["jpg", "image/jpeg"],
["js", "text/javascript"],
["otf", "application/vnd.ms-opentype"],
["png", "image/png"],
["svg", "image/svg+xml"],
["ttf", "application/x-font-ttf"],
]);
const extToBinaryEncoding = new Set(["gif", "jpeg", "jpg", "otf", "png", "ttf"]);
let cacheModeMap;
let WebViewExtClient;
let WebChromeViewExtClient;
let WebViewBridgeInterface;
function initializeWebViewClient() {
if (WebViewExtClient) {
return;
}
cacheModeMap = new Map([
["cache_first", android.webkit.WebSettings.LOAD_CACHE_ELSE_NETWORK],
["cache_only", android.webkit.WebSettings.LOAD_CACHE_ONLY],
["default", android.webkit.WebSettings.LOAD_DEFAULT],
["no_cache", android.webkit.WebSettings.LOAD_NO_CACHE],
["normal", android.webkit.WebSettings.LOAD_NORMAL],
]);
var WebViewExtClientImpl = /** @class */ (function (_super) {
__extends(WebViewExtClientImpl, _super);
function WebViewExtClientImpl(owner) {
var _this = _super.call(this) || this;
_this.owner = new WeakRef(owner);
return global.__native(_this);
}
/**
* Give the host application a chance to take control when a URL is about to be loaded in the current WebView.
*/
WebViewExtClientImpl.prototype.shouldOverrideUrlLoading = function (view, request) {
var owner = this.owner.get();
if (!owner) {
console.warn("WebViewExtClientImpl.shouldOverrideUrlLoading(...) - no owner");
return true;
}
var url = request;
var httpMethod = "GET";
var isRedirect = false;
var hasGesture = false;
var isForMainFrame = false;
var requestHeaders = null;
if (typeof request === "object") {
httpMethod = request.getMethod();
isRedirect = request.isRedirect();
hasGesture = request.hasGesture();
isForMainFrame = request.isForMainFrame();
requestHeaders = request.getRequestHeaders();
url = request.getUrl().toString();
}
owner.writeTrace("WebViewClientClass.shouldOverrideUrlLoading(\"" + url + "\") - method:" + httpMethod + " isRedirect:" + isRedirect + " hasGesture:" + hasGesture + " isForMainFrame:" + isForMainFrame + " headers:" + requestHeaders);
if (url.startsWith(owner.interceptScheme)) {
owner.writeTrace("WebViewClientClass.shouldOverrideUrlLoading(\"" + url + "\") - \"" + owner.interceptScheme + "\" - cancel");
return true;
}
var shouldOverrideUrlLoading = owner._onShouldOverrideUrlLoading(url, httpMethod);
if (shouldOverrideUrlLoading === true) {
owner.writeTrace("WebViewClientClass.shouldOverrideUrlLoading(\"" + url + "\") - cancel loading url");
return true;
}
return false;
};
WebViewExtClientImpl.prototype.shouldInterceptRequest = function (view, request) {
var owner = this.owner.get();
if (!owner) {
console.warn("WebViewExtClientImpl.shouldInterceptRequest(...) - no owner");
return _super.prototype.shouldInterceptRequest.call(this, view, request);
}
var url;
if (typeof request === "string") {
url = request;
}
else if (typeof request === "object") {
url = request.getUrl().toString();
}
if (typeof url !== "string") {
owner.writeTrace("WebViewClientClass.shouldInterceptRequest(\"" + url + "\") - is not a string");
return _super.prototype.shouldInterceptRequest.call(this, view, request);
}
if (!url.startsWith(owner.interceptScheme)) {
return _super.prototype.shouldInterceptRequest.call(this, view, request);
}
var filepath = owner.getRegisteredLocalResource(url);
if (!filepath) {
owner.writeTrace("WebViewClientClass.shouldInterceptRequest(\"" + url + "\") - no matching file");
return _super.prototype.shouldInterceptRequest.call(this, view, request);
}
if (!File.exists(filepath)) {
owner.writeTrace("WebViewClientClass.shouldInterceptRequest(\"" + url + "\") - file: \"" + filepath + "\" doesn't exists");
return _super.prototype.shouldInterceptRequest.call(this, view, request);
}
var tnsFile = File.fromPath(filepath);
var javaFile = new java.io.File(tnsFile.path);
var stream = new java.io.FileInputStream(javaFile);
var ext = tnsFile.extension.substr(1).toLowerCase();
var mimeType = extToMimeType.get(ext) || "application/octet-stream";
var encoding = extToBinaryEncoding.has(ext) || mimeType === "application/octet-stream" ? "binary" : "UTF-8";
owner.writeTrace("WebViewClientClass.shouldInterceptRequest(\"" + url + "\") - file: \"" + filepath + "\" mimeType:" + mimeType + " encoding:" + encoding);
var response = new android.webkit.WebResourceResponse(mimeType, encoding, stream);
if (android.os.Build.VERSION.SDK_INT < 21 || !response.getResponseHeaders) {
return response;
}
var responseHeaders = response.getResponseHeaders();
if (!responseHeaders) {
responseHeaders = new java.util.HashMap();
}
responseHeaders.put("Access-Control-Allow-Origin", "*");
response.setResponseHeaders(responseHeaders);
return response;
};
WebViewExtClientImpl.prototype.onPageStarted = function (view, url, favicon) {
_super.prototype.onPageStarted.call(this, view, url, favicon);
var owner = this.owner.get();
if (!owner) {
console.warn("WebViewExtClientImpl.onPageStarted(\"" + view + "\", \"" + url + "\", \"" + favicon + "\") - no owner");
return;
}
owner.writeTrace("WebViewClientClass.onPageStarted(\"" + view + "\", \"" + url + "\", \"" + favicon + "\")");
owner._onLoadStarted(url);
};
WebViewExtClientImpl.prototype.onPageFinished = function (view, url) {
_super.prototype.onPageFinished.call(this, view, url);
var owner = this.owner.get();
if (!owner) {
console.warn("WebViewExtClientImpl.onPageFinished(\"" + view + "\", " + url + "\") - no owner");
return;
}
owner.writeTrace("WebViewClientClass.onPageFinished(\"" + view + "\", " + url + "\")");
owner._onLoadFinished(url).catch(function () { return void 0; });
};
WebViewExtClientImpl.prototype.onReceivedError = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
if (args.length === 4) {
var _a = args, view = _a[0], errorCode = _a[1], description = _a[2], failingUrl = _a[3];
this.onReceivedErrorBeforeAPI23(view, errorCode, description, failingUrl);
}
else {
var _b = args, view = _b[0], request = _b[1], error = _b[2];
this.onReceivedErrorAPI23(view, request, error);
}
};
WebViewExtClientImpl.prototype.onReceivedErrorAPI23 = function (view, request, error) {
_super.prototype.onReceivedError.call(this, view, request, error);
var owner = this.owner.get();
if (!owner) {
console.warn("WebViewExtClientImpl.onReceivedErrorAPI23(...) - no owner");
return;
}
var url = error.getUrl && error.getUrl();
if (!url && typeof request === "object") {
url = request.getUrl().toString();
}
owner.writeTrace("WebViewClientClass.onReceivedErrorAPI23(" + error.getErrorCode() + ", " + error.getDescription() + ", " + url + ")");
owner._onLoadFinished(url, error.getDescription() + "(" + error.getErrorCode() + ")").catch(function () { return void 0; });
};
WebViewExtClientImpl.prototype.onReceivedErrorBeforeAPI23 = function (view, errorCode, description, failingUrl) {
_super.prototype.onReceivedError.call(this, view, errorCode, description, failingUrl);
var owner = this.owner.get();
if (!owner) {
console.warn("WebViewExtClientImpl.onReceivedErrorBeforeAPI23(...) - no owner");
return;
}
owner.writeTrace("WebViewClientClass.onReceivedErrorBeforeAPI23(" + errorCode + ", \"" + description + "\", \"" + failingUrl + "\")");
owner._onLoadFinished(failingUrl, description + "(" + errorCode + ")").catch(function () { return void 0; });
};
return WebViewExtClientImpl;
}(android.webkit.WebViewClient));
WebViewExtClient = WebViewExtClientImpl;
var WebChromeViewExtClientImpl = /** @class */ (function (_super) {
__extends(WebChromeViewExtClientImpl, _super);
function WebChromeViewExtClientImpl(owner) {
var _this = _super.call(this) || this;
_this.owner = new WeakRef(owner);
return global.__native(_this);
}
WebChromeViewExtClientImpl.prototype.onShowCustomView = function (view) {
var _this = this;
var owner = this.owner.get();
if (!owner) {
return;
}
var callback;
if (arguments.length === 3) {
callback = arguments[2];
}
else if (arguments.length === 2) {
callback = arguments[1];
}
else {
return;
}
if (owner._onEnterFullscreen(function () { return _this.hideCustomView(); })) {
this.showCustomViewCallback = callback;
}
else {
callback.onCustomViewHidden();
}
};
WebChromeViewExtClientImpl.prototype.hideCustomView = function () {
if (this.showCustomViewCallback) {
this.showCustomViewCallback.onCustomViewHidden();
}
this.showCustomViewCallback = undefined;
};
WebChromeViewExtClientImpl.prototype.onHideCustomView = function () {
this.showCustomViewCallback = undefined;
var owner = this.owner.get();
if (!owner) {
return;
}
owner._onExitFullscreen();
};
WebChromeViewExtClientImpl.prototype.onProgressChanged = function (view, newProgress) {
var owner = this.owner.get();
if (!owner) {
return;
}
owner._loadProgress(newProgress);
};
WebChromeViewExtClientImpl.prototype.onReceivedTitle = function (view, title) {
var owner = this.owner.get();
if (!owner) {
return;
}
owner._titleChanged(title);
};
WebChromeViewExtClientImpl.prototype.onJsAlert = function (view, url, message, result) {
var owner = this.owner.get();
if (!owner) {
return false;
}
var gotResponse = false;
return owner._webAlert(message, function () {
if (!gotResponse) {
result.confirm();
}
gotResponse = true;
});
};
WebChromeViewExtClientImpl.prototype.onJsConfirm = function (view, url, message, result) {
var owner = this.owner.get();
if (!owner) {
return false;
}
var gotResponse = false;
return owner._webConfirm(message, function (confirmed) {
if (!gotResponse) {
if (confirmed) {
result.confirm();
}
else {
result.cancel();
}
}
gotResponse = true;
});
};
WebChromeViewExtClientImpl.prototype.onJsPrompt = function (view, url, message, defaultValue, result) {
var owner = this.owner.get();
if (!owner) {
return false;
}
var gotResponse = false;
return owner._webPrompt(message, defaultValue, function (message) {
if (!gotResponse) {
if (message) {
result.confirm(message);
}
else {
result.confirm();
}
}
gotResponse = true;
});
};
WebChromeViewExtClientImpl.prototype.onConsoleMessage = function () {
if (arguments.length !== 1) {
return false;
}
var owner = this.owner.get();
if (!owner) {
return false;
}
var consoleMessage = arguments[0];
if (consoleMessage instanceof android.webkit.ConsoleMessage) {
var message = consoleMessage.message();
var lineNo = consoleMessage.lineNumber();
var level = "log";
var _a = android.webkit.ConsoleMessage.MessageLevel, DEBUG = _a.DEBUG, LOG = _a.LOG, WARNING = _a.WARNING;
switch (consoleMessage.messageLevel()) {
case DEBUG: {
level = "debug";
break;
}
case LOG: {
level = "log";
break;
}
case WARNING: {
level = "warn";
break;
}
}
return owner._webConsole(message, lineNo, level);
}
return false;
};
return WebChromeViewExtClientImpl;
}(android.webkit.WebChromeClient));
WebChromeViewExtClient = WebChromeViewExtClientImpl;
var WebViewBridgeInterfaceImpl = /** @class */ (function (_super) {
__extends(WebViewBridgeInterfaceImpl, _super);
function WebViewBridgeInterfaceImpl(owner) {
var _this = _super.call(this) || this;
_this.owner = new WeakRef(owner);
return global.__native(_this);
}
WebViewBridgeInterfaceImpl.prototype.emitEventToNativeScript = function (eventName, data) {
var owner = this.owner.get();
if (!owner) {
console.warn("WebViewExtClientImpl.emitEventToNativeScript(\"" + eventName + "\") - no owner");
return;
}
try {
if (typeof data == "string" && data) {
owner.onWebViewEvent(eventName, JSON.parse(data));
return;
}
owner.onWebViewEvent(eventName, null);
return;
}
catch (err) {
owner.writeTrace("WebViewExtClientImpl.emitEventToNativeScript(\"" + eventName + "\") - couldn't parse data: " + JSON.stringify(data) + " err: " + err);
}
};
return WebViewBridgeInterfaceImpl;
}(dk.nota.webviewinterface.WebViewBridgeInterface));
WebViewBridgeInterface = WebViewBridgeInterfaceImpl;
}
let instanceNo = 0;
export class WebViewExt extends WebViewExtBase {
constructor() {
super(...arguments);
this.localResourceMap = new Map();
this.supportXLocalScheme = true;
this.instance = ++instanceNo;
}
createNativeView() {
const nativeView = new android.webkit.WebView(this._context);
const settings = nativeView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setAllowFileAccess(true);
settings.setBuiltInZoomControls(!!this.builtInZoomControls);
settings.setDisplayZoomControls(!!this.displayZoomControls);
settings.setSupportZoom(!!this.supportZoom);
if (android.os.Build.VERSION.SDK_INT >= 21) {
settings.setMixedContentMode(android.webkit.WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
}
settings.setAllowUniversalAccessFromFileURLs(true);
return nativeView;
}
initNativeView() {
super.initNativeView();
initializeWebViewClient();
const nativeView = this.nativeViewProtected;
if (!nativeView) {
return;
}
const client = new WebViewExtClient(this);
const chromeClient = new WebChromeViewExtClient(this);
nativeView.setWebViewClient(client);
nativeView.client = client;
nativeView.setWebChromeClient(chromeClient);
nativeView.chromeClient = chromeClient;
const bridgeInterface = new WebViewBridgeInterface(this);
nativeView.addJavascriptInterface(bridgeInterface, "androidWebViewBridge");
nativeView.bridgeInterface = bridgeInterface;
}
disposeNativeView() {
const nativeView = this.nativeViewProtected;
if (nativeView) {
nativeView.client = null;
nativeView.chromeClient = null;
nativeView.destroy();
}
super.disposeNativeView();
}
async ensurePromiseSupport() {
if (android.os.Build.VERSION.SDK_INT >= 21) {
return;
}
return await super.ensurePromiseSupport();
}
_loadUrl(src) {
const nativeView = this.nativeViewProtected;
if (!nativeView) {
return;
}
this.writeTrace(`WebViewExt<android>._loadUrl("${src}")`);
nativeView.loadUrl(src);
this.writeTrace(`WebViewExt<android>._loadUrl("${src}") - end`);
}
_loadData(src) {
const nativeView = this.nativeViewProtected;
if (!nativeView) {
return;
}
const baseUrl = `file:///${knownFolders.currentApp().path}/`;
this.writeTrace(`WebViewExt<android>._loadData("${src}") -> baseUrl: "${baseUrl}"`);
nativeView.loadDataWithBaseURL(baseUrl, src, "text/html", "utf-8", null);
}
get canGoBack() {
const nativeView = this.nativeViewProtected;
if (nativeView) {
return nativeView.canGoBack();
}
return false;
}
stopLoading() {
const nativeView = this.nativeViewProtected;
if (nativeView) {
nativeView.stopLoading();
}
}
get canGoForward() {
const nativeView = this.nativeViewProtected;
if (nativeView) {
return nativeView.canGoForward();
}
return false;
}
goBack() {
const nativeView = this.nativeViewProtected;
if (nativeView) {
return nativeView.goBack();
}
}
goForward() {
const nativeView = this.nativeViewProtected;
if (nativeView) {
return nativeView.goForward();
}
}
reload() {
const nativeView = this.nativeViewProtected;
if (nativeView) {
return nativeView.reload();
}
}
registerLocalResource(resourceName, path) {
resourceName = this.fixLocalResourceName(resourceName);
const filepath = this.resolveLocalResourceFilePath(path);
if (!filepath) {
this.writeTrace(`WebViewExt<android>.registerLocalResource("${resourceName}", "${path}") -> file doesn't exist`, Trace.messageType.error);
return;
}
this.writeTrace(`WebViewExt<android>.registerLocalResource("${resourceName}", "${path}") -> file: "${filepath}"`);
this.localResourceMap.set(resourceName, filepath);
}
unregisterLocalResource(resourceName) {
this.writeTrace(`WebViewExt<android>.unregisterLocalResource("${resourceName}")`);
resourceName = this.fixLocalResourceName(resourceName);
this.localResourceMap.delete(resourceName);
}
getRegisteredLocalResource(resourceName) {
resourceName = this.fixLocalResourceName(resourceName);
const result = this.localResourceMap.get(resourceName);
this.writeTrace(`WebViewExt<android>.getRegisteredLocalResource("${resourceName}") => "${result}"`);
return result;
}
async ensureFetchSupport() {
this.writeTrace("WebViewExt<android>.ensureFetchSupport() - Override 'Fetch API' to support x-local.");
await this.executeJavaScript(`
try {
window.fetch = null;
} catch (err) {
console.error("null'ing Native Fetch API failed:", err);
}
`);
await this.loadFetchPolyfill();
}
async executeJavaScript(scriptCode) {
if (android.os.Build.VERSION.SDK_INT < 19) {
this.writeTrace(`WebViewExt<android>.executeJavaScript() -> SDK:${android.os.Build.VERSION.SDK_INT} not supported`, Trace.messageType.error);
return Promise.reject(new UnsupportedSDKError(19));
}
const result = await new Promise((resolve, reject) => {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
this.writeTrace(`WebViewExt<android>.executeJavaScript() -> no nativeView?`, Trace.messageType.error);
reject(new Error("Native Android not initialized, cannot call executeJavaScript"));
return;
}
androidWebView.evaluateJavascript(scriptCode, new android.webkit.ValueCallback({
onReceiveValue(result) {
resolve(result);
},
}));
});
return await this.parseWebViewJavascriptResult(result);
}
async getTitle() {
return this.nativeViewProtected && this.nativeViewProtected.getTitle();
}
zoomIn() {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return false;
}
return androidWebView.zoomIn();
}
zoomOut() {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return false;
}
return androidWebView.zoomOut();
}
zoomBy(zoomFactor) {
if (android.os.Build.VERSION.SDK_INT < 21) {
this.writeTrace(`WebViewExt<android>.zoomBy - not supported on this SDK`);
return;
}
if (!this.nativeViewProtected) {
return;
}
if (zoomFactor >= 0.01 && zoomFactor <= 100) {
return this.nativeViewProtected.zoomBy(zoomFactor);
}
throw new Error(`ZoomBy only accepts values between 0.01 and 100 both inclusive`);
}
[debugModeProperty.getDefault]() {
return false;
}
[debugModeProperty.setNative](enabled) {
android.webkit.WebView.setWebContentsDebuggingEnabled(!!enabled);
}
[builtInZoomControlsProperty.getDefault]() {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return false;
}
const settings = androidWebView.getSettings();
return settings.getBuiltInZoomControls();
}
[builtInZoomControlsProperty.setNative](enabled) {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return;
}
const settings = androidWebView.getSettings();
settings.setBuiltInZoomControls(!!enabled);
}
[displayZoomControlsProperty.getDefault]() {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return false;
}
const settings = androidWebView.getSettings();
return settings.getDisplayZoomControls();
}
[displayZoomControlsProperty.setNative](enabled) {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return;
}
const settings = androidWebView.getSettings();
settings.setDisplayZoomControls(!!enabled);
}
[cacheModeProperty.getDefault]() {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return null;
}
const settings = androidWebView.getSettings();
const cacheModeInt = settings.getCacheMode();
for (const [key, value] of cacheModeMap) {
if (value === cacheModeInt) {
return key;
}
}
return null;
}
[cacheModeProperty.setNative](cacheMode) {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return;
}
const settings = androidWebView.getSettings();
for (const [key, nativeValue] of cacheModeMap) {
if (key === cacheMode) {
settings.setCacheMode(nativeValue);
return;
}
}
}
[databaseStorageProperty.getDefault]() {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return false;
}
const settings = androidWebView.getSettings();
return settings.getDatabaseEnabled();
}
[databaseStorageProperty.setNative](enabled) {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return;
}
const settings = androidWebView.getSettings();
settings.setDatabaseEnabled(!!enabled);
}
[domStorageProperty.getDefault]() {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return false;
}
const settings = androidWebView.getSettings();
return settings.getDomStorageEnabled();
}
[domStorageProperty.setNative](enabled) {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return;
}
const settings = androidWebView.getSettings();
settings.setDomStorageEnabled(!!enabled);
}
[supportZoomProperty.getDefault]() {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return false;
}
const settings = androidWebView.getSettings();
return settings.supportZoom();
}
[supportZoomProperty.setNative](enabled) {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return;
}
const settings = androidWebView.getSettings();
settings.setSupportZoom(!!enabled);
}
[isEnabledProperty.setNative](enabled) {
const androidWebView = this.nativeViewProtected;
if (!androidWebView) {
return;
}
if (enabled) {
androidWebView.setOnTouchListener(null);
}
else {
androidWebView.setOnTouchListener(new android.view.View.OnTouchListener({
onTouch() {
return true;
},
}));
}
}
}
WebViewExt.supportXLocalScheme = true;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2Vidmlldy1leHQuYW5kcm9pZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIndlYnZpZXctZXh0LmFuZHJvaWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxLQUFLLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUMvRCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUNwRSxPQUFPLEVBQ0gsMkJBQTJCLEVBRTNCLGlCQUFpQixFQUNqQix1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLDJCQUEyQixFQUMzQixrQkFBa0IsRUFDbEIsbUJBQW1CLEVBQ25CLG1CQUFtQixFQUNuQixjQUFjLEdBQ2pCLE1BQU0sc0JBQXNCLENBQUM7QUFFOUIsY0FBYyxzQkFBc0IsQ0FBQztBQUVyQyxNQUFNLGFBQWEsR0FBRyxJQUFJLEdBQUcsQ0FBaUI7SUFDMUMsQ0FBQyxNQUFNLEVBQUUsV0FBVyxDQUFDO0lBQ3JCLENBQUMsS0FBSyxFQUFFLFdBQVcsQ0FBQztJQUNwQixDQUFDLE9BQU8sRUFBRSxXQUFXLENBQUM7SUFDdEIsQ0FBQyxNQUFNLEVBQUUsV0FBVyxDQUFDO0lBQ3JCLENBQUMsS0FBSyxFQUFFLFVBQVUsQ0FBQztJQUNuQixDQUFDLEtBQUssRUFBRSxXQUFXLENBQUM7SUFDcEIsQ0FBQyxNQUFNLEVBQUUsWUFBWSxDQUFDO0lBQ3RCLENBQUMsS0FBSyxFQUFFLFlBQVksQ0FBQztJQUNyQixDQUFDLElBQUksRUFBRSxpQkFBaUIsQ0FBQztJQUN6QixDQUFDLEtBQUssRUFBRSw2QkFBNkIsQ0FBQztJQUN0QyxDQUFDLEtBQUssRUFBRSxXQUFXLENBQUM7SUFDcEIsQ0FBQyxLQUFLLEVBQUUsZUFBZSxDQUFDO0lBQ3hCLENBQUMsS0FBSyxFQUFFLHdCQUF3QixDQUFDO0NBQ3BDLENBQUMsQ0FBQztBQUVILE1BQU0sbUJBQW1C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QUFHLGNBQWMsQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUM5QyxRQUFRLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN2QyxDQUFDO0lBRUQsQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLENBQUMsQ0FBQyxPQUFnQjtRQUMxQyxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsbUJBQW1CLENBQUM7UUFDaEQsSUFBSSxDQUFDLGNBQWMsRUFBRTtZQUNqQixPQUFPO1NBQ1Y7UUFFRCxJQUFJLE9BQU8sRUFBRTtZQUNULGNBQWMsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFLLENBQUMsQ0FBQztTQUM1QzthQUFNO1lBQ0gsY0FBYyxDQUFDLGtCQUFrQixDQUM3QixJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQztnQkFDbEMsT0FBTztvQkFDSCxPQUFPLElBQUksQ0FBQztnQkFDaEIsQ0FBQzthQUNKLENBQUMsQ0FDTCxDQUFDO1NBQ0w7SUFDTCxDQUFDOztBQS9hYSw4QkFBbUIsR0FBRyxJQUFJLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLy8gPHJlZmVyZW5jZSBwYXRoPVwiLi90eXBlcy9hbmRyb2lkL3dlYnZpZXdpbnRlcmZhY2UuZC50c1wiIC8+XG5cbmltcG9ydCBcIkBuYXRpdmVzY3JpcHQvY29yZVwiO1xuaW1wb3J0IHsgRmlsZSwga25vd25Gb2xkZXJzLCBUcmFjZSB9IGZyb20gXCJAbmF0aXZlc2NyaXB0L2NvcmVcIjtcbmltcG9ydCB7IGlzRW5hYmxlZFByb3BlcnR5IH0gZnJvbSBcIkBuYXRpdmVzY3JpcHQvY29yZS91aS9jb3JlL3ZpZXdcIjtcbmltcG9ydCB7XG4gICAgYnVpbHRJblpvb21Db250cm9sc1Byb3BlcnR5LFxuICAgIENhY2hlTW9kZSxcbiAgICBjYWNoZU1vZGVQcm9wZXJ0eSxcbiAgICBkYXRhYmFzZVN0b3JhZ2VQcm9wZXJ0eSxcbiAgICBkZWJ1Z01vZGVQcm9wZXJ0eSxcbiAgICBkaXNwbGF5Wm9vbUNvbnRyb2xzUHJvcGVydHksXG4gICAgZG9tU3RvcmFnZVByb3BlcnR5LFxuICAgIHN1cHBvcnRab29tUHJvcGVydHksXG4gICAgVW5zdXBwb3J0ZWRTREtFcnJvcixcbiAgICBXZWJWaWV3RXh0QmFzZSxcbn0gZnJvbSBcIi4vd2Vidmlldy1leHQtY29tbW9uXCI7XG5cbmV4cG9ydCAqIGZyb20gXCIuL3dlYnZpZXctZXh0LWNvbW1vblwiO1xuXG5jb25zdCBleHRUb01pbWVUeXBlID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZz4oW1xuICAgIFtcImh0bWxcIiwgXCJ0ZXh0L2h0bWxcIl0sXG4gICAgW1wiaHRtXCIsIFwidGV4dC9odG1sXCJdLFxuICAgIFtcInhodG1sXCIsIFwidGV4dC9odG1sXCJdLFxuICAgIFtcInhodG1cIiwgXCJ0ZXh0L2h0bWxcIl0sXG4gICAgW1wiY3NzXCIsIFwidGV4dC9jc3NcIl0sXG4gICAgW1wiZ2lmXCIsIFwiaW1hZ2UvZ2lmXCJdLFxuICAgIFtcImpwZWdcIiwgXCJpbWFnZS9qcGVnXCJdLFxuICAgIFtcImpwZ1wiLCBcImltYWdlL2pwZWdcIl0sXG4gICAgW1wianNcIiwgXCJ0ZXh0L2phdmFzY3JpcHRcIl0sXG4gICAgW1wib3RmXCIsIFwiYXBwbGljYXRpb24vdm5kLm1zLW9wZW50eXBlXCJdLFxuICAgIFtcInBuZ1wiLCBcImltYWdlL3BuZ1wiXSxcbiAgICBbXCJzdmdcIiwgXCJpbWFnZS9zdmcreG1sXCJdLFxuICAgIFtcInR0ZlwiLCBcImFwcGxpY2F0aW9uL3gtZm9udC10dGZcIl0sXG5dKTtcblxuY29uc3QgZXh0VG9CaW5hcnlFbmNvZGluZyA9IG5ldyBTZXQ8c3RyaW5nPihbXCJnaWZcIiwgXCJqcGVnXCIsIFwianBnXCIsIFwib3RmXCIsIFwicG5nXCIsIFwidHRmXCJdKTtcblxuLy8jcmVnaW9uIGFuZHJvaWRfbmF0aXZlX2NsYXNzZXNcbmxldCBjYWNoZU1vZGVNYXA6IE1hcDxDYWNoZU1vZGUsIG51bWJlcj47XG5cbmV4cG9ydCBpbnRlcmZhY2UgQW5kcm9pZFdlYlZpZXdDbGllbnQgZXh0ZW5kcyBhbmRyb2lkLndlYmtpdC5XZWJWaWV3Q2xpZW50IHt9XG5cbmV4cG9ydCBpbnRlcmZhY2UgQW5kcm9pZFdlYlZpZXcgZXh0ZW5kcyBhbmRyb2lkLndlYmtpdC5XZWJWaWV3IHtcbiAgICBjbGllbnQ6IEFuZHJvaWRXZWJWaWV3Q2xpZW50IHwgbnVsbDtcbiAgICBjaHJvbWVDbGllbnQ6IGFuZHJvaWQud2Via2l0LldlYkNocm9tZUNsaWVudCB8IG51bGw7XG4gICAgYnJpZGdlSW50ZXJmYWNlPzogZGsubm90YS53ZWJ2aWV3aW50ZXJmYWNlLldlYlZpZXdCcmlkZ2VJbnRlcmZhY2U7XG59XG5cbmxldCBXZWJWaWV3RXh0Q2xpZW50OiBuZXcgKG93bmVyOiBXZWJWaWV3RXh0KSA9PiBBbmRyb2lkV2ViVmlld0NsaWVudDtcbmxldCBXZWJDaHJvbWVWaWV3RXh0Q2xpZW50OiBuZXcgKG93bmVyOiBXZWJWaWV3RXh0KSA9PiBhbmRyb2lkLndlYmtpdC5XZWJDaHJvbWVDbGllbnQ7XG5sZXQgV2ViVmlld0JyaWRnZUludGVyZmFjZTogbmV3IChvd25lcjogV2ViVmlld0V4dCkgPT4gZGsubm90YS53ZWJ2aWV3aW50ZXJmYWNlLldlYlZpZXdCcmlkZ2VJbnRlcmZhY2U7XG5cbmZ1bmN0aW9uIGluaXRpYWxpemVXZWJWaWV3Q2xpZW50KCk6IHZvaWQge1xuICAgIGlmIChXZWJWaWV3RXh0Q2xpZW50KSB7XG4gICAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjYWNoZU1vZGVNYXAgPSBuZXcgTWFwPENhY2hlTW9kZSwgbnVtYmVyPihbXG4gICAgICAgIFtcImNhY2hlX2ZpcnN0XCIsIGFuZHJvaWQud2Via2l0LldlYlNldHRpbmdzLkxPQURfQ0FDSEVfRUxTRV9ORVRXT1JLXSxcbiAgICAgICAgW1wiY2FjaGVfb25seVwiLCBhbmRyb2lkLndlYmtpdC5XZWJTZXR0aW5ncy5MT0FEX0NBQ0hFX09OTFldLFxuICAgICAgICBbXCJkZWZhdWx0XCIsIGFuZHJvaWQud2Via2l0LldlYlNldHRpbmdzLkxPQURfREVGQVVMVF0sXG4gICAgICAgIFtcIm5vX2NhY2hlXCIsIGFuZHJvaWQud2Via2l0LldlYlNldHRpbmdzLkxPQURfTk9fQ0FDSEVdLFxuICAgICAgICBbXCJub3JtYWxcIiwgYW5kcm9pZC53ZWJraXQuV2ViU2V0dGluZ3MuTE9BRF9OT1JNQUxdLFxuICAgIF0pO1xuXG4gICAgQE5hdGl2ZUNsYXNzKClcbiAgICBjbGFzcyBXZWJWaWV3RXh0Q2xpZW50SW1wbCBleHRlbmRzIGFuZHJvaWQud2Via2l0LldlYlZpZXdDbGllbnQge1xuICAgICAgICBwcml2YXRlIG93bmVyOiBXZWFrUmVmPFdlYlZpZXdFeHQ+O1xuICAgICAgICBjb25zdHJ1Y3Rvcihvd25lcjogV2ViVmlld0V4dCkge1xuICAgICAgICAgICAgc3VwZXIoKTtcblxuICAgICAgICAgICAgdGhpcy5vd25lciA9IG5ldyBXZWFrUmVmKG93bmVyKTtcblxuICAgICAgICAgICAgcmV0dXJuIGdsb2JhbC5fX25hdGl2ZSh0aGlzKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBHaXZlIHRoZSBob3N0IGFwcGxpY2F0aW9uIGEgY2hhbmNlIHRvIHRha2UgY29udHJvbCB3aGVuIGEgVVJMIGlzIGFib3V0IHRvIGJlIGxvYWRlZCBpbiB0aGUgY3VycmVudCBXZWJWaWV3LlxuICAgICAgICAgKi9cbiAgICAgICAgcHVibGljIHNob3VsZE92ZXJyaWRlVXJsTG9hZGluZyh2aWV3OiBhbmRyb2lkLndlYmtpdC5XZWJWaWV3LCByZXF1ZXN0OiBzdHJpbmcgfCBhbmRyb2lkLndlYmtpdC5XZWJSZXNvdXJjZVJlcXVlc3QpIHtcbiAgICAgICAgICAgIGNvbnN0IG93bmVyID0gdGhpcy5vd25lci5nZXQoKTtcbiAgICAgICAgICAgIGlmICghb3duZXIpIHtcbiAgICAgICAgICAgICAgICBjb25zb2xlLndhcm4oXCJXZWJWaWV3RXh0Q2xpZW50SW1wbC5zaG91bGRPdmVycmlkZVVybExvYWRpbmcoLi4uKSAtIG5vIG93bmVyXCIpO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGxldCB1cmwgPSByZXF1ZXN0IGFzIHN0cmluZztcbiAgICAgICAgICAgIGxldCBodHRwTWV0aG9kID0gXCJHRVRcIjtcbiAgICAgICAgICAgIGxldCBpc1JlZGlyZWN0ID0gZmFsc2U7XG4gICAgICAgICAgICBsZXQgaGFzR2VzdHVyZSA9IGZhbHNlO1xuICAgICAgICAgICAgbGV0IGlzRm9yTWFpbkZyYW1lID0gZmFsc2U7XG4gICAgICAgICAgICBsZXQgcmVxdWVzdEhlYWRlcnM6IGphdmEudXRpbC5NYXA8c3RyaW5nLCBzdHJpbmc+IHwgbnVsbCA9IG51bGw7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHJlcXVlc3QgPT09IFwib2JqZWN0XCIpIHtcbiAgICAgICAgICAgICAgICBodHRwTWV0aG9kID0gcmVxdWVzdC5nZXRNZXRob2QoKTtcbiAgICAgICAgICAgICAgICBpc1JlZGlyZWN0ID0gcmVxdWVzdC5pc1JlZGlyZWN0KCk7XG4gICAgICAgICAgICAgICAgaGFzR2VzdHVyZSA9IHJlcXVlc3QuaGFzR2VzdHVyZSgpO1xuICAgICAgICAgICAgICAgIGlzRm9yTWFpbkZyYW1lID0gcmVxdWVzdC5pc0Zvck1haW5GcmFtZSgpO1xuICAgICAgICAgICAgICAgIHJlcXVlc3RIZWFkZXJzID0gcmVxdWVzdC5nZXRSZXF1ZXN0SGVhZGVycygpO1xuXG4gICAgICAgICAgICAgICAgdXJsID0gcmVxdWVzdC5nZXRVcmwoKS50b1N0cmluZygpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBvd25lci53cml0ZVRyYWNlKFxuICAgICAgICAgICAgICAgIGBXZWJWaWV3Q2xpZW50Q2xhc3Muc2hvdWxkT3ZlcnJpZGVVcmxMb2FkaW5nKFwiJHt1cmx9XCIpIC0gbWV0aG9kOiR7aHR0cE1ldGhvZH0gaXNSZWRpcmVjdDoke2lzUmVkaXJlY3R9IGhhc0dlc3R1cmU6JHtoYXNHZXN0dXJlfSBpc0Zvck1haW5GcmFtZToke2lzRm9yTWFpbkZyYW1lfSBoZWFkZXJzOiR7cmVxdWVzdEhlYWRlcnN9YCxcbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIGlmICh1cmwuc3RhcnRzV2l0aChvd25lci5pbnRlcmNlcHRTY2hlbWUpKSB7XG4gICAgICAgICAgICAgICAgb3duZXIud3JpdGVUcmFjZShgV2ViVmlld0NsaWVudENsYXNzLnNob3VsZE92ZXJyaWRlVXJsTG9hZGluZyhcIiR7dXJsfVwiKSAtIFwiJHtvd25lci5pbnRlcmNlcHRTY2hlbWV9XCIgLSBjYW5jZWxgKTtcblxuICAgICAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBzaG91bGRPdmVycmlkZVVybExvYWRpbmcgPSBvd25lci5fb25TaG91bGRPdmVycmlkZVVybExvYWRpbmcodXJsLCBodHRwTWV0aG9kKTtcbiAgICAgICAgICAgIGlmIChzaG91bGRPdmVycmlkZVVybExvYWRpbmcgPT09IHRydWUpIHtcbiAgICAgICAgICAgICAgICBvd25lci53cml0ZVRyYWNlKGBXZWJWaWV3Q2xpZW50Q2xhc3Muc2hvdWxkT3ZlcnJpZGVVcmxMb2FkaW5nKFwiJHt1cmx9XCIpIC0gY2FuY2VsIGxvYWRpbmcgdXJsYCk7XG5cbiAgICAgICAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgcHVibGljIHNob3VsZEludGVyY2VwdFJlcXVlc3QodmlldzogYW5kcm9pZC53ZWJraXQuV2ViVmlldywgcmVxdWVzdDogc3RyaW5nIHwgYW5kcm9pZC53ZWJraXQuV2ViUmVzb3VyY2VSZXF1ZXN0KSB7XG4gICAgICAgICAgICBjb25zdCBvd25lciA9IHRoaXMub3duZXIuZ2V0KCk7XG4gICAgICAgICAgICBpZiAoIW93bmVyKSB7XG4gICAgICAgICAgICAgICAgY29uc29sZS53YXJuKFwiV2ViVmlld0V4dENsaWVudEltcGwuc2hvdWxkSW50ZXJjZXB0UmVxdWVzdCguLi4pIC0gbm8gb3duZXJcIik7XG5cbiAgICAgICAgICAgICAgICByZXR1cm4gc3VwZXIuc2hvdWxkSW50ZXJjZXB0UmVxdWVzdCh2aWV3LCByZXF1ZXN0IGFzIGFuZHJvaWQud2Via2l0LldlYlJlc291cmNlUmVxdWVzdCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGxldCB1cmw6IHN0cmluZyB8IHZvaWQ7XG4gICAgICAgICAgICBpZiAodHlwZW9mIHJlcXVlc3QgPT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICAgICAgICB1cmwgPSByZXF1ZXN0O1xuICAgICAgICAgICAgfSBlbHNlIGlmICh0eXBlb2YgcmVxdWVzdCA9PT0gXCJvYmplY3RcIikge1xuICAgICAgICAgICAgICAgIHVybCA9IHJlcXVlc3QuZ2V0VXJsKCkudG9TdHJpbmcoKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgaWYgKHR5cGVvZiB1cmwgIT09IFwic3RyaW5nXCIpIHtcbiAgICAgICAgICAgICAgICBvd25lci53cml0ZVRyYWNlKGBXZWJWaWV3Q2xpZW50Q2xhc3Muc2hvdWxkSW50ZXJjZXB0UmVxdWVzdChcIiR7dXJsfVwiKSAtIGlzIG5vdCBhIHN0cmluZ2ApO1xuXG4gICAgICAgICAgICAgICAgcmV0dXJuIHN1cGVyLnNob3VsZEludGVyY2VwdFJlcXVlc3QodmlldywgcmVxdWVzdCBhcyBhbmRyb2lkLndlYmtpdC5XZWJSZXNvdXJjZVJlcXVlc3QpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoIXVybC5zdGFydHNXaXRoKG93bmVyLmludGVyY2VwdFNjaGVtZSkpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gc3VwZXIuc2hvdWxkSW50ZXJjZXB0UmVxdWVzdCh2aWV3LCByZXF1ZXN0IGFzIGFuZHJvaWQud2Via2l0LldlYlJlc291cmNlUmVxdWVzdCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGNvbnN0IGZpbGVwYXRoID0gb3duZXIuZ2V0UmVnaXN0ZXJlZExvY2FsUmVzb3VyY2UodXJsKTtcbiAgICAgICAgICAgIGlmICghZmlsZXBhdGgpIHtcbiAgICAgICAgICAgICAgICBvd25lci53cml0ZVRyYWNlKGBXZWJWaWV3Q2xpZW50Q2xhc3Muc2hvdWxkSW50ZXJjZXB0UmVxdWVzdChcIiR7dXJsfVwiKSAtI