1
0
Fork 0
EdikoyoWiki-n04/Edikoyo/.obsidian/plugins/privacy-glasses/main.js

508 lines
99 KiB
JavaScript
Raw Normal View History

2024-09-07 07:56:20 +00:00
/*
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
if you want to view the source visit the plugins github repository
*/
'use strict';
var obsidian = require('obsidian');
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
/*
Privacy Glasses plugin for Obsidian
Copyright 2021 Jill Alberts
Licensed under the MIT License (http://opensource.org/licenses/MIT)
*/
function isMarkdownFileInfoView(x) {
const anyX = x;
return !!Object.getOwnPropertyDescriptor(anyX, "file");
}
function isHooked(view) {
const anyView = view;
const ownProps = Object.getOwnPropertyNames(anyView);
return (ownProps.contains("setState") && typeof anyView.setState === "function");
}
function hookViewStateChanged(view, onBeforeStateChange, onAfterStateChange) {
const anyView = view;
const original = anyView.__proto__.setState;
function wrapper() {
onBeforeStateChange(view);
const r = original.apply(this, arguments);
if (typeof r.then === "function") {
r.then(() => {
onAfterStateChange(view);
});
}
else {
onAfterStateChange(view);
}
return r;
}
anyView.setState = wrapper.bind(view);
return anyView;
}
/**
* Constants
*/
var Level;
(function (Level) {
Level["HideAll"] = "hide-all";
Level["HidePrivate"] = "hide-private";
Level["RevealAll"] = "reveal-all";
Level["RevealHeadlines"] = "reveal-headlines";
})(Level || (Level = {}));
var CssClass;
(function (CssClass) {
CssClass["BlurAll"] = "privacy-glasses-blur-all";
CssClass["RevealOnHover"] = "privacy-glasses-reveal-on-hover";
CssClass["RevealAll"] = "privacy-glasses-reveal-all";
CssClass["RevealUnderCaret"] = "privacy-glasses-reveal-under-caret";
CssClass["RevealHeadlines"] = "privacy-glasses-reveal-headlines";
CssClass["Reveal"] = "privacy-glasses-reveal";
CssClass["IsMdView"] = "is-md-view";
CssClass["IsNonMdView"] = "is-non-md-view";
CssClass["IsMdViewHeadlinesOnly"] = "is-md-view-headlines-only";
CssClass["PrivacyGlassesReveal"] = "privacy-glasses-reveal";
})(CssClass || (CssClass = {}));
/**
* Main
*/
class PrivacyGlassesPlugin extends obsidian.Plugin {
constructor() {
super(...arguments);
this.revealed = [];
}
onload() {
return __awaiter(this, void 0, void 0, function* () {
this.statusBar = this.addStatusBarItem();
yield this.loadSettings();
this.addSettingTab(new privacyGlassesSettingTab(this.app, this));
obsidian.addIcon("eye", eyeIcon);
obsidian.addIcon("eye-closed", eyeClosedIcon);
obsidian.addIcon("eye-slash", eyeSlashIcon);
obsidian.addIcon("eye-glasses", eyeGlasses);
this.addRibbonIcon("eye-closed", "Hide all", () => {
this.currentLevel = Level.HideAll;
this.updateLeavesAndGlobalReveals();
});
this.addRibbonIcon("eye-slash", "Reveal non-private", () => {
this.currentLevel = Level.HidePrivate;
this.updateLeavesAndGlobalReveals();
});
this.addRibbonIcon("eye-glasses", "Reveal headlines only", () => {
this.currentLevel = Level.RevealHeadlines;
this.updateLeavesAndGlobalReveals();
});
this.addRibbonIcon("eye", "Reveal all", () => {
this.currentLevel = Level.RevealAll;
this.updateLeavesAndGlobalReveals();
});
this.addCommand({
id: "privacy-glasses-hide-all",
name: "Privacy Glasses - hide all",
callback: () => {
this.currentLevel = Level.HideAll;
this.updateLeavesAndGlobalReveals();
},
});
this.addCommand({
id: "privacy-glasses-hide-private",
name: "Privacy Glasses - hide files in folders marked as private",
callback: () => {
this.currentLevel = Level.HidePrivate;
this.updateLeavesAndGlobalReveals();
},
});
this.addCommand({
id: "privacy-glasses-reveal-headlines",
name: "Privacy Glasses - reveal headlines only, keeping body content hidden",
callback: () => {
this.currentLevel = Level.RevealHeadlines;
this.updateLeavesAndGlobalReveals();
},
});
this.addCommand({
id: "privacy-glasses-reveal-all",
name: "Privacy Glasses - do not hide anything",
callback: () => {
this.currentLevel = Level.RevealAll;
this.updateLeavesAndGlobalReveals();
},
});
this.registerInterval(window.setInterval(() => {
this.checkIdleTimeout();
}, 1000));
this.app.workspace.onLayoutReady(() => {
this.registerDomActivityEvents(this.app.workspace.rootSplit.win);
this.currentLevel = this.settings.blurOnStartup;
this.updateLeavesAndGlobalReveals();
this.updatePrivateDirsEl(this.app.workspace.rootSplit.win.document);
this.ensureLeavesHooked();
});
this.registerEvent(this.app.workspace.on("window-open", (win) => {
this.registerDomActivityEvents(win.win);
}));
this.registerEvent(this.app.workspace.on("active-leaf-change", (e) => {
this.ensureLeavesHooked();
this.updateLeafViewStyle(e.view);
}));
this.lastEventTime = performance.now();
});
}
// we hook into setState function of the view, because it is synchronously called
// before the content switch. this is to prevent private content from being accidentally briefly revealed
onBeforeViewStateChange(l) {
this.revealed.forEach((r) => {
r.removeClass(CssClass.Reveal);
});
}
onAfterViewStateChange(l) {
// some panels update using the same event, so it is important to update leaves after they are ready
setTimeout(() => {
this.updateLeavesStyle();
}, 200);
this.ensureLeavesHooked();
}
ensureLeavesHooked() {
this.app.workspace.iterateAllLeaves((e) => {
if (isHooked(e.view)) {
return;
}
hookViewStateChanged(e.view, () => {
this.onBeforeViewStateChange(e);
}, () => {
this.onAfterViewStateChange(e);
});
});
}
registerDomActivityEvents(win) {
this.registerDomEvent(win, "mousedown", (e) => {
this.lastEventTime = e.timeStamp;
});
this.registerDomEvent(win, "keydown", (e) => {
this.lastEventTime = e.timeStamp;
});
this.addBlurLevelEl(win.document);
}
checkIdleTimeout() {
if (this.settings.blurOnIdleTimeoutSeconds < 0) {
return;
}
if (this.currentLevel === Level.HideAll) {
return;
}
if (!this.lastEventTime) {
return;
}
const now = performance.now();
if ((now - this.lastEventTime) / 1000 >=
this.settings.blurOnIdleTimeoutSeconds) {
this.currentLevel = Level.HideAll;
this.updateLeavesAndGlobalReveals();
}
}
onunload() {
return __awaiter(this, void 0, void 0, function* () {
this.statusBar.remove();
yield this.saveSettings();
});
}
loadSettings() {
return __awaiter(this, void 0, void 0, function* () {
this.settings = Object.assign(DEFAULT_SETTINGS, yield this.loadData());
});
}
saveSettings() {
return __awaiter(this, void 0, void 0, function* () {
yield this.saveData(this.settings);
});
}
shouldRevealLeaf(view) {
var _a;
if (this.currentLevel === Level.RevealAll) {
return true;
}
if (this.currentLevel === Level.HideAll ||
this.currentLevel === Level.RevealHeadlines) {
return false;
}
if (!isMarkdownFileInfoView(view)) {
return true;
}
if (view.editor &&
this.settings.privateNoteMarker &&
this.settings.privateNoteMarker !== "") {
let tags = [];
// Get tags in the note body, if any
if ('tags' in this.app.metadataCache.getFileCache(view.file)) {
tags.push(...this.app.metadataCache.getFileCache(view.file).tags.filter(x => !!x.tag).map(x => x.tag));
}
// Get tags in properties, if any
if ('tags' in ((_a = this.app.metadataCache.getFileCache(view.file)) === null || _a === void 0 ? void 0 : _a.frontmatter)) {
tags.push(...this.app.metadataCache.getFileCache(view.file).frontmatter.tags.filter((x) => !!x));
}
if (tags && tags.length > 0) {
return !tags.includes(this.settings.privateNoteMarker);
}
}
if (view.file &&
!this.settings.privateDirs.contains(view.file.parent.path)) {
return true;
}
return false;
}
updateLeafViewStyle(view) {
const isMd = isMarkdownFileInfoView(view) && view.editor;
view.containerEl.removeClass(CssClass.IsMdView, CssClass.IsNonMdView, CssClass.IsMdViewHeadlinesOnly);
if (isMd && this.currentLevel === Level.RevealHeadlines) {
view.containerEl.addClass(CssClass.IsMdViewHeadlinesOnly);
}
else if (isMd) {
view.containerEl.addClass(CssClass.IsMdView);
}
else {
view.containerEl.addClass(CssClass.IsNonMdView);
}
const shouldReveal = this.shouldRevealLeaf(view);
if (shouldReveal) {
view.containerEl.addClass(CssClass.PrivacyGlassesReveal);
this.revealed.push(view.containerEl);
}
else {
view.containerEl.removeClass(CssClass.PrivacyGlassesReveal);
}
}
updateLeavesAndGlobalReveals() {
this.updateLeavesStyle();
this.updateGlobalRevealStyle();
}
updateLeavesStyle() {
this.app.workspace.iterateAllLeaves((e) => {
this.updateLeafViewStyle(e.view);
});
}
updateGlobalRevealStyle() {
this.removeAllClasses();
this.setClassToDocumentBody(this.currentLevel);
if (this.settings.hoverToReveal) {
document.body.classList.add(CssClass.RevealOnHover);
}
if (this.settings.revealUnderCaret) {
document.body.classList.add(CssClass.RevealUnderCaret);
}
}
removeAllClasses() {
document.body.removeClass(CssClass.BlurAll, CssClass.RevealOnHover, CssClass.RevealAll, CssClass.RevealUnderCaret, CssClass.RevealHeadlines);
}
setClassToDocumentBody(currentLevel) {
switch (currentLevel) {
case Level.HideAll:
document.body.classList.add(CssClass.BlurAll);
break;
case Level.RevealAll:
document.body.classList.add(CssClass.RevealAll);
break;
case Level.RevealHeadlines:
document.body.classList.add(CssClass.RevealHeadlines);
break;
}
}
addBlurLevelEl(doc) {
this.blurLevelStyleEl = doc.createElement("style");
this.blurLevelStyleEl.id = "privacyGlassesBlurLevel";
doc.head.appendChild(this.blurLevelStyleEl);
this.updateBlurLevelEl();
}
updateBlurLevelEl() {
if (!this.blurLevelStyleEl) {
return;
}
this.blurLevelStyleEl.textContent = `body {--blurLevel:${this.settings.blurLevel}em};`;
}
updatePrivateDirsEl(doc) {
if (doc && !this.privateDirsStyleEl) {
this.privateDirsStyleEl = doc.createElement("style");
this.privateDirsStyleEl.id = "privacyGlassesDirBlur";
doc.head.appendChild(this.privateDirsStyleEl);
}
const dirs = this.settings.privateDirs.split(",");
this.privateDirsStyleEl.textContent = dirs
.map((d) => `
:is(.nav-folder-title, .nav-file-title)[data-path^=${d}] {filter: blur(calc(var(--blurLevel) * 1))}
:is(.nav-folder-title, .nav-file-title)[data-path^=${d}]:hover {filter: unset}
.privacy-glasses-reveal-all :is(.nav-folder-title, .nav-file-title)[data-path^=${d}] {filter: unset}
`)
.join("");
}
}
const DEFAULT_SETTINGS = {
blurOnStartup: Level.HidePrivate,
blurLevel: 0.3,
blurOnIdleTimeoutSeconds: -1,
hoverToReveal: true,
revealUnderCaret: false,
privateDirs: "",
privateNoteMarker: "#private",
};
class privacyGlassesSettingTab extends obsidian.PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.plugin = plugin;
}
display() {
let { containerEl } = this;
containerEl.empty();
containerEl.createEl("h3", {
text: "Privacy Glasses v" + this.plugin.manifest.version,
});
containerEl.createEl("a", {
text: "https://github.com/jillalberts/privacy-glasses",
href: "https://github.com/jillalberts/privacy-glasses",
});
containerEl.createEl("span", {
text: ": documentation, report issues, contact info",
});
containerEl.createEl("p", {
text: 'To activate/deactivate Privacy Glasses, click the glasses icon on the left-hand ribbon or run "Privacy Glasses" commands in the Command Palette (Ctrl-P). The command can also be bound to a keyboard shortcut if you wish.',
});
new obsidian.Setting(containerEl)
.setName("Activate Privacy Glasses on startup")
.setDesc("Indicates whether the plugin is automatically activated when starting Obsidian.")
.addDropdown((toggle) => {
toggle.addOptions({
"hide-all": "Hide all",
"hide-private": "Hide private (default)",
"reveal-all": "Reveal all",
"reveal-headlines": "Reveal headlines only"
});
toggle.setValue(this.plugin.settings.blurOnStartup);
toggle.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.blurOnStartup = value;
yield this.plugin.saveSettings();
}));
});
new obsidian.Setting(containerEl)
.setName("Hide all after user inactivity (seconds)")
.setDesc("Inactivity time after which Privacy Glasses will hide all. -1 to disable auto-hiding.")
.addText((textfield) => {
textfield.setPlaceholder("-1");
textfield.inputEl.type = "number";
textfield.inputEl.min = "-1";
textfield.setValue(String(this.plugin.settings.blurOnIdleTimeoutSeconds));
textfield.onChange((value) => __awaiter(this, void 0, void 0, function* () {
let parsed = parseFloat(value);
if (isNaN(parsed)) {
parsed = -1;
}
this.plugin.settings.blurOnIdleTimeoutSeconds = parsed;
yield this.plugin.saveSettings();
}));
});
new obsidian.Setting(containerEl)
.setName("Hover to reveal")
.setDesc("Indicates whether or not to reveal content when hovering the cursor over it.")
.addToggle((toggle) => {
toggle.setValue(this.plugin.settings.hoverToReveal);
toggle.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.hoverToReveal = value;
this.plugin.updateLeavesAndGlobalReveals();
yield this.plugin.saveSettings();
}));
});
new obsidian.Setting(containerEl)
.setName("Reveal under caret")
.setDesc("Indicates whether or not to reveal content when caret is on it.")
.addToggle((toggle) => {
toggle.setValue(this.plugin.settings.revealUnderCaret);
toggle.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.revealUnderCaret = value;
this.plugin.updateGlobalRevealStyle();
yield this.plugin.saveSettings();
}));
});
var sliderEl = new obsidian.Setting(containerEl);
let sliderElDesc = "Higher is blurrier. Default=60, current=";
sliderEl
.setName("Blur level")
.setDesc(sliderElDesc + Math.round(this.plugin.settings.blurLevel * 100))
// ^ need rounding to not show values like '55.00000000000001'
.addSlider((slider) => slider
.setLimits(0.1, 1.5, 0.05)
.setValue(this.plugin.settings.blurLevel)
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.blurLevel = value;
sliderEl.setDesc(sliderElDesc + Math.round(this.plugin.settings.blurLevel * 100));
this.plugin.updateBlurLevelEl();
this.plugin.saveSettings();
})));
new obsidian.Setting(containerEl)
.setName("Private directories")
.setDesc("Comma-separated list of directories, in which files are considered private")
.addText((text) => text
.setPlaceholder("finance,therapy")
.setValue(this.plugin.settings.privateDirs)
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.privateDirs = value;
yield this.plugin.saveSettings();
this.plugin.updateLeavesAndGlobalReveals();
this.plugin.updatePrivateDirsEl();
})));
new obsidian.Setting(containerEl)
.setName("Private note marker")
.setDesc("Start a note with this text to mark note as private")
.addText((text) => text
.setPlaceholder("#private")
.setValue(this.plugin.settings.privateNoteMarker)
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.privateNoteMarker = value;
yield this.plugin.saveSettings();
this.plugin.updateLeavesStyle();
})));
}
}
// https://icon-sets.iconify.design/ph/eye-slash/
const eyeSlashIcon = `<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="currentColor" d="M53.9 34.6a8 8 0 0 0-11.8 10.8l19.2 21.1C25 88.8 9.4 123.2 8.7 124.8a8.2 8.2 0 0 0 0 6.5c.3.7 8.8 19.5 27.6 38.4c25.1 25 56.8 38.3 91.7 38.3a128.6 128.6 0 0 0 52.1-10.8l22 24.2a8 8 0 0 0 5.9 2.6a8.2 8.2 0 0 0 5.4-2.1a7.9 7.9 0 0 0 .5-11.3Zm47.3 75.9l41.7 45.8A31.6 31.6 0 0 1 128 160a32 32 0 0 1-26.8-49.5ZM128 192c-30.8 0-57.7-11.2-79.9-33.3A128.3 128.3 0 0 1 25 128c4.7-8.8 19.8-33.5 47.3-49.4l18 19.8a48 48 0 0 0 63.6 70l14.7 16.2A112.1 112.1 0 0 1 128 192Zm119.3-60.7c-.4.9-10.5 23.3-33.4 43.8a8.1 8.1 0 0 1-5.3 2a7.6 7.6 0 0 1-5.9-2.7a8 8 0 0 1 .6-11.3A131 131 0 0 0 231 128a130.3 130.3 0 0 0-23.1-30.8C185.7 75.2 158.8 64 128 64a112.9 112.9 0 0 0-19.4 1.6a8.1 8.1 0 0 1-9.2-6.6a8 8 0 0 1 6.6-9.2a132.4 132.4 0 0 1 22-1.8c34.9 0 66.6 13.3 91.7 38.3c18.8 18.9 27.3 37.7 27.6 38.5a8.2 8.2 0 0 1 0 6.5ZM134 96.6a8 8 0 0 1 3-15.8a48.3 48.3 0 0 1 38.8 42.7a8 8 0 0 1-7.2 8.7h-.8a7.9 7.9 0 0 1-7.9-7.2A32.2 32.2 0 0 0 134 96.6Z"/></svg>`;
// https://icon-sets.iconify.design/ph/eye-closed-bold/
const eyeClosedIcon = `<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="currentColor" d="M234.4 160.8a12 12 0 0 1-10.4 18a11.8 11.8 0 0 1-10.4-6l-16.3-28.2a126 126 0 0 1-29.4 13.5l5.2 29.4a11.9 11.9 0 0 1-9.7 13.9l-2.1.2a12 12 0 0 1-11.8-9.9l-5.1-28.7a123.5 123.5 0 0 1-16.4 1a146.3 146.3 0 0 1-16.5-1l-5.1 28.7a12 12 0 0 1-11.8 9.9l-2.1-.2a11.9 11.9 0 0 1-9.7-13.9l5.2-29.4a125.3 125.3 0 0 1-29.3-13.5L42.3 173a12.1 12.1 0 0 1-10.4 6a11.7 11.7 0 0 1-6-1.6a12 12 0 0 1-4.4-16.4l17.9-31a142.4 142.4 0 0 1-16.7-17.6a12 12 0 1 1 18.6-15.1C57.1 116.8 84.9 140 128 140s70.9-23.2 86.7-42.7a12 12 0 1 1 18.6 15.1a150.3 150.3 0 0 1-16.7 17.7Z"/></svg>`;
// https://icon-sets.iconify.design/ph/eye/
const eyeIcon = `<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="currentColor" d="M247.3 124.8c-.3-.8-8.8-19.6-27.6-38.5C194.6 61.3 162.9 48 128 48S61.4 61.3 36.3 86.3C17.5 105.2 9 124 8.7 124.8a7.9 7.9 0 0 0 0 6.4c.3.8 8.8 19.6 27.6 38.5c25.1 25 56.8 38.3 91.7 38.3s66.6-13.3 91.7-38.3c18.8-18.9 27.3-37.7 27.6-38.5a7.9 7.9 0 0 0 0-6.4ZM128 192c-30.8 0-57.7-11.2-79.9-33.3A130.3 130.3 0 0 1 25 128a130.3 130.3 0 0 1 23.1-30.8C70.3 75.2 97.2 64 128 64s57.7 11.2 79.9 33.2A130.3 130.3 0 0 1 231 128c-7.2 13.5-38.6 64-103 64Zm0-112a48 48 0 1 0 48 48a48 48 0 0 0-48-48Zm0 80a32 32 0 1 1 32-32a32.1 32.1 0 0 1-32 32Z"/></svg>`;
// https://icon-sets.iconify.design/ph/eyeglasses/
const eyeGlasses = `<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path fill="currentColor" d="M200 40a8 8 0 0 0 0 16a16 16 0 0 1 16 16v58.08A44 44 0 0 0 145.68 152h-35.36A44 44 0 0 0 40 130.08V72a16 16 0 0 1 16-16a8 8 0 0 0 0-16a32 32 0 0 0-32 32v92a44 44 0 0 0 87.81 4h32.38a44 44 0 0 0 87.81-4V72a32 32 0 0 0-32-32ZM68 192a28 28 0 1 1 28-28a28 28 0 0 1-28 28Zm120 0a28 28 0 1 1 28-28a28 28 0 0 1-28 28Z"/></svg>`;
module.exports = PrivacyGlassesPlugin;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsIm1haW4udHMiXSwic291cmNlc0NvbnRlbnQiOlsiLyoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKlxyXG5Db3B5cmlnaHQgKGMpIE1pY3Jvc29mdCBDb3Jwb3JhdGlvbi5cclxuXHJcblBlcm1pc3Npb24gdG8gdXNlLCBjb3B5LCBtb2RpZnksIGFuZC9vciBkaXN0cmlidXRlIHRoaXMgc29mdHdhcmUgZm9yIGFueVxyXG5wdXJwb3NlIHdpdGggb3Igd2l0aG91dCBmZWUgaXMgaGVyZWJ5IGdyYW50ZWQuXHJcblxyXG5USEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiIEFORCBUSEUgQVVUSE9SIERJU0NMQUlNUyBBTEwgV0FSUkFOVElFUyBXSVRIXHJcblJFR0FSRCBUTyBUSElTIFNPRlRXQVJFIElOQ0xVRElORyBBTEwgSU1QTElFRCBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWVxyXG5BTkQgRklUTkVTUy4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFIEFVVEhPUiBCRSBMSUFCTEUgRk9SIEFOWSBTUEVDSUFMLCBESVJFQ1QsXHJcbklORElSRUNULCBPUiBDT05TRVFVRU5USUFMIERBTUFHRVMgT1IgQU5ZIERBTUFHRVMgV0hBVFNPRVZFUiBSRVNVTFRJTkcgRlJPTVxyXG5MT1NTIE9GIFVTRSwgREFUQSBPUiBQUk9GSVRTLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgTkVHTElHRU5DRSBPUlxyXG5PVEhFUiBUT1JUSU9VUyBBQ1RJT04sIEFSSVNJTkcgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgVVNFIE9SXHJcblBFUkZPUk1BTkNFIE9GIFRISVMgU09GVFdBUkUuXHJcbioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqICovXHJcbi8qIGdsb2JhbCBSZWZsZWN0LCBQcm9taXNlLCBTdXBwcmVzc2VkRXJyb3IsIFN5bWJvbCAqL1xyXG5cclxudmFyIGV4dGVuZFN0YXRpY3MgPSBmdW5jdGlvbihkLCBiKSB7XHJcbiAgICBleHRlbmRTdGF0aWNzID0gT2JqZWN0LnNldFByb3RvdHlwZU9mIHx8XHJcbiAgICAgICAgKHsgX19wcm90b19fOiBbXSB9IGluc3RhbmNlb2YgQXJyYXkgJiYgZnVuY3Rpb24gKGQsIGIpIHsgZC5fX3Byb3RvX18gPSBiOyB9KSB8fFxyXG4gICAgICAgIGZ1bmN0aW9uIChkLCBiKSB7IGZvciAodmFyIHAgaW4gYikgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChiLCBwKSkgZFtwXSA9IGJbcF07IH07XHJcbiAgICByZXR1cm4gZXh0ZW5kU3RhdGljcyhkLCBiKTtcclxufTtcclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2V4dGVuZHMoZCwgYikge1xyXG4gICAgaWYgKHR5cGVvZiBiICE9PSBcImZ1bmN0aW9uXCIgJiYgYiAhPT0gbnVsbClcclxuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKFwiQ2xhc3MgZXh0ZW5kcyB2YWx1ZSBcIiArIFN0cmluZyhiKSArIFwiIGlzIG5vdCBhIGNvbnN0cnVjdG9yIG9yIG51bGxcIik7XHJcbiAgICBleHRlbmRTdGF0aWNzKGQsIGIpO1xyXG4gICAgZnVuY3Rpb24gX18oKSB7IHRoaXMuY29uc3RydWN0b3IgPSBkOyB9XHJcbiAgICBkLnByb3RvdHlwZSA9IGIgPT09IG51bGwgPyBPYmplY3QuY3JlYXRlKGIpIDogKF9fLnByb3RvdHlwZSA9IGIucHJvdG90eXBlLCBuZXcgX18oKSk7XHJcbn1cclxuXHJcbmV4cG9ydCB2YXIgX19hc3NpZ24gPSBmdW5jdGlvbigpIHtcclxuICAgIF9fYXNzaWduID0gT2JqZWN0LmFzc2lnbiB8fCBmdW5jdGlvbiBfX2Fzc2lnbih0KSB7XHJcbiAgICAgICAgZm9yICh2YXIgcywgaSA9IDEsIG4gPSBhcmd1bWVudHMubGVuZ3RoOyBpIDwgbjsgaSsrKSB7XHJcbiAgICAgICAgICAgIHMgPSBhcmd1bWVudHNbaV07XHJcbiAgICAgICAgICAgIGZvciAodmFyIHAgaW4gcykgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChzLCBwKSkgdFtwXSA9IHNbcF07XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB0O1xyXG4gICAgfVxyXG4gICAgcmV0dXJuIF9fYXNzaWduLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX3Jlc3QocywgZSkge1xyXG4gICAgdmFyIHQgPSB7fTtcclxuICAgIGZvciAodmFyIHAgaW4gcykgaWYgKE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbChzLCBwKSAmJiBlLmluZGV4T2YocCkgPCAwKVxyXG4gICAgICAgIHRbcF0gPSBzW3BdO1xyXG4gICAgaWYgKHMgIT0gbnVsbCAmJiB0eXBlb2YgT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyA9PT0gXCJmdW5jdGlvblwiKVxyXG4gICAgICAgIGZvciAodmFyIGkgPSAwLCBwID0gT2JqZWN0LmdldE93blByb3BlcnR5U3ltYm9scyhzKTsgaSA8IHAubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICAgICAgaWYgKGUuaW5kZXhPZihwW2ldKSA8IDAgJiYgT2JqZWN0LnByb3RvdHlwZS5wcm9wZXJ0eUlzRW51bWVyYWJsZS5jYWxsKHMsIHBbaV0pKVxyXG4gICAgICAgICAgICAgICAgdFtwW2ldXSA9IHNbcFtpXV07XHJcbiAgICAgICAgfVxyXG4gICAgcmV0dXJuIHQ7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBfX2RlY29yYXRlKGRlY29yYXRvcnMsIHRhcmdldCwga2V5LCBkZXNjKSB7XHJcbiAgICB2YXIgYyA9IGFyZ3VtZW50cy5sZW5ndGgsIHIgPSBjIDwgMyA/IHRhcmdldCA6IGRlc2MgPT09IG51bGwgPyBkZXNjID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0YXJnZXQsIGtleSkgOiBkZXNjLCBkO1xyXG4gICAgaWYgKHR5cGVvZiBSZWZsZWN0ID09PSBcIm9iamVjdFwiICYmIHR5cGVvZiBSZWZsZWN0LmRlY29yYXRlID09PSBcImZ1bmN0aW9uXCIpIHIgPSBSZWZsZWN0LmRlY29yYXRlKGRlY29yYXRvcnMsIHRhcmdldCwga2V5LCBkZXNjKTtcclxuICAgIGVsc2UgZm9yICh2YXIgaSA9IGRlY29yYXRvcnMubGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIGlmIChkID0gZGVjb3JhdG9