From acbfb7b5c208bfa0ecce5e154ece1d6610a49c3b Mon Sep 17 00:00:00 2001 From: zihad Date: Thu, 10 Apr 2025 03:41:26 +0600 Subject: [PATCH] initial commit --- data.json | 6 + esbuild.config.mjs | 49 ++++++ main.js | 366 +++++++++++++++++++++++++++++++++++++++ main.ts | 414 +++++++++++++++++++++++++++++++++++++++++++++ manifest.json | 11 ++ package.json | 30 ++++ styles.css | 8 + tsconfig.json | 24 +++ version-bump.mjs | 14 ++ versions.json | 3 + 10 files changed, 925 insertions(+) create mode 100644 data.json create mode 100644 esbuild.config.mjs create mode 100644 main.js create mode 100644 main.ts create mode 100644 manifest.json create mode 100644 package.json create mode 100644 styles.css create mode 100644 tsconfig.json create mode 100644 version-bump.mjs create mode 100644 versions.json diff --git a/data.json b/data.json new file mode 100644 index 0000000..d7034d1 --- /dev/null +++ b/data.json @@ -0,0 +1,6 @@ +{ + "mySetting": "default", + "notesDB": [ + ], + "themeMode": "system" +} diff --git a/esbuild.config.mjs b/esbuild.config.mjs new file mode 100644 index 0000000..a5de8b8 --- /dev/null +++ b/esbuild.config.mjs @@ -0,0 +1,49 @@ +import esbuild from "esbuild"; +import process from "process"; +import builtins from "builtin-modules"; + +const banner = +`/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ +`; + +const prod = (process.argv[2] === "production"); + +const context = await esbuild.context({ + banner: { + js: banner, + }, + entryPoints: ["main.ts"], + bundle: true, + external: [ + "obsidian", + "electron", + "@codemirror/autocomplete", + "@codemirror/collab", + "@codemirror/commands", + "@codemirror/language", + "@codemirror/lint", + "@codemirror/search", + "@codemirror/state", + "@codemirror/view", + "@lezer/common", + "@lezer/highlight", + "@lezer/lr", + ...builtins], + format: "cjs", + target: "es2018", + logLevel: "info", + sourcemap: prod ? false : "inline", + treeShaking: true, + outfile: "main.js", + minify: prod, +}); + +if (prod) { + await context.rebuild(); + process.exit(0); +} else { + await context.watch(); +} diff --git a/main.js b/main.js new file mode 100644 index 0000000..c666a02 --- /dev/null +++ b/main.js @@ -0,0 +1,366 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// main.ts +var main_exports = {}; +__export(main_exports, { + default: () => MyPlugin +}); +module.exports = __toCommonJS(main_exports); +var import_obsidian = require("obsidian"); +var VIEW_TYPE_EASY_KEEP = "easy-keep-view"; +var DEFAULT_SETTINGS = { + mySetting: "default", + notesDB: [], + themeMode: "system" +}; +var EasyKeepView = class extends import_obsidian.ItemView { + constructor(leaf, plugin) { + super(leaf); + this.plugin = plugin; + } + getViewType() { + return VIEW_TYPE_EASY_KEEP; + } + getDisplayText() { + return "Easy Keep View"; + } + buildContent() { + this.mainContainer.empty(); + this.mainContainer.style.overflowY = "auto"; + this.mainContainer.style.height = "100%"; + const cardContainer = this.mainContainer.createDiv("easy-keep-cards-container"); + const newNoteCard = cardContainer.createDiv("easy-keep-card new-note-card"); + newNoteCard.createEl("h3", { text: "+" }); + newNoteCard.createEl("p", { text: "Add New Note" }); + newNoteCard.onclick = () => { + this.plugin.createNewNote(); + }; + let notes = this.plugin.settings.notesDB.slice().sort((a, b) => b.time - a.time).slice(0, 20); + if (notes.length) { + notes.forEach((note) => { + const card = cardContainer.createDiv("easy-keep-card"); + card.createEl("h3", { text: note.title }); + if (note.imagePath) { + const resourcePath = this.app.vault.adapter.getResourcePath(note.imagePath); + const img = card.createEl("img", { cls: "easy-keep-thumbnail" }); + img.src = resourcePath; + img.onerror = () => { + img.remove(); + if (note.excerpt) { + card.createEl("p", { text: note.excerpt }); + } + }; + img.onload = () => { + console.log(`[Render] Image loaded: ${resourcePath}`); + }; + } else { + if (note.excerpt) { + card.createEl("p", { text: note.excerpt }); + } + } + card.onclick = () => this.plugin.openNoteInNewTab(note.path); + }); + } else { + const noHistory = cardContainer.createDiv("no-history-message"); + noHistory.setText("No notes, create a new one!"); + } + this.injectCSS(); + } + refreshContent() { + this.buildContent(); + } + async onOpen() { + this.mainContainer = this.containerEl; + this.buildContent(); + } + async onClose() { + } + injectCSS() { + const existingStyle = document.getElementById("easy-keep-css"); + if (existingStyle) + existingStyle.remove(); + let bgColor = "#f9f9f9"; + let borderColor = "#ddd"; + let textColor = "#555"; + const theme = this.plugin.settings.themeMode; + if (theme === "dark" || theme === "system" && document.body.hasClass("theme-dark")) { + bgColor = "#333"; + borderColor = "#555"; + textColor = "#ccc"; + } + const style = document.createElement("style"); + style.id = "easy-keep-css"; + style.innerText = ` + .easy-keep-cards-container { + display: flex; + flex-wrap: wrap; + gap: 16px; + padding: 10px; + } + .easy-keep-card { + width: 250px; + min-height: 150px; + padding: 16px; + border: 1px solid ${borderColor}; + border-radius: 8px; + background-color: ${bgColor}; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + cursor: pointer; + color: ${textColor}; + display: flex; + flex-direction: column; + justify-content: space-between; + } + .easy-keep-card h3 { + font-size: 18px; + margin-bottom: 8px; + } + .easy-keep-card p { + font-size: 14px; + color: ${textColor}; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + margin: 0; + } + .easy-keep-thumbnail { + max-width: 100%; + max-height: 100px; + object-fit: cover; + border-radius: 4px; + margin-top: 8px; + display: block; + } + .new-note-card { + width: 250px; + height: 150px; + display: flex; + align-items: center; + justify-content: center; + background-color: ${bgColor}; + border: 2px dashed ${borderColor}; + cursor: pointer; + } + .no-history-message { + font-size: 20px; + color: ${textColor}; + text-align: center; + padding: 20px; + width: 100%; + } + `; + document.head.appendChild(style); + } +}; +var SampleSettingTab = class extends import_obsidian.PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + this.plugin = plugin; + } + display() { + const { containerEl } = this; + containerEl.empty(); + containerEl.createEl("h2", { text: "Easy Keep View Plugin Settings" }); + new import_obsidian.Setting(containerEl).setName("Theme Mode").setDesc("Select a theme mode for the Easy Keep View").addDropdown((dropdown) => { + dropdown.addOption("system", "System (adapt)"); + dropdown.addOption("light", "Light"); + dropdown.addOption("dark", "Dark"); + dropdown.setValue(this.plugin.settings.themeMode); + dropdown.onChange(async (value) => { + this.plugin.settings.themeMode = value; + await this.plugin.saveSettings(); + this.plugin.refreshEasyKeepViewIfOpen(); + }); + }); + } +}; +var MyPlugin = class extends import_obsidian.Plugin { + async onload() { + await this.loadSettings(); + await this.cleanDatabase(); + this.registerView(VIEW_TYPE_EASY_KEEP, (leaf) => new EasyKeepView(leaf, this)); + const ribbonIconEl = this.addRibbonIcon("dice", "Easy Keep View", () => { + this.activateEasyKeepView(); + }); + ribbonIconEl.addClass("my-plugin-ribbon-class"); + this.registerEvent(this.app.workspace.on("file-open", async (file) => { + if (file) { + await this.addToDatabase(file); + this.refreshEasyKeepViewIfOpen(); + } + })); + this.registerEvent(this.app.vault.on("delete", async (file) => { + if (file instanceof import_obsidian.TFile) { + await this.removeFromDatabase(file.path); + this.refreshEasyKeepViewIfOpen(); + } + })); + this.registerEvent(this.app.vault.on("rename", async (file, oldPath) => { + if (file instanceof import_obsidian.TFile) { + const entryIndex = this.settings.notesDB.findIndex((e) => e.path === oldPath); + if (entryIndex !== -1) { + this.settings.notesDB[entryIndex].path = file.path; + this.settings.notesDB[entryIndex].title = file.basename; + await this.saveSettings(); + this.refreshEasyKeepViewIfOpen(); + } + } + })); + this.registerEvent(this.app.vault.on("rename", async (file, oldPath) => { + if (file instanceof import_obsidian.TFile) { + const entryIndex = this.settings.notesDB.findIndex((e) => e.path === oldPath); + if (entryIndex !== -1) { + this.settings.notesDB[entryIndex].path = file.path; + this.settings.notesDB[entryIndex].title = file.basename; + await this.saveSettings(); + this.refreshEasyKeepViewIfOpen(); + } + if (/\.(jpg|jpeg|png|webp)$/i.test(file.path)) { + let updated = false; + this.settings.notesDB.forEach((entry) => { + if (entry.imagePath === oldPath) { + entry.imagePath = file.path; + updated = true; + } + }); + if (updated) { + await this.saveSettings(); + this.refreshEasyKeepViewIfOpen(); + } + } + } + })); + this.addSettingTab(new SampleSettingTab(this.app, this)); + } + onunload() { + this.app.workspace.detachLeavesOfType(VIEW_TYPE_EASY_KEEP); + } + async activateEasyKeepView() { + const existingLeaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EASY_KEEP); + if (existingLeaves.length > 0) { + this.app.workspace.revealLeaf(existingLeaves[0]); + return; + } + const leaf = this.app.workspace.getLeaf(true); + await leaf.setViewState({ type: VIEW_TYPE_EASY_KEEP, active: true }); + this.app.workspace.revealLeaf(leaf); + } + refreshEasyKeepViewIfOpen() { + const existingLeaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EASY_KEEP); + if (existingLeaves.length > 0) { + const view = existingLeaves[0].view; + view.refreshContent(); + } + } + async addToDatabase(file) { + const content = await this.app.vault.cachedRead(file); + const lines = content.trim().split("\n").filter((line) => line.trim() !== ""); + let excerpt = ""; + let imagePath; + if (/\.(jpg|jpeg|png|webp)$/i.test(file.path)) { + imagePath = file.path; + } else { + const imageMatch = content.match(/!\[.*?\]\((.*?)\)|\!\[\[(.*?)\]\]/i); + if (imageMatch) { + imagePath = imageMatch[1] || imageMatch[2]; + if (imagePath && !imagePath.startsWith("http") && !imagePath.startsWith("/")) { + const fileFolder = file.path.substring(0, file.path.lastIndexOf("/")); + imagePath = `${fileFolder}/${imagePath}`.replace(/\/+/g, "/"); + } + const imageFile = this.app.vault.getAbstractFileByPath(imagePath); + if (!(imageFile instanceof import_obsidian.TFile && /\.(jpg|jpeg|png|webp)$/i.test(imagePath))) { + imagePath = void 0; + } + } + } + if (!imagePath && lines.length > 0) { + excerpt = lines.slice(0, 2).join(" "); + if (lines.length > 2) + excerpt = excerpt.trim() + " \u2026"; + } + const titleWithoutExt = file.name.replace(/\.(md|png|jpg|jpeg|webp)$/i, ""); + const newEntry = { + path: file.path, + title: titleWithoutExt, + excerpt, + time: Date.now(), + imagePath + }; + this.settings.notesDB = this.settings.notesDB.filter((entry) => entry.path !== file.path); + this.settings.notesDB.unshift(newEntry); + await this.saveSettings(); + } + async removeFromDatabase(filePath) { + const initialLength = this.settings.notesDB.length; + this.settings.notesDB = this.settings.notesDB.filter((entry) => entry.path !== filePath); + if (this.settings.notesDB.length !== initialLength) { + await this.saveSettings(); + } + } + async cleanDatabase() { + const files = this.app.vault.getMarkdownFiles(); + const existingPaths = new Set(files.map((file) => file.path)); + this.settings.notesDB = this.settings.notesDB.filter((entry) => existingPaths.has(entry.path)); + await this.saveSettings(); + } + async openNoteInNewTab(filePath) { + const file = this.app.vault.getAbstractFileByPath(filePath); + if (!(file instanceof import_obsidian.TFile)) + return; + let existingLeaf = null; + this.app.workspace.iterateAllLeaves((leaf) => { + var _a; + if (leaf.view.getViewType() === "markdown" && ((_a = leaf.view.file) == null ? void 0 : _a.path) === filePath) { + existingLeaf = leaf; + } + }); + if (existingLeaf) { + this.app.workspace.setActiveLeaf(existingLeaf, { focus: true }); + } else { + const newLeaf = this.app.workspace.getLeaf(true); + await newLeaf.openFile(file); + this.app.workspace.setActiveLeaf(newLeaf, { focus: true }); + } + } + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } + async saveSettings() { + await this.saveData(this.settings); + } + async createNewNote() { + const fileName = `New Note ${Date.now()}.md`; + const filePath = `/${fileName}`; + const file = await this.app.vault.create(filePath, `# ${fileName} +`); + await this.addToDatabase(file); + this.refreshEasyKeepViewIfOpen(); + const newLeaf = this.app.workspace.getLeaf(true); + await newLeaf.openFile(file); + this.app.workspace.setActiveLeaf(newLeaf, { focus: true }); + } +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibWFpbi50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiaW1wb3J0IHtcblx0QXBwLFxuXHRQbHVnaW4sXG5cdE5vdGljZSxcblx0V29ya3NwYWNlTGVhZixcblx0SXRlbVZpZXcsXG5cdFRGaWxlLFxuXHRQbHVnaW5TZXR0aW5nVGFiLFxuXHRTZXR0aW5nLFxufSBmcm9tIFwib2JzaWRpYW5cIjtcblxuY29uc3QgVklFV19UWVBFX0VBU1lfS0VFUCA9IFwiZWFzeS1rZWVwLXZpZXdcIjtcblxuaW50ZXJmYWNlIE5vdGVFbnRyeSB7XG5cdHBhdGg6IHN0cmluZztcblx0dGl0bGU6IHN0cmluZztcblx0ZXhjZXJwdDogc3RyaW5nO1xuXHR0aW1lOiBudW1iZXI7XG5cdGltYWdlUGF0aD86IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIE15UGx1Z2luU2V0dGluZ3Mge1xuXHRteVNldHRpbmc6IHN0cmluZztcblx0bm90ZXNEQjogTm90ZUVudHJ5W107XG5cdHRoZW1lTW9kZTogXCJzeXN0ZW1cIiB8IFwibGlnaHRcIiB8IFwiZGFya1wiO1xufVxuXG5jb25zdCBERUZBVUxUX1NFVFRJTkdTOiBNeVBsdWdpblNldHRpbmdzID0ge1xuXHRteVNldHRpbmc6IFwiZGVmYXVsdFwiLFxuXHRub3Rlc0RCOiBbXSxcblx0dGhlbWVNb2RlOiBcInN5c3RlbVwiLFxufTtcblxuY2xhc3MgRWFzeUtlZXBWaWV3IGV4dGVuZHMgSXRlbVZpZXcge1xuXHRwbHVnaW46IE15UGx1Z2luO1xuXHRwcml2YXRlIG1haW5Db250YWluZXI6IEhUTUxFbGVtZW50O1xuXG5cdGNvbnN0cnVjdG9yKGxlYWY6IFdvcmtzcGFjZUxlYWYsIHBsdWdpbjogTXlQbHVnaW4pIHtcblx0XHRzdXBlcihsZWFmKTtcblx0XHR0aGlzLnBsdWdpbiA9IHBsdWdpbjtcblx0fVxuXG5cdGdldFZpZXdUeXBlKCk6IHN0cmluZyB7XG5cdFx0cmV0dXJuIFZJRVdfVFlQRV9FQVNZX0tFRVA7XG5cdH1cblxuXHRnZXREaXNwbGF5VGV4dCgpOiBzdHJpbmcge1xuXHRcdHJldHVybiBcIkVhc3kgS2VlcCBWaWV3XCI7XG5cdH1cblxuXHRidWlsZENvbnRlbnQoKSB7XG5cdFx0dGhpcy5tYWluQ29udGFpbmVyLmVtcHR5KCk7XG5cdFx0dGhpcy5tYWluQ29udGFpbmVyLnN0eWxlLm92ZXJmbG93WSA9IFwiYXV0b1wiO1xuXHRcdHRoaXMubWFpbkNvbnRhaW5lci5zdHlsZS5oZWlnaHQgPSBcIjEwMCVcIjtcblxuXHRcdGNvbnN0IGNhcmRDb250YWluZXIgPSB0aGlzLm1haW5Db250YWluZXIuY3JlYXRlRGl2KFwiZWFzeS1rZWVwLWNhcmRzLWNvbnRhaW5lclwiKTtcblxuXHRcdGNvbnN0IG5ld05vdGVDYXJkID0gY2FyZENvbnRhaW5lci5jcmVhdGVEaXYoXCJlYXN5LWtlZXAtY2FyZCBuZXctbm90ZS1jYXJkXCIpO1xuXHRcdG5ld05vdGVDYXJkLmNyZWF0ZUVsKFwiaDNcIiwgeyB0ZXh0OiBcIitcIiB9KTtcblx0XHRuZXdOb3RlQ2FyZC5jcmVhdGVFbChcInBcIiwgeyB0ZXh0OiBcIkFkZCBOZXcgTm90ZVwiIH0pO1xuXG5cdFx0bmV3Tm90ZUNhcmQub25jbGljayA9ICgpID0+IHtcblx0XHRcdHRoaXMucGx1Z2luLmNyZWF0ZU5ld05vdGUoKTtcblx0XHR9O1xuXG5cdFx0bGV0IG5vdGVzID0gdGhpcy5wbHVnaW4uc2V0dGluZ3Mubm90ZXNEQi5zbGljZSgpLnNvcnQoKGEsIGIpID0+IGIudGltZSAtIGEudGltZSkuc2xpY2UoMCwgMjApO1xuXG5cdFx0aWYgKG5vdGVzLmxlbmd0aCkge1xuXHRcdFx0bm90ZXMuZm9yRWFjaChub3RlID0+IHtcblx0XHRcdFx0Y29uc3QgY2FyZCA9IGNhcmRDb250YWluZXIuY3JlYXRlRGl2KFwiZWFzeS1rZWVwLWNhcmRcIik7XG5cdFx0XHRcdGNhcmQuY3JlYXRlRWwoXCJoM1wiLCB7IHRleHQ6IG5vdGUudGl0bGUgfSk7XG5cblx0XHRcdFx0aWYgKG5vdGUuaW1hZ2VQYXRoKSB7XG5cdFx0XHRcdFx0Y29uc3QgcmVzb3VyY2VQYXRoID0gdGhpcy5hcHAudmF1bHQuYWRhcHRlci5nZXRSZXNvdXJjZVBhdGgobm90ZS5pbWFnZVBhdGgpO1xuXHRcdFx0XHRcdGNvbnN0IGltZyA9IGNhcmQuY3JlYXRlRWwoXCJpbWdcIiwgeyBjbHM6IFwiZWFzeS1rZWVwLXRodW1ibmFpbFwiIH0pO1xuXHRcdFx0XHRcdGltZy5zcmMgPSByZXNvdXJjZVBhdGg7XG5cblx0XHRcdFx0XHRpbWcub25lcnJvciA9ICgpID0+IHtcblx0XHRcdFx0XHRcdGltZy5yZW1vdmUoKTtcblx0XHRcdFx0XHRcdGlmIChub3RlLmV4Y2VycHQpIHtcblx0XHRcdFx0XHRcdFx0Y2FyZC5jcmVhdGVFbChcInBcIiwgeyB0ZXh0OiBub3RlLmV4Y2VycHQgfSk7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fTtcblxuXHRcdFx0XHRcdGltZy5vbmxvYWQgPSAoKSA9PiB7XG5cdFx0XHRcdFx0XHRjb25zb2xlLmxvZyhgW1JlbmRlcl0gSW1hZ2UgbG9hZGVkOiAke3Jlc291cmNlUGF0aH1gKTtcblx0XHRcdFx0XHR9O1xuXHRcdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRcdGlmIChub3RlLmV4Y2VycHQpIHtcblx0XHRcdFx0XHRcdGNhcmQuY3JlYXRlRWwoXCJwXCIsIHsgdGV4dDogbm90ZS5leGNlcnB0IH0pO1xuXHRcdFx0XHRcdH1cblx0XHRcdFx0fVxuXG5cdFx0XHRcdGNhcmQub25jbGljayA9ICgpID0+IHRoaXMucGx1Z2luLm9wZW5Ob3RlSW5OZXdUYWIobm90ZS5wYXRoKTtcblx0XHRcdH0pO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRjb25zdCBub0hpc3RvcnkgPSBjYXJkQ29udGFpbmVyLmNyZWF0ZURpdihcIm5vLWhpc3RvcnktbWVzc2FnZVwiKTtcblx0XHRcdG5vSGlzdG9yeS5zZXRUZXh0KFwiTm8gbm90ZXMsIGNyZWF0ZSBhIG5ldyBvbmUhXCIpO1xuXHRcdH1cblxuXHRcdHRoaXMuaW5qZWN0Q1NTKCk7XG5cdH1cblxuXHRyZWZyZXNoQ29udGVudCgpIHtcblx0XHR0aGlzLmJ1aWxkQ29udGVudCgpO1xuXHR9XG5cblx0YXN5bmMgb25PcGVuKCkge1xuXHRcdHRoaXMubWFpbkNvbnRhaW5lciA9IHRoaXMuY29udGFpbmVyRWw7XG5cdFx0dGhpcy5idWlsZENvbnRlbnQoKTtcblx0fVxuXG5cdGFzeW5jIG9uQ2xvc2UoKSB7fVxuXG5cdGluamVjdENTUygpIHtcblx0XHRjb25zdCBleGlzdGluZ1N0eWxlID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJlYXN5LWtlZXAtY3NzXCIpO1xuXHRcdGlmIChleGlzdGluZ1N0eWxlKSBleGlzdGluZ1N0eWxlLnJlbW92ZSgpO1xuXG5cdFx0bGV0IGJnQ29sb3IgPSBcIiNmOWY5ZjlcIjtcblx0XHRsZXQgYm9yZGVyQ29sb3IgPSBcIiNkZGRcIjtcblx0XHRsZXQgdGV4dENvbG9yID0gXCIjNTU1XCI7XG5cdFx0Y29uc3QgdGhlbWUgPSB0aGlzLnBsdWdpbi5zZXR0aW5ncy50aGVtZU1vZGU7XG5cblx0XHRpZiAodGhlbWUgPT09IFwiZGFya1wiIHx8ICh0aGVtZSA9PT0gXCJzeXN0ZW1cIiAmJiBkb2N1bWVudC5ib2R5Lmhhc0NsYXNzKFwidGhlbWUtZGFya1wiKSkpIHtcblx0XHRcdGJnQ29sb3IgPSBcIiMzMzNcIjtcblx0XHRcdGJvcmRlckNvbG9yID0gXCIjNTU1XCI7XG5cdFx0XHR0ZXh0Q29sb3IgPSBcIiNjY2NcIjtcblx0XHR9XG5cblx0XHRjb25zdCBzdHlsZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJzdHlsZVwiKTtcblx0XHRzdHlsZS5pZCA9IFwiZWFzeS1rZWVwLWNzc1wiO1xuXHRcdHN0eWxlLmlubmVyVGV4dCA9IGBcblx0XHQuZWFzeS1rZWVwLWNhcmRzLWNvbnRhaW5lciB7XG5cdFx0XHRkaXNwbGF5OiBmbGV4O1xuXHRcdFx0ZmxleC13cmFwOiB3cmFwO1xuXHRcdFx0Z2FwOiAxNnB4O1xuXHRcdFx0cGFkZGluZzogMTBweDtcblx0XHR9XG5cdFx0LmVhc3kta2VlcC1jYXJkIHtcblx0XHRcdHdpZHRoOiAyNTBweDtcblx0XHRcdG1pbi1oZWlnaHQ6IDE1MHB4O1xuXHRcdFx0cGFkZGluZzogMTZweDtcblx0XHRcdGJvcmRlcjogMXB4IHNvbGlkICR7Ym9yZGVyQ29sb3J9O1xuXHRcdFx0Ym9yZGVyLXJhZGl1czogOHB4O1xuXHRcdFx0YmFja2dyb3VuZC1jb2xvcjogJHtiZ0NvbG9yfTtcblx0XHRcdGJveC1zaGFkb3c6IDAgMnB4IDVweCByZ2JhKDAsIDAsIDAsIDAuMSk7XG5cdFx0XHRjdXJzb3I6IHBvaW50ZXI7XG5cdFx0XHRjb2xvcjogJHt0ZXh0Q29sb3J9O1xuXHRcdFx0ZGlzcGxheTogZmxleDtcblx0XHRcdGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG5cdFx0XHRqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWJldHdlZW47XG5cdFx0fVxuXHRcdC5lYXN5LWtlZXAtY2FyZCBoMyB7XG5cdFx0XHRmb250LXNpemU6IDE4cHg7XG5cdFx0XHRtYXJnaW4tYm90dG9tOiA4cHg7XG5cdFx0fVxuXHRcdC5lYXN5LWtlZXAtY2FyZCBwIHtcblx0XHRcdGZvbnQtc2l6ZTogMTRweDtcblx0XHRcdGNvbG9yOiAke3RleHRDb2xvcn07XG5cdFx0XHRvdmVyZmxvdzogaGlkZGVuO1xuXHRcdFx0dGV4dC1vdmVyZmxvdzogZWxsaXBzaXM7XG5cdFx0XHRkaXNwbGF5OiAtd2Via2l0LWJveDtcblx0XHRcdC13ZWJraXQtbGluZS1jbGFtcDogMjtcblx0XHRcdC13ZWJraXQtYm94LW9yaWVudDogdmVydGljYWw7XG5cdFx0XHRtYXJnaW46IDA7XG5cdFx0fVxuXHRcdC5lYXN5LWtlZXAtdGh1bWJuYWlsIHtcblx0XHRcdG1heC13aWR0aDogMTAwJTtcblx0XHRcdG1heC1oZWlnaHQ6IDEwMHB4O1xuXHRcdFx0b2JqZWN0LWZpdDogY292ZXI7XG5cdFx0XHRib3JkZXItcmFkaXVzOiA0cHg7XG5cdFx0XHRtYXJnaW4tdG9wOiA4cHg7XG5cdFx0XHRkaXNwbGF5OiBibG9jaztcblx0XHR9XG5cdFx0Lm5ldy1ub3RlLWNhcmQge1xuXHRcdFx0d2lkdGg6IDI1MHB4O1xuXHRcdFx0aGVpZ2h0OiAxNTBweDtcblx0XHRcdGRpc3BsYXk6IGZsZXg7XG5cdFx0XHRhbGlnbi1pdGVtczogY2VudGVyO1xuXHRcdFx0anVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG5cdFx0XHRiYWNrZ3JvdW5kLWNvbG9yOiAke2JnQ29sb3J9O1xuXHRcdFx0Ym9yZGVyOiAycHggZGFzaGVkICR7Ym9yZGVyQ29sb3J9O1xuXHRcdFx0Y3Vyc29yOiBwb2ludGVyO1xuXHRcdH1cblx0XHQubm8taGlzdG9yeS1tZXNzYWdlIHtcblx0XHRcdGZvbnQtc2l6ZTogMjBweDtcblx0XHRcdGNvbG9yOiAke3RleHRDb2xvcn07XG5cdFx0XHR0ZXh0LWFsaWduOiBjZW50ZXI7XG5cdFx0XHRwYWRkaW5nOiAyMHB4O1xuXHRcdFx0d2lkdGg6IDEwMCU7XG5cdFx0fVxuXHRcdGA7XG5cdFx0ZG9jdW1lbnQuaGVhZC5hcHBlbmRDaGlsZChzdHlsZSk7XG5cdH1cbn1cblxuY2xhc3MgU2FtcGxlU2V0dGluZ1RhYiBleHRlbmRzIFBsdWdpblNldHRpbmdUYWIge1xuXHRwbHVnaW46IE15UGx1Z2luO1xuXHRjb25zdHJ1Y3RvcihhcHA6IEFwcCwgcGx1Z2luOiBNeVBsdWdpbikge1xuXHRcdHN1cGVyKGFwcCwgcGx1Z2luKTtcblx0XHR0aGlzLnBsdWdpbiA9IHBsdWdpbjtcblx0fVxuXHRkaXNwbGF5KCk6IHZvaWQge1xuXHRcdGNvbnN0IHsgY29udGFpbmVyRWwgfSA9IHRoaXM7XG5cdFx0Y29udGFpbmVyRWwuZW1wdHkoKTtcblx0XHRjb250YWluZXJFbC5jcmVhdGVFbChcImgyXCIsIHsgdGV4dDogXCJFYXN5IEtlZXAgVmlldyBQbHVnaW4gU2V0dGluZ3NcIiB9KTtcblx0XHRuZXcgU2V0dGluZyhjb250YWluZXJFbClcblx0XHQuc2V0TmFtZShcIlRoZW1lIE1vZGVcIilcblx0XHQuc2V0RGVzYyhcIlNlbGVjdCBhIHRoZW1lIG1vZGUgZm9yIHRoZSBFYXN5IEtlZXAgVmlld1wiKVxuXHRcdC5hZGREcm9wZG93bihkcm9wZG93biA9PiB7XG5cdFx0XHRkcm9wZG93bi5hZGRPcHRpb24oXCJzeXN0ZW1cIiwgXCJTeXN0ZW0gKGFkYXB0KVwiKTtcblx0XHRcdGRyb3Bkb3duLmFkZE9wdGlvbihcImxpZ2h0XCIsIFwiTGlnaHRcIik7XG5cdFx0XHRkcm9wZG93bi5hZGRPcHRpb24oXCJkYXJrXCIsIFwiRGFya1wiKTtcblx0XHRcdGRyb3Bkb3duLnNldFZhbHVlKHRoaXMucGx1Z2luLnNldHRpbmdzLnRoZW1lTW9kZSk7XG5cdFx0XHRkcm9wZG93bi5vbkNoYW5nZShhc3luYyAodmFsdWU6IFwic3lzdGVtXCIgfCBcImxpZ2h0XCIgfCBcImRhcmtcIikgPT4ge1xuXHRcdFx0XHR0aGlzLnBsdWdpbi5zZXR0aW5ncy50aGVtZU1vZGUgPSB2YWx1ZTtcblx0XHRcdFx0YXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKCk7XG5cdFx0XHRcdHRoaXMucGx1Z2luLnJlZnJlc2hFYXN5S2VlcFZpZXdJZk9wZW4oKTtcblx0XHRcdH0pO1xuXHRcdH0pO1xuXHR9XG59XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIE15UGx1Z2luIGV4dGVuZHMgUGx1Z2luIHtcblx0c2V0dGluZ3M6IE15UGx1Z2luU2V0dGluZ3M7XG5cblx0YXN5bmMgb25sb2FkKCkge1xuXHRcdGF3YWl0IHRoaXMubG9hZFNldHRpbmdzKCk7XG5cdFx0YXdhaXQgdGhpcy5jbGVhbkRhdGFiYXNlKCk7XG5cblx0XHR0aGlzLnJlZ2lzdGVyVmlldyhWSUVXX1RZUEVfRUFTWV9LRUVQLCAobGVhZikgPT4gbmV3IEVhc3lLZWVwVmlldyhsZWFmLCB0aGlzKSk7XG5cblx0XHRjb25zdCByaWJib25JY29uRWwgPSB0aGlzLmFkZFJpYmJvbkljb24oXCJkaWNlXCIsIFwiRWFzeSBLZWVwIFZpZXdcIiwgKCkgPT4ge1xuXHRcdFx0dGhpcy5hY3RpdmF0ZUVhc3lLZWVwVmlldygpO1xuXHRcdH0pO1xuXHRcdHJpYmJvbkljb25FbC5hZGRDbGFzcyhcIm15LXBsdWdpbi1yaWJib24tY2xhc3NcIik7XG5cblx0XHR0aGlzLnJlZ2lzdGVyRXZlbnQodGhpcy5hcHAud29ya3NwYWNlLm9uKFwiZmlsZS1vcGVuXCIsIGFzeW5jIChmaWxlKSA9PiB7XG5cdFx0XHRpZiAoZmlsZSkge1xuXHRcdFx0XHRhd2FpdCB0aGlzLmFkZFRvRGF0YWJhc2UoZmlsZSk7XG5cdFx0XHRcdHRoaXMucmVmcmVzaEVhc3lLZWVwVmlld0lmT3BlbigpO1xuXHRcdFx0fVxuXHRcdH0pKTtcblxuXHRcdHRoaXMucmVnaXN0ZXJFdmVudCh0aGlzLmFwcC52YXVsdC5vbihcImRlbGV0ZVwiLCBhc3luYyAoZmlsZSkgPT4ge1xuXHRcdFx0aWYgKGZpbGUgaW5zdGFuY2VvZiBURmlsZSkge1xuXHRcdFx0XHRhd2FpdCB0aGlzLnJlbW92ZUZyb21EYXRhYmFzZShmaWxlLnBhdGgpO1xuXHRcdFx0XHR0aGlzLnJlZnJlc2hFYXN5S2VlcFZpZXdJZk9wZW4oKTtcblx0XHRcdH1cblx0XHR9KSk7XG5cblx0XHR0aGlzLnJlZ2lzdGVyRXZlbnQodGhpcy5hcHAudmF1bHQub24oXCJyZW5hbWVcIiwgYXN5bmMgKGZpbGUsIG9sZFBhdGgpID0+IHtcblx0XHRcdGlmIChmaWxlIGluc3RhbmNlb2YgVEZpbGUpIHtcblx0XHRcdFx0Y29uc3QgZW50cnlJbmRleCA9IHRoaXMuc2V0dGluZ3Mubm90ZXNEQi5maW5kSW5kZXgoZSA9PiBlLnBhdGggPT09IG9sZFBhdGgpO1xuXHRcdFx0XHRpZiAoZW50cnlJbmRleCAhPT0gLTEpIHtcblx0XHRcdFx0XHR0aGlzLnNldHRpbmdzLm5vdGVzREJbZW50cnlJbmRleF0ucGF0aCA9IGZpbGUucGF0aDtcblx0XHRcdFx0XHR0aGlzLnNldHRpbmdzLm5vdGVzREJbZW50cnlJbmRleF0udGl0bGUgPSBmaWxlLmJhc2VuYW1lO1xuXHRcdFx0XHRcdGF3YWl0IHRoaXMuc2F2ZVNldHRpbmdzKCk7XG5cdFx0XHRcdFx0dGhpcy5yZWZyZXNoRWFzeUtlZXBWaWV3SWZPcGVuKCk7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9KSk7XG5cblx0XHR0aGlzLnJlZ2lzdGVyRXZlbnQodGhpcy5hcHAudmF1bHQub24oXCJyZW5hbWVcIiwgYXN5bmMgKGZpbGUsIG9sZFBhdGgpID0+IHtcblx0XHRcdGlmIChmaWxlIGluc3RhbmNlb2YgVEZpbGUpIHtcblx0XHRcdFx0Ly8gVXBkYXRlIHJlbmFtZWQgbm90ZSBmaWxlXG5cdFx0XHRcdGNvbnN0IGVudHJ5SW5kZXggPSB0aGlzLnNldHRpbmdzLm5vdGVzREIuZmluZEluZGV4KGUgPT4gZS5wYXRoID09PSBvbGRQYXRoKTtcblx0XHRcdFx0aWYgKGVudHJ5SW5kZXggIT09IC0xKSB7XG5cdFx0XHRcdFx0dGhpcy5zZXR0aW5ncy5ub3Rlc0RCW2VudHJ5SW5kZXhdLnBhdGggPSBmaWxlLnBhdGg7XG5cdFx0XHRcdFx0dGhpcy5zZXR0aW5ncy5ub3Rlc0RCW2VudHJ5SW5kZXhdLnRpdGxlID0gZmlsZS5iYXNlbmFtZTtcblx0XHRcdFx0XHRhd2FpdCB0aGlzLnNhdmVTZXR0aW5ncygpO1xuXHRcdFx0XHRcdHRoaXMucmVmcmVzaEVhc3lLZWVwVmlld0lmT3BlbigpO1xuXHRcdFx0XHR9XG5cblx0XHRcdFx0Ly8gVXBkYXRlIHJlbmFtZWQgaW1hZ2UgZmlsZVxuXHRcdFx0XHRpZiAoL1xcLihqcGd8anBlZ3xwbmd8d2VicCkkL2kudGVzdChmaWxlLnBhdGgpKSB7XG5cdFx0XHRcdFx0bGV0IHVwZGF0ZWQgPSBmYWxzZTtcblx0XHRcdFx0XHR0aGlzLnNldHRpbmdzLm5vdGVzREIuZm9yRWFjaChlbnRyeSA9PiB7XG5cdFx0XHRcdFx0XHRpZiAoZW50cnkuaW1hZ2VQYXRoID09PSBvbGRQYXRoKSB7XG5cdFx0XHRcdFx0XHRcdGVudHJ5LmltYWdlUGF0aCA9IGZpbGUucGF0aDtcblx0XHRcdFx0XHRcdFx0dXBkYXRlZCA9IHRydWU7XG5cdFx0XHRcdFx0XHR9XG5cdFx0XHRcdFx0fSk7XG5cdFx0XHRcdFx0aWYgKHVwZGF0ZWQpIHtcblx0XHRcdFx0XHRcdGF3YWl0IHRoaXMuc2F2ZVNldHRpbmdzKCk7XG5cdFx0XHRcdFx0XHR0aGlzLnJlZnJlc2hFYXN5S2VlcFZpZXdJZk9wZW4oKTtcblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9KSk7XG5cblx0XHR0aGlzLmFkZFNldHRpbmdUYWIobmV3IFNhbXBsZVNldHRpbmdUYWIodGhpcy5hcHAsIHRoaXMpKTtcblx0fVxuXG5cdG9udW5sb2FkKCkge1xuXHRcdHRoaXMuYXBwLndvcmtzcGFjZS5kZXRhY2hMZWF2ZXNPZlR5cGUoVklFV19UWVBFX0VBU1lfS0VFUCk7XG5cdH1cblxuXHRhc3luYyBhY3RpdmF0ZUVhc3lLZWVwVmlldygpIHtcblx0XHRjb25zdCBleGlzdGluZ0xlYXZlcyA9IHRoaXMuYXBwLndvcmtzcGFjZS5nZXRMZWF2ZXNPZlR5cGUoVklFV19UWVBFX0VBU1lfS0VFUCk7XG5cdFx0aWYgKGV4aXN0aW5nTGVhdmVzLmxlbmd0aCA+IDApIHtcblx0XHRcdHRoaXMuYXBwLndvcmtzcGFjZS5yZXZlYWxMZWFmKGV4aXN0aW5nTGVhdmVzWzBdKTtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHRjb25zdCBsZWFmID0gdGhpcy5hcHAud29ya3NwYWNlLmdldExlYWYodHJ1ZSk7XG5cdFx0YXdhaXQgbGVhZi5zZXRWaWV3U3RhdGUoeyB0eXBlOiBWSUVXX1RZUEVfRUFTWV9LRUVQLCBhY3RpdmU6IHRydWUgfSk7XG5cdFx0dGhpcy5hcHAud29ya3NwYWNlLnJldmVhbExlYWYobGVhZik7XG5cdH1cblxuXHRyZWZyZXNoRWFzeUtlZXBWaWV3SWZPcGVuKCkge1xuXHRcdGNvbnN0IGV4aXN0aW5nTGVhdmVzID0gdGhpcy5hcHAud29ya3NwYWNlLmdldExlYXZlc09mVHlwZShWSUVXX1RZUEVfRUFTWV9LRUVQKTtcblx0XHRpZiAoZXhpc3RpbmdMZWF2ZXMubGVuZ3RoID4gMCkge1xuXHRcdFx0Y29uc3QgdmlldyA9IGV4aXN0aW5nTGVhdmVzWzBdLnZpZXcgYXMgRWFzeUtlZXBWaWV3O1xuXHRcdFx0dmlldy5yZWZyZXNoQ29udGVudCgpO1xuXHRcdH1cblx0fVxuXG5cdGFzeW5jIGFkZFRvRGF0YWJhc2UoZmlsZTogVEZpbGUpIHtcblx0XHRjb25zdCBjb250ZW50ID0gYXdhaXQgdGhpcy5hcHAudmF1bHQuY2FjaGVkUmVhZChmaWxlKTtcblx0XHRjb25zdCBsaW5lcyA9IGNvbnRlbnQudHJpbSgpLnNwbGl0KFwiXFxuXCIpLmZpbHRlcihsaW5lID0+IGxpbmUudHJpbSgpICE9PSBcIlwiKTtcblx0XHRsZXQgZXhjZXJwdCA9IFwiXCI7XG5cdFx0bGV0IGltYWdlUGF0aDogc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG5cdFx0aWYgKC9cXC4oanBnfGpwZWd8cG5nfHdlYnApJC9pLnRlc3QoZmlsZS5wYXRoKSkge1xuXHRcdFx0aW1hZ2VQYXRoID0gZmlsZS5wYXRoO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRjb25zdCBpbWFnZU1hdGNoID0gY29udGVudC5tYXRjaCgvIVxcWy4qP1xcXVxcKCguKj8pXFwpfFxcIVxcW1xcWyguKj8pXFxdXFxdL2kpO1xuXHRcdFx0aWYgKGltYWdlTWF0Y2gpIHtcblx0XHRcdFx0aW1hZ2VQYXRoID0gaW1hZ2VNYXRjaFsxXSB8fCBpbWFnZU1hdGNoWzJdO1xuXHRcdFx0XHRpZiAoaW1hZ2VQYXRoICYmICFpbWFnZVBhdGguc3RhcnRzV2l0aChcImh0dHBcIikgJiYgIWltYWdlUGF0aC5zdGFydHNXaXRoKFwiL1wiKSkge1xuXHRcdFx0XHRcdGNvbnN0IGZpbGVGb2xkZXIgPSBmaWxlLnBhdGguc3Vic3RyaW5nKDAsIGZpbGUucGF0aC5sYXN0SW5kZXhPZihcIi9cIikpO1xuXHRcdFx0XHRcdGltYWdlUGF0aCA9IGAke2ZpbGVGb2xkZXJ9LyR7aW1hZ2VQYXRofWAucmVwbGFjZSgvXFwvKy9nLCBcIi9cIik7XG5cdFx0XHRcdH1cblx0XHRcdFx0Y29uc3QgaW1hZ2VGaWxlID0gdGhpcy5hcHAudmF1bHQuZ2V0QWJzdHJhY3RGaWxlQnlQYXRoKGltYWdlUGF0aCk7XG5cdFx0XHRcdGlmICghKGltYWdlRmlsZSBpbnN0YW5jZW9mIFRGaWxlICYmIC9cXC4oanBnfGpwZWd8cG5nfHdlYnApJC9pLnRlc3QoaW1hZ2VQYXRoKSkpIHtcblx0XHRcdFx0XHRpbWFnZVBhdGggPSB1bmRlZmluZWQ7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9XG5cblx0XHRpZiAoIWltYWdlUGF0aCAmJiBsaW5lcy5sZW5ndGggPiAwKSB7XG5cdFx0XHRleGNlcnB0ID0gbGluZXMuc2xpY2UoMCwgMikuam9pbihcIiBcIik7XG5cdFx0XHRpZiAobGluZXMubGVuZ3RoID4gMikgZXhjZXJwdCA9IGV4Y2VycHQudHJpbSgpICsgXCIgXHUyMDI2XCI7XG5cdFx0fVxuXG5cdFx0Y29uc3QgdGl0bGVXaXRob3V0RXh0ID0gZmlsZS5uYW1lLnJlcGxhY2UoL1xcLihtZHxwbmd8anBnfGpwZWd8d2VicCkkL2ksIFwiXCIpO1xuXHRcdGNvbnN0IG5ld0VudHJ5OiBOb3RlRW50cnkgPSB7XG5cdFx0XHRwYXRoOiBmaWxlLnBhdGgsXG5cdFx0XHR0aXRsZTogdGl0bGVXaXRob3V0RXh0LFxuXHRcdFx0ZXhjZXJwdCxcblx0XHRcdHRpbWU6IERhdGUubm93KCksXG5cdFx0XHRpbWFnZVBhdGgsXG5cdFx0fTtcblxuXHRcdHRoaXMuc2V0dGluZ3Mubm90ZXNEQiA9IHRoaXMuc2V0dGluZ3Mubm90ZXNEQi5maWx0ZXIoZW50cnkgPT4gZW50cnkucGF0aCAhPT0gZmlsZS5wYXRoKTtcblx0XHR0aGlzLnNldHRpbmdzLm5vdGVzREIudW5zaGlmdChuZXdFbnRyeSk7XG5cdFx0YXdhaXQgdGhpcy5zYXZlU2V0dGluZ3MoKTtcblx0fVxuXG5cdGFzeW5jIHJlbW92ZUZyb21EYXRhYmFzZShmaWxlUGF0aDogc3RyaW5nKSB7XG5cdFx0Y29uc3QgaW5pdGlhbExlbmd0aCA9IHRoaXMuc2V0dGluZ3Mubm90ZXNEQi5sZW5ndGg7XG5cdFx0dGhpcy5zZXR0aW5ncy5ub3Rlc0RCID0gdGhpcy5zZXR0aW5ncy5ub3Rlc0RCLmZpbHRlcihlbnRyeSA9PiBlbnRyeS5wYXRoICE9PSBmaWxlUGF0aCk7XG5cdFx0aWYgKHRoaXMuc2V0dGluZ3Mubm90ZXNEQi5sZW5ndGggIT09IGluaXRpYWxMZW5ndGgpIHtcblx0XHRcdGF3YWl0IHRoaXMuc2F2ZVNldHRpbmdzKCk7XG5cdFx0fVxuXHR9XG5cblx0YXN5bmMgY2xlYW5EYXRhYmFzZSgpIHtcblx0XHRjb25zdCBmaWxlcyA9IHRoaXMuYXBwLnZhdWx0LmdldE1hcmtkb3duRmlsZXMoKTtcblx0XHRjb25zdCBleGlzdGluZ1BhdGhzID0gbmV3IFNldChmaWxlcy5tYXAoZmlsZSA9PiBmaWxlLnBhdGgpKTtcblx0XHR0aGlzLnNldHRpbmdzLm5vdGVzREIgPSB0aGlzLnNldHRpbmdzLm5vdGVzREIuZmlsdGVyKGVudHJ5ID0+IGV4aXN0aW5nUGF0aHMuaGFzKGVudHJ5LnBhdGgpKTtcblx0XHRhd2FpdCB0aGlzLnNhdmVTZXR0aW5ncygpO1xuXHR9XG5cblx0YXN5bmMgb3Blbk5vdGVJbk5ld1RhYihmaWxlUGF0aDogc3RyaW5nKSB7XG5cdFx0Y29uc3QgZmlsZSA9IHRoaXMuYXBwLnZhdWx0LmdldEFic3RyYWN0RmlsZUJ5UGF0aChmaWxlUGF0aCk7XG5cdFx0aWYgKCEoZmlsZSBpbnN0YW5jZW9mIFRGaWxlKSkgcmV0dXJuO1xuXG5cdFx0bGV0IGV4aXN0aW5nTGVhZjogV29ya3NwYWNlTGVhZiB8IG51bGwgPSBudWxsO1xuXHRcdHRoaXMuYXBwLndvcmtzcGFjZS5pdGVyYXRlQWxsTGVhdmVzKGxlYWYgPT4ge1xuXHRcdFx0aWYgKGxlYWYudmlldy5nZXRWaWV3VHlwZSgpID09PSBcIm1hcmtkb3duXCIgJiYgKGxlYWYudmlldyBhcyBhbnkpLmZpbGU/LnBhdGggPT09IGZpbGVQYXRoKSB7XG5cdFx0XHRcdGV4aXN0aW5nTGVhZiA9IGxlYWY7XG5cdFx0XHR9XG5cdFx0fSk7XG5cblx0XHRpZiAoZXhpc3RpbmdMZWFmKSB7XG5cdFx0XHR0aGlzLmFwcC53b3Jrc3BhY2Uuc2V0QWN0aXZlTGVhZihleGlzdGluZ0xlYWYsIHsgZm9jdXM6IHRydWUgfSk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdGNvbnN0IG5ld0xlYWYgPSB0aGlzLmFwcC53b3Jrc3BhY2UuZ2V0TGVhZih0cnVlKTtcblx0XHRcdGF3YWl0IG5ld0xlYWYub3BlbkZpbGUoZmlsZSk7XG5cdFx0XHR0aGlzLmFwcC53b3Jrc3BhY2Uuc2V0QWN0aXZlTGVhZihuZXdMZWFmLCB7IGZvY3VzOiB0cnVlIH0pO1xuXHRcdH1cblx0fVxuXG5cdGFzeW5jIGxvYWRTZXR0aW5ncygpIHtcblx0XHR0aGlzLnNldHRpbmdzID0gT2JqZWN0LmFzc2lnbih7fSwgREVGQVVMVF9TRVRUSU5HUywgYXdhaXQgdGhpcy5sb2FkRGF0YSgpKTtcblx0fVxuXG5cdGFzeW5jIHNhdmVTZXR0aW5ncygpIHtcblx0XHRhd2FpdCB0aGlzLnNhdmVEYXRhKHRoaXMuc2V0dGluZ3MpO1xuXHR9XG5cblx0YXN5bmMgY3JlYXRlTmV3Tm90ZSgpIHtcblx0XHRjb25zdCBmaWxlTmFtZSA9IGBOZXcgTm90ZSAke0RhdGUubm93KCl9Lm1kYDtcblx0XHRjb25zdCBmaWxlUGF0aCA9IGAvJHtmaWxlTmFtZX1gO1xuXHRcdGNvbnN0IGZpbGUgPSBhd2FpdCB0aGlzLmFwcC52YXVsdC5jcmVhdGUoZmlsZVBhdGgsIGAjICR7ZmlsZU5hbWV9XFxuYCk7XG5cdFx0YXdhaXQgdGhpcy5hZGRUb0RhdGFiYXNlKGZpbGUpO1xuXHRcdHRoaXMucmVmcmVzaEVhc3lLZWVwVmlld0lmT3BlbigpO1xuXHRcdGNvbnN0IG5ld0xlYWYgPSB0aGlzLmFwcC53b3Jrc3BhY2UuZ2V0TGVhZih0cnVlKTtcblx0XHRhd2FpdCBuZXdMZWFmLm9wZW5GaWxlKGZpbGUpO1xuXHRcdHRoaXMuYXBwLndvcmtzcGFjZS5zZXRBY3RpdmVMZWFmKG5ld0xlYWYsIHsgZm9jdXM6IHRydWUgfSk7XG5cdH1cbn1cbiJdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxzQkFTTztBQUVQLElBQU0sc0JBQXNCO0FBZ0I1QixJQUFNLG1CQUFxQztBQUFBLEVBQzFDLFdBQVc7QUFBQSxFQUNYLFNBQVMsQ0FBQztBQUFBLEVBQ1YsV0FBVztBQUNaO0FBRUEsSUFBTSxlQUFOLGNBQTJCLHlCQUFTO0FBQUEsRUFJbkMsWUFBWSxNQUFxQixRQUFrQjtBQUNsRCxVQUFNLElBQUk7QUFDVixTQUFLLFNBQVM7QUFBQSxFQUNmO0FBQUEsRUFFQSxjQUFzQjtBQUNyQixXQUFPO0FBQUEsRUFDUjtBQUFBLEVBRUEsaUJBQXlCO0FBQ3hCLFdBQU87QUFBQSxFQUNSO0FBQUEsRUFFQSxlQUFlO0FBQ2QsU0FBSyxjQUFjLE1BQU07QUFDekIsU0FBSyxjQUFjLE1BQU0sWUFBWTtBQUNyQyxTQUFLLGNBQWMsTUFBTSxTQUFTO0FBRWxDLFVBQU0sZ0JBQWdCLEtBQUssY0FBYyxVQUFVLDJCQUEyQjtBQUU5RSxVQUFNLGNBQWMsY0FBYyxVQUFVLDhCQUE4QjtBQUMxRSxnQkFBWSxTQUFTLE1BQU0sRUFBRSxNQUFNLElBQUksQ0FBQztBQUN4QyxnQkFBWSxTQUFTLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUVsRCxnQkFBWSxVQUFVLE1BQU07QUFDM0IsV0FBSyxPQUFPLGNBQWM7QUFBQSxJQUMzQjtBQUVBLFFBQUksUUFBUSxLQUFLLE9BQU8sU0FBUyxRQUFRLE1BQU0sRUFBRSxLQUFLLENBQUMsR0FBRyxNQUFNLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLEdBQUcsRUFBRTtBQUU1RixRQUFJLE1BQU0sUUFBUTtBQUNqQixZQUFNLFFBQVEsVUFBUTtBQUNyQixjQUFNLE9BQU8sY0FBYyxVQUFVLGdCQUFnQjtBQUNyRCxhQUFLLFNBQVMsTUFBTSxFQUFFLE1BQU0sS0FBSyxNQUFNLENBQUM7QUFFeEMsWUFBSSxLQUFLLFdBQVc7QUFDbkIsZ0JBQU0sZUFBZSxLQUFLLElBQUksTUFBTSxRQUFRLGdCQUFnQixLQUFLLFNBQVM7QUFDMUUsZ0JBQU0sTUFBTSxLQUFLLFNBQVMsT0FBTyxFQUFFLEtBQUssc0JBQXNCLENBQUM7QUFDL0QsY0FBSSxNQUFNO0FBRVYsY0FBSSxVQUFVLE1BQU07QUFDbkIsZ0JBQUksT0FBTztBQUNYLGdCQUFJLEtBQUssU0FBUztBQUNqQixtQkFBSyxTQUFTLEtBQUssRUFBRSxNQUFNLEtBQUssUUFBUSxDQUFDO0FBQUEsWUFDMUM7QUFBQSxVQUNEO0FBRUEsY0FBSSxTQUFTLE1BQU07QUFDbEIsb0JBQVEsSUFBSSwwQkFBMEIsY0FBYztBQUFBLFVBQ3JEO0FBQUEsUUFDRCxPQUFPO0FBQ04sY0FBSSxLQUFLLFNBQVM7QUFDakIsaUJBQUssU0FBUyxLQUFLLEVBQUUsTUFBTSxLQUFLLFFBQVEsQ0FBQztBQUFBLFVBQzFDO0FBQUEsUUFDRDtBQUVBLGFBQUssVUFBVSxNQUFNLEtBQUssT0FBTyxpQkFBaUIsS0FBSyxJQUFJO0FBQUEsTUFDNUQsQ0FBQztBQUFBLElBQ0YsT0FBTztBQUNOLFlBQU0sWUFBWSxjQUFjLFVBQVUsb0JBQW9CO0FBQzlELGdCQUFVLFFBQVEsNkJBQTZCO0FBQUEsSUFDaEQ7QUFFQSxTQUFLLFVBQVU7QUFBQSxFQUNoQjtBQUFBLEVBRUEsaUJBQWlCO0FBQ2hCLFNBQUssYUFBYTtBQUFBLEVBQ25CO0FBQUEsRUFFQSxNQUFNLFNBQVM7QUFDZCxTQUFLLGdCQUFnQixLQUFLO0FBQzFCLFNBQUssYUFBYTtBQUFBLEVBQ25CO0FBQUEsRUFFQSxNQUFNLFVBQVU7QUFBQSxFQUFDO0FBQUEsRUFFakIsWUFBWTtBQUNYLFVBQU0sZ0JBQWdCLFNBQVMsZUFBZSxlQUFlO0FBQzdELFFBQUk7QUFBZSxvQkFBYyxPQUFPO0FBRXhDLFFBQUksVUFBVTtBQUNkLFFBQUksY0FBYztBQUNsQixRQUFJLFlBQVk7QUFDaEIsVUFBTSxRQUFRLEtBQUssT0FBTyxTQUFTO0FBRW5DLFFBQUksVUFBVSxVQUFXLFVBQVUsWUFBWSxTQUFTLEtBQUssU0FBUyxZQUFZLEdBQUk7QUFDckYsZ0JBQVU7QUFDVixvQkFBYztBQUNkLGtCQUFZO0FBQUEsSUFDYjtBQUVBLFVBQU0sUUFBUSxTQUFTLGNBQWMsT0FBTztBQUM1QyxVQUFNLEtBQUs7QUFDWCxVQUFNLFlBQVk7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLHVCQVdHO0FBQUE7QUFBQSx1QkFFQTtBQUFBO0FBQUE7QUFBQSxZQUdYO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxZQVdBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsdUJBc0JXO0FBQUEsd0JBQ0M7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLFlBS1o7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBTVYsYUFBUyxLQUFLLFlBQVksS0FBSztBQUFBLEVBQ2hDO0FBQ0Q7QUFFQSxJQUFNLG1CQUFOLGNBQStCLGlDQUFpQjtBQUFBLEVBRS9DLFlBQVksS0FBVSxRQUFrQjtBQUN2QyxVQUFNLEtBQUssTUFBTTtBQUNqQixTQUFLLFNBQVM7QUFBQSxFQUNmO0FBQUEsRUFDQSxVQUFnQjtBQUNmLFVBQU0sRUFBRSxZQUFZLElBQUk7QUFDeEIsZ0JBQVksTUFBTTtBQUNsQixnQkFBWSxTQUFTLE1BQU0sRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3JFLFFBQUksd0JBQVEsV0FBVyxFQUN0QixRQUFRLFlBQVksRUFDcEIsUUFBUSw0Q0FBNEMsRUFDcEQsWUFBWSxjQUFZO0FBQ3hCLGVBQVMsVUFBVSxVQUFVLGdCQUFnQjtBQUM3QyxlQUFTLFVBQVUsU0FBUyxPQUFPO0FBQ25DLGVBQVMsVUFBVSxRQUFRLE1BQU07QUFDakMsZUFBUyxTQUFTLEtBQUssT0FBTyxTQUFTLFNBQVM7QUFDaEQsZUFBUyxTQUFTLE9BQU8sVUFBdUM7QUFDL0QsYUFBSyxPQUFPLFNBQVMsWUFBWTtBQUNqQyxjQUFNLEtBQUssT0FBTyxhQUFhO0FBQy9CLGFBQUssT0FBTywwQkFBMEI7QUFBQSxNQUN2QyxDQUFDO0FBQUEsSUFDRixDQUFDO0FBQUEsRUFDRjtBQUNEO0FBRUEsSUFBcUIsV0FBckIsY0FBc0MsdUJBQU87QUFBQSxFQUc1QyxNQUFNLFNBQVM7QUFDZCxVQUFNLEtBQUssYUFBYTtBQUN4QixVQUFNLEtBQUssY0FBYztBQUV6QixTQUFLLGFBQWEscUJBQXFCLENBQUMsU0FBUyxJQUFJLGFBQWEsTUFBTSxJQUFJLENBQUM7QUFFN0UsVUFBTSxlQUFlLEtBQUssY0FBYyxRQUFRLGtCQUFrQixNQUFNO0FBQ3ZFLFdBQUsscUJBQXFCO0FBQUEsSUFDM0IsQ0FBQztBQUNELGlCQUFhLFNBQVMsd0JBQXdCO0FBRTlDLFNBQUssY0FBYyxLQUFLLElBQUksVUFBVSxHQUFHLGFBQWEsT0FBTyxTQUFTO0FBQ3JFLFVBQUksTUFBTTtBQUNULGNBQU0sS0FBSyxjQUFjLElBQUk7QUFDN0IsYUFBSywwQkFBMEI7QUFBQSxNQUNoQztBQUFBLElBQ0QsQ0FBQyxDQUFDO0FBRUYsU0FBSyxjQUFjLEtBQUssSUFBSSxNQUFNLEdBQUcsVUFBVSxPQUFPLFNBQVM7QUFDOUQsVUFBSSxnQkFBZ0IsdUJBQU87QUFDMUIsY0FBTSxLQUFLLG1CQUFtQixLQUFLLElBQUk7QUFDdkMsYUFBSywwQkFBMEI7QUFBQSxNQUNoQztBQUFBLElBQ0QsQ0FBQyxDQUFDO0FBRUYsU0FBSyxjQUFjLEtBQUssSUFBSSxNQUFNLEdBQUcsVUFBVSxPQUFPLE1BQU0sWUFBWTtBQUN2RSxVQUFJLGdCQUFnQix1QkFBTztBQUMxQixjQUFNLGFBQWEsS0FBSyxTQUFTLFFBQVEsVUFBVSxPQUFLLEVBQUUsU0FBUyxPQUFPO0FBQzFFLFlBQUksZUFBZSxJQUFJO0FBQ3RCLGVBQUssU0FBUyxRQUFRLFVBQVUsRUFBRSxPQUFPLEtBQUs7QUFDOUMsZUFBSyxTQUFTLFFBQVEsVUFBVSxFQUFFLFFBQVEsS0FBSztBQUMvQyxnQkFBTSxLQUFLLGFBQWE7QUFDeEIsZUFBSywwQkFBMEI7QUFBQSxRQUNoQztBQUFBLE1BQ0Q7QUFBQSxJQUNELENBQUMsQ0FBQztBQUVGLFNBQUssY0FBYyxLQUFLLElBQUksTUFBTSxHQUFHLFVBQVUsT0FBTyxNQUFNLFlBQVk7QUFDdkUsVUFBSSxnQkFBZ0IsdUJBQU87QUFFMUIsY0FBTSxhQUFhLEtBQUssU0FBUyxRQUFRLFVBQVUsT0FBSyxFQUFFLFNBQVMsT0FBTztBQUMxRSxZQUFJLGVBQWUsSUFBSTtBQUN0QixlQUFLLFNBQVMsUUFBUSxVQUFVLEVBQUUsT0FBTyxLQUFLO0FBQzlDLGVBQUssU0FBUyxRQUFRLFVBQVUsRUFBRSxRQUFRLEtBQUs7QUFDL0MsZ0JBQU0sS0FBSyxhQUFhO0FBQ3hCLGVBQUssMEJBQTBCO0FBQUEsUUFDaEM7QUFHQSxZQUFJLDBCQUEwQixLQUFLLEtBQUssSUFBSSxHQUFHO0FBQzlDLGNBQUksVUFBVTtBQUNkLGVBQUssU0FBUyxRQUFRLFFBQVEsV0FBUztBQUN0QyxnQkFBSSxNQUFNLGNBQWMsU0FBUztBQUNoQyxvQkFBTSxZQUFZLEtBQUs7QUFDdkIsd0JBQVU7QUFBQSxZQUNYO0FBQUEsVUFDRCxDQUFDO0FBQ0QsY0FBSSxTQUFTO0FBQ1osa0JBQU0sS0FBSyxhQUFhO0FBQ3hCLGlCQUFLLDBCQUEwQjtBQUFBLFVBQ2hDO0FBQUEsUUFDRDtBQUFBLE1BQ0Q7QUFBQSxJQUNELENBQUMsQ0FBQztBQUVGLFNBQUssY0FBYyxJQUFJLGlCQUFpQixLQUFLLEtBQUssSUFBSSxDQUFDO0FBQUEsRUFDeEQ7QUFBQSxFQUVBLFdBQVc7QUFDVixTQUFLLElBQUksVUFBVSxtQkFBbUIsbUJBQW1CO0FBQUEsRUFDMUQ7QUFBQSxFQUVBLE1BQU0sdUJBQXVCO0FBQzVCLFVBQU0saUJBQWlCLEtBQUssSUFBSSxVQUFVLGdCQUFnQixtQkFBbUI7QUFDN0UsUUFBSSxlQUFlLFNBQVMsR0FBRztBQUM5QixXQUFLLElBQUksVUFBVSxXQUFXLGVBQWUsQ0FBQyxDQUFDO0FBQy9DO0FBQUEsSUFDRDtBQUVBLFVBQU0sT0FBTyxLQUFLLElBQUksVUFBVSxRQUFRLElBQUk7QUFDNUMsVUFBTSxLQUFLLGFBQWEsRUFBRSxNQUFNLHFCQUFxQixRQUFRLEtBQUssQ0FBQztBQUNuRSxTQUFLLElBQUksVUFBVSxXQUFXLElBQUk7QUFBQSxFQUNuQztBQUFBLEVBRUEsNEJBQTRCO0FBQzNCLFVBQU0saUJBQWlCLEtBQUssSUFBSSxVQUFVLGdCQUFnQixtQkFBbUI7QUFDN0UsUUFBSSxlQUFlLFNBQVMsR0FBRztBQUM5QixZQUFNLE9BQU8sZUFBZSxDQUFDLEVBQUU7QUFDL0IsV0FBSyxlQUFlO0FBQUEsSUFDckI7QUFBQSxFQUNEO0FBQUEsRUFFQSxNQUFNLGNBQWMsTUFBYTtBQUNoQyxVQUFNLFVBQVUsTUFBTSxLQUFLLElBQUksTUFBTSxXQUFXLElBQUk7QUFDcEQsVUFBTSxRQUFRLFFBQVEsS0FBSyxFQUFFLE1BQU0sSUFBSSxFQUFFLE9BQU8sVUFBUSxLQUFLLEtBQUssTUFBTSxFQUFFO0FBQzFFLFFBQUksVUFBVTtBQUNkLFFBQUk7QUFFSixRQUFJLDBCQUEwQixLQUFLLEtBQUssSUFBSSxHQUFHO0FBQzlDLGtCQUFZLEtBQUs7QUFBQSxJQUNsQixPQUFPO0FBQ04sWUFBTSxhQUFhLFFBQVEsTUFBTSxvQ0FBb0M7QUFDckUsVUFBSSxZQUFZO0FBQ2Ysb0JBQVksV0FBVyxDQUFDLEtBQUssV0FBVyxDQUFDO0FBQ3pDLFlBQUksYUFBYSxDQUFDLFVBQVUsV0FBVyxNQUFNLEtBQUssQ0FBQyxVQUFVLFdBQVcsR0FBRyxHQUFHO0FBQzdFLGdCQUFNLGFBQWEsS0FBSyxLQUFLLFVBQVUsR0FBRyxLQUFLLEtBQUssWUFBWSxHQUFHLENBQUM7QUFDcEUsc0JBQVksR0FBRyxjQUFjLFlBQVksUUFBUSxRQUFRLEdBQUc7QUFBQSxRQUM3RDtBQUNBLGNBQU0sWUFBWSxLQUFLLElBQUksTUFBTSxzQkFBc0IsU0FBUztBQUNoRSxZQUFJLEVBQUUscUJBQXFCLHlCQUFTLDBCQUEwQixLQUFLLFNBQVMsSUFBSTtBQUMvRSxzQkFBWTtBQUFBLFFBQ2I7QUFBQSxNQUNEO0FBQUEsSUFDRDtBQUVBLFFBQUksQ0FBQyxhQUFhLE1BQU0sU0FBUyxHQUFHO0FBQ25DLGdCQUFVLE1BQU0sTUFBTSxHQUFHLENBQUMsRUFBRSxLQUFLLEdBQUc7QUFDcEMsVUFBSSxNQUFNLFNBQVM7QUFBRyxrQkFBVSxRQUFRLEtBQUssSUFBSTtBQUFBLElBQ2xEO0FBRUEsVUFBTSxrQkFBa0IsS0FBSyxLQUFLLFFBQVEsOEJBQThCLEVBQUU7QUFDMUUsVUFBTSxXQUFzQjtBQUFBLE1BQzNCLE1BQU0sS0FBSztBQUFBLE1BQ1gsT0FBTztBQUFBLE1BQ1A7QUFBQSxNQUNBLE1BQU0sS0FBSyxJQUFJO0FBQUEsTUFDZjtBQUFBLElBQ0Q7QUFFQSxTQUFLLFNBQVMsVUFBVSxLQUFLLFNBQVMsUUFBUSxPQUFPLFdBQVMsTUFBTSxTQUFTLEtBQUssSUFBSTtBQUN0RixTQUFLLFNBQVMsUUFBUSxRQUFRLFFBQVE7QUFDdEMsVUFBTSxLQUFLLGFBQWE7QUFBQSxFQUN6QjtBQUFBLEVBRUEsTUFBTSxtQkFBbUIsVUFBa0I7QUFDMUMsVUFBTSxnQkFBZ0IsS0FBSyxTQUFTLFFBQVE7QUFDNUMsU0FBSyxTQUFTLFVBQVUsS0FBSyxTQUFTLFFBQVEsT0FBTyxXQUFTLE1BQU0sU0FBUyxRQUFRO0FBQ3JGLFFBQUksS0FBSyxTQUFTLFFBQVEsV0FBVyxlQUFlO0FBQ25ELFlBQU0sS0FBSyxhQUFhO0FBQUEsSUFDekI7QUFBQSxFQUNEO0FBQUEsRUFFQSxNQUFNLGdCQUFnQjtBQUNyQixVQUFNLFFBQVEsS0FBSyxJQUFJLE1BQU0saUJBQWlCO0FBQzlDLFVBQU0sZ0JBQWdCLElBQUksSUFBSSxNQUFNLElBQUksVUFBUSxLQUFLLElBQUksQ0FBQztBQUMxRCxTQUFLLFNBQVMsVUFBVSxLQUFLLFNBQVMsUUFBUSxPQUFPLFdBQVMsY0FBYyxJQUFJLE1BQU0sSUFBSSxDQUFDO0FBQzNGLFVBQU0sS0FBSyxhQUFhO0FBQUEsRUFDekI7QUFBQSxFQUVBLE1BQU0saUJBQWlCLFVBQWtCO0FBQ3hDLFVBQU0sT0FBTyxLQUFLLElBQUksTUFBTSxzQkFBc0IsUUFBUTtBQUMxRCxRQUFJLEVBQUUsZ0JBQWdCO0FBQVE7QUFFOUIsUUFBSSxlQUFxQztBQUN6QyxTQUFLLElBQUksVUFBVSxpQkFBaUIsVUFBUTtBQTVYOUM7QUE2WEcsVUFBSSxLQUFLLEtBQUssWUFBWSxNQUFNLGdCQUFlLFVBQUssS0FBYSxTQUFsQixtQkFBd0IsVUFBUyxVQUFVO0FBQ3pGLHVCQUFlO0FBQUEsTUFDaEI7QUFBQSxJQUNELENBQUM7QUFFRCxRQUFJLGNBQWM7QUFDakIsV0FBSyxJQUFJLFVBQVUsY0FBYyxjQUFjLEVBQUUsT0FBTyxLQUFLLENBQUM7QUFBQSxJQUMvRCxPQUFPO0FBQ04sWUFBTSxVQUFVLEtBQUssSUFBSSxVQUFVLFFBQVEsSUFBSTtBQUMvQyxZQUFNLFFBQVEsU0FBUyxJQUFJO0FBQzNCLFdBQUssSUFBSSxVQUFVLGNBQWMsU0FBUyxFQUFFLE9BQU8sS0FBSyxDQUFDO0FBQUEsSUFDMUQ7QUFBQSxFQUNEO0FBQUEsRUFFQSxNQUFNLGVBQWU7QUFDcEIsU0FBSyxXQUFXLE9BQU8sT0FBTyxDQUFDLEdBQUcsa0JBQWtCLE1BQU0sS0FBSyxTQUFTLENBQUM7QUFBQSxFQUMxRTtBQUFBLEVBRUEsTUFBTSxlQUFlO0FBQ3BCLFVBQU0sS0FBSyxTQUFTLEtBQUssUUFBUTtBQUFBLEVBQ2xDO0FBQUEsRUFFQSxNQUFNLGdCQUFnQjtBQUNyQixVQUFNLFdBQVcsWUFBWSxLQUFLLElBQUk7QUFDdEMsVUFBTSxXQUFXLElBQUk7QUFDckIsVUFBTSxPQUFPLE1BQU0sS0FBSyxJQUFJLE1BQU0sT0FBTyxVQUFVLEtBQUs7QUFBQSxDQUFZO0FBQ3BFLFVBQU0sS0FBSyxjQUFjLElBQUk7QUFDN0IsU0FBSywwQkFBMEI7QUFDL0IsVUFBTSxVQUFVLEtBQUssSUFBSSxVQUFVLFFBQVEsSUFBSTtBQUMvQyxVQUFNLFFBQVEsU0FBUyxJQUFJO0FBQzNCLFNBQUssSUFBSSxVQUFVLGNBQWMsU0FBUyxFQUFFLE9BQU8sS0FBSyxDQUFDO0FBQUEsRUFDMUQ7QUFDRDsiLAogICJuYW1lcyI6IFtdCn0K diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..5d783ca --- /dev/null +++ b/main.ts @@ -0,0 +1,414 @@ +import { + App, + Plugin, + Notice, + WorkspaceLeaf, + ItemView, + TFile, + PluginSettingTab, + Setting, +} from "obsidian"; + +const VIEW_TYPE_EASY_KEEP = "easy-keep-view"; + +interface NoteEntry { + path: string; + title: string; + excerpt: string; + time: number; + imagePath?: string; +} + +interface MyPluginSettings { + mySetting: string; + notesDB: NoteEntry[]; + themeMode: "system" | "light" | "dark"; +} + +const DEFAULT_SETTINGS: MyPluginSettings = { + mySetting: "default", + notesDB: [], + themeMode: "system", +}; + +class EasyKeepView extends ItemView { + plugin: MyPlugin; + private mainContainer: HTMLElement; + + constructor(leaf: WorkspaceLeaf, plugin: MyPlugin) { + super(leaf); + this.plugin = plugin; + } + + getViewType(): string { + return VIEW_TYPE_EASY_KEEP; + } + + getDisplayText(): string { + return "Easy Keep View"; + } + + buildContent() { + this.mainContainer.empty(); + this.mainContainer.style.overflowY = "auto"; + this.mainContainer.style.height = "100%"; + + const cardContainer = this.mainContainer.createDiv("easy-keep-cards-container"); + + const newNoteCard = cardContainer.createDiv("easy-keep-card new-note-card"); + newNoteCard.createEl("h3", { text: "+" }); + newNoteCard.createEl("p", { text: "Add New Note" }); + + newNoteCard.onclick = () => { + this.plugin.createNewNote(); + }; + + let notes = this.plugin.settings.notesDB.slice().sort((a, b) => b.time - a.time).slice(0, 20); + + if (notes.length) { + notes.forEach(note => { + const card = cardContainer.createDiv("easy-keep-card"); + card.createEl("h3", { text: note.title }); + + if (note.imagePath) { + const resourcePath = this.app.vault.adapter.getResourcePath(note.imagePath); + const img = card.createEl("img", { cls: "easy-keep-thumbnail" }); + img.src = resourcePath; + + img.onerror = () => { + img.remove(); + if (note.excerpt) { + card.createEl("p", { text: note.excerpt }); + } + }; + + img.onload = () => { + console.log(`[Render] Image loaded: ${resourcePath}`); + }; + } else { + if (note.excerpt) { + card.createEl("p", { text: note.excerpt }); + } + } + + card.onclick = () => this.plugin.openNoteInNewTab(note.path); + }); + } else { + const noHistory = cardContainer.createDiv("no-history-message"); + noHistory.setText("No notes, create a new one!"); + } + + this.injectCSS(); + } + + refreshContent() { + this.buildContent(); + } + + async onOpen() { + this.mainContainer = this.containerEl; + this.buildContent(); + } + + async onClose() {} + + injectCSS() { + const existingStyle = document.getElementById("easy-keep-css"); + if (existingStyle) existingStyle.remove(); + + let bgColor = "#f9f9f9"; + let borderColor = "#ddd"; + let textColor = "#555"; + const theme = this.plugin.settings.themeMode; + + if (theme === "dark" || (theme === "system" && document.body.hasClass("theme-dark"))) { + bgColor = "#333"; + borderColor = "#555"; + textColor = "#ccc"; + } + + const style = document.createElement("style"); + style.id = "easy-keep-css"; + style.innerText = ` + .easy-keep-cards-container { + display: flex; + flex-wrap: wrap; + gap: 16px; + padding: 10px; + } + .easy-keep-card { + width: 250px; + min-height: 150px; + padding: 16px; + border: 1px solid ${borderColor}; + border-radius: 8px; + background-color: ${bgColor}; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + cursor: pointer; + color: ${textColor}; + display: flex; + flex-direction: column; + justify-content: space-between; + } + .easy-keep-card h3 { + font-size: 18px; + margin-bottom: 8px; + } + .easy-keep-card p { + font-size: 14px; + color: ${textColor}; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + margin: 0; + } + .easy-keep-thumbnail { + max-width: 100%; + max-height: 100px; + object-fit: cover; + border-radius: 4px; + margin-top: 8px; + display: block; + } + .new-note-card { + width: 250px; + height: 150px; + display: flex; + align-items: center; + justify-content: center; + background-color: ${bgColor}; + border: 2px dashed ${borderColor}; + cursor: pointer; + } + .no-history-message { + font-size: 20px; + color: ${textColor}; + text-align: center; + padding: 20px; + width: 100%; + } + `; + document.head.appendChild(style); + } +} + +class SampleSettingTab extends PluginSettingTab { + plugin: MyPlugin; + constructor(app: App, plugin: MyPlugin) { + super(app, plugin); + this.plugin = plugin; + } + display(): void { + const { containerEl } = this; + containerEl.empty(); + containerEl.createEl("h2", { text: "Easy Keep View Plugin Settings" }); + new Setting(containerEl) + .setName("Theme Mode") + .setDesc("Select a theme mode for the Easy Keep View") + .addDropdown(dropdown => { + dropdown.addOption("system", "System (adapt)"); + dropdown.addOption("light", "Light"); + dropdown.addOption("dark", "Dark"); + dropdown.setValue(this.plugin.settings.themeMode); + dropdown.onChange(async (value: "system" | "light" | "dark") => { + this.plugin.settings.themeMode = value; + await this.plugin.saveSettings(); + this.plugin.refreshEasyKeepViewIfOpen(); + }); + }); + } +} + +export default class MyPlugin extends Plugin { + settings: MyPluginSettings; + + async onload() { + await this.loadSettings(); + await this.cleanDatabase(); + + this.registerView(VIEW_TYPE_EASY_KEEP, (leaf) => new EasyKeepView(leaf, this)); + + const ribbonIconEl = this.addRibbonIcon("dice", "Easy Keep View", () => { + this.activateEasyKeepView(); + }); + ribbonIconEl.addClass("my-plugin-ribbon-class"); + + this.registerEvent(this.app.workspace.on("file-open", async (file) => { + if (file) { + await this.addToDatabase(file); + this.refreshEasyKeepViewIfOpen(); + } + })); + + this.registerEvent(this.app.vault.on("delete", async (file) => { + if (file instanceof TFile) { + await this.removeFromDatabase(file.path); + this.refreshEasyKeepViewIfOpen(); + } + })); + + this.registerEvent(this.app.vault.on("rename", async (file, oldPath) => { + if (file instanceof TFile) { + const entryIndex = this.settings.notesDB.findIndex(e => e.path === oldPath); + if (entryIndex !== -1) { + this.settings.notesDB[entryIndex].path = file.path; + this.settings.notesDB[entryIndex].title = file.basename; + await this.saveSettings(); + this.refreshEasyKeepViewIfOpen(); + } + } + })); + + this.registerEvent(this.app.vault.on("rename", async (file, oldPath) => { + if (file instanceof TFile) { + // Update renamed note file + const entryIndex = this.settings.notesDB.findIndex(e => e.path === oldPath); + if (entryIndex !== -1) { + this.settings.notesDB[entryIndex].path = file.path; + this.settings.notesDB[entryIndex].title = file.basename; + await this.saveSettings(); + this.refreshEasyKeepViewIfOpen(); + } + + // Update renamed image file + if (/\.(jpg|jpeg|png|webp)$/i.test(file.path)) { + let updated = false; + this.settings.notesDB.forEach(entry => { + if (entry.imagePath === oldPath) { + entry.imagePath = file.path; + updated = true; + } + }); + if (updated) { + await this.saveSettings(); + this.refreshEasyKeepViewIfOpen(); + } + } + } + })); + + this.addSettingTab(new SampleSettingTab(this.app, this)); + } + + onunload() { + this.app.workspace.detachLeavesOfType(VIEW_TYPE_EASY_KEEP); + } + + async activateEasyKeepView() { + const existingLeaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EASY_KEEP); + if (existingLeaves.length > 0) { + this.app.workspace.revealLeaf(existingLeaves[0]); + return; + } + + const leaf = this.app.workspace.getLeaf(true); + await leaf.setViewState({ type: VIEW_TYPE_EASY_KEEP, active: true }); + this.app.workspace.revealLeaf(leaf); + } + + refreshEasyKeepViewIfOpen() { + const existingLeaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EASY_KEEP); + if (existingLeaves.length > 0) { + const view = existingLeaves[0].view as EasyKeepView; + view.refreshContent(); + } + } + + async addToDatabase(file: TFile) { + const content = await this.app.vault.cachedRead(file); + const lines = content.trim().split("\n").filter(line => line.trim() !== ""); + let excerpt = ""; + let imagePath: string | undefined; + + if (/\.(jpg|jpeg|png|webp)$/i.test(file.path)) { + imagePath = file.path; + } else { + const imageMatch = content.match(/!\[.*?\]\((.*?)\)|\!\[\[(.*?)\]\]/i); + if (imageMatch) { + imagePath = imageMatch[1] || imageMatch[2]; + if (imagePath && !imagePath.startsWith("http") && !imagePath.startsWith("/")) { + const fileFolder = file.path.substring(0, file.path.lastIndexOf("/")); + imagePath = `${fileFolder}/${imagePath}`.replace(/\/+/g, "/"); + } + const imageFile = this.app.vault.getAbstractFileByPath(imagePath); + if (!(imageFile instanceof TFile && /\.(jpg|jpeg|png|webp)$/i.test(imagePath))) { + imagePath = undefined; + } + } + } + + if (!imagePath && lines.length > 0) { + excerpt = lines.slice(0, 2).join(" "); + if (lines.length > 2) excerpt = excerpt.trim() + " …"; + } + + const titleWithoutExt = file.name.replace(/\.(md|png|jpg|jpeg|webp)$/i, ""); + const newEntry: NoteEntry = { + path: file.path, + title: titleWithoutExt, + excerpt, + time: Date.now(), + imagePath, + }; + + this.settings.notesDB = this.settings.notesDB.filter(entry => entry.path !== file.path); + this.settings.notesDB.unshift(newEntry); + await this.saveSettings(); + } + + async removeFromDatabase(filePath: string) { + const initialLength = this.settings.notesDB.length; + this.settings.notesDB = this.settings.notesDB.filter(entry => entry.path !== filePath); + if (this.settings.notesDB.length !== initialLength) { + await this.saveSettings(); + } + } + + async cleanDatabase() { + const files = this.app.vault.getMarkdownFiles(); + const existingPaths = new Set(files.map(file => file.path)); + this.settings.notesDB = this.settings.notesDB.filter(entry => existingPaths.has(entry.path)); + await this.saveSettings(); + } + + async openNoteInNewTab(filePath: string) { + const file = this.app.vault.getAbstractFileByPath(filePath); + if (!(file instanceof TFile)) return; + + let existingLeaf: WorkspaceLeaf | null = null; + this.app.workspace.iterateAllLeaves(leaf => { + if (leaf.view.getViewType() === "markdown" && (leaf.view as any).file?.path === filePath) { + existingLeaf = leaf; + } + }); + + if (existingLeaf) { + this.app.workspace.setActiveLeaf(existingLeaf, { focus: true }); + } else { + const newLeaf = this.app.workspace.getLeaf(true); + await newLeaf.openFile(file); + this.app.workspace.setActiveLeaf(newLeaf, { focus: true }); + } + } + + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } + + async saveSettings() { + await this.saveData(this.settings); + } + + async createNewNote() { + const fileName = `New Note ${Date.now()}.md`; + const filePath = `/${fileName}`; + const file = await this.app.vault.create(filePath, `# ${fileName}\n`); + await this.addToDatabase(file); + this.refreshEasyKeepViewIfOpen(); + const newLeaf = this.app.workspace.getLeaf(true); + await newLeaf.openFile(file); + this.app.workspace.setActiveLeaf(newLeaf, { focus: true }); + } +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..dfa940e --- /dev/null +++ b/manifest.json @@ -0,0 +1,11 @@ +{ + "id": "sample-plugin", + "name": "Sample Plugin", + "version": "1.0.0", + "minAppVersion": "0.15.0", + "description": "Demonstrates some of the capabilities of the Obsidian API.", + "author": "Obsidian", + "authorUrl": "https://obsidian.md", + "fundingUrl": "https://obsidian.md/pricing", + "isDesktopOnly": false +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f2c551f --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "obsidian-easy-keep-view", + "version": "1.0.0", + "description": "An Obsidian plugin for mimicking the Google Keep interface, allowing you to view and manage your notes with image thumbnails and excerpts.", + "main": "main.js", + "scripts": { + "dev": "node esbuild.config.mjs", + "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", + "version": "node version-bump.mjs && git add manifest.json versions.json" + }, + "keywords": [ + "obsidian", + "plugin", + "notes", + "google keep", + "easy keep view" + ], + "author": "@tazihad", + "license": "MIT", + "devDependencies": { + "@types/node": "^16.11.6", + "@typescript-eslint/eslint-plugin": "5.29.0", + "@typescript-eslint/parser": "5.29.0", + "builtin-modules": "3.3.0", + "esbuild": "0.17.3", + "obsidian": "latest", + "tslib": "2.4.0", + "typescript": "4.7.4" + } +} diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..71cc60f --- /dev/null +++ b/styles.css @@ -0,0 +1,8 @@ +/* + +This CSS file will be included with your plugin, and +available in the app when your plugin is enabled. + +If your plugin does not need CSS, delete this file. + +*/ diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c44b729 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "inlineSourceMap": true, + "inlineSources": true, + "module": "ESNext", + "target": "ES6", + "allowJs": true, + "noImplicitAny": true, + "moduleResolution": "node", + "importHelpers": true, + "isolatedModules": true, + "strictNullChecks": true, + "lib": [ + "DOM", + "ES5", + "ES6", + "ES7" + ] + }, + "include": [ + "**/*.ts" + ] +} diff --git a/version-bump.mjs b/version-bump.mjs new file mode 100644 index 0000000..d409fa0 --- /dev/null +++ b/version-bump.mjs @@ -0,0 +1,14 @@ +import { readFileSync, writeFileSync } from "fs"; + +const targetVersion = process.env.npm_package_version; + +// read minAppVersion from manifest.json and bump version to target version +let manifest = JSON.parse(readFileSync("manifest.json", "utf8")); +const { minAppVersion } = manifest; +manifest.version = targetVersion; +writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); + +// update versions.json with target version and minAppVersion from manifest.json +let versions = JSON.parse(readFileSync("versions.json", "utf8")); +versions[targetVersion] = minAppVersion; +writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); diff --git a/versions.json b/versions.json new file mode 100644 index 0000000..479cc62 --- /dev/null +++ b/versions.json @@ -0,0 +1,3 @@ +{ + "0.0.1": "1.0.0" +}