2022-08-21 17:37:20 +02:00
/ *
THIS IS A GENERATED / BUNDLED FILE BY ESBUILD
if you want to view the source , please visit the github repository of this plugin
* /
var _ _create = Object . create ;
var _ _defProp = Object . defineProperty ;
var _ _getOwnPropDesc = Object . getOwnPropertyDescriptor ;
var _ _getOwnPropNames = Object . getOwnPropertyNames ;
var _ _getProtoOf = Object . getPrototypeOf ;
var _ _hasOwnProp = Object . prototype . hasOwnProperty ;
var _ _markAsModule = ( target ) => _ _defProp ( target , "__esModule" , { value : true } ) ;
var _ _export = ( target , all ) => {
_ _markAsModule ( target ) ;
for ( var name in all )
_ _defProp ( target , name , { get : all [ name ] , enumerable : true } ) ;
} ;
var _ _reExport = ( target , module2 , desc ) => {
if ( module2 && typeof module2 === "object" || typeof module2 === "function" ) {
for ( let key of _ _getOwnPropNames ( module2 ) )
if ( ! _ _hasOwnProp . call ( target , key ) && key !== "default" )
_ _defProp ( target , key , { get : ( ) => module2 [ key ] , enumerable : ! ( desc = _ _getOwnPropDesc ( module2 , key ) ) || desc . enumerable } ) ;
}
return target ;
} ;
var _ _toModule = ( module2 ) => {
return _ _reExport ( _ _markAsModule ( _ _defProp ( module2 != null ? _ _create ( _ _getProtoOf ( module2 ) ) : { } , "default" , module2 && module2 . _ _esModule && "default" in module2 ? { get : ( ) => module2 . default , enumerable : true } : { value : module2 , enumerable : true } ) ) , module2 ) ;
} ;
var _ _async = ( _ _this , _ _arguments , generator ) => {
return new Promise ( ( resolve , reject ) => {
var fulfilled = ( value ) => {
try {
step ( generator . next ( value ) ) ;
} catch ( e ) {
reject ( e ) ;
}
} ;
var rejected = ( value ) => {
try {
step ( generator . throw ( value ) ) ;
} catch ( e ) {
reject ( e ) ;
}
} ;
var step = ( x ) => x . done ? resolve ( x . value ) : Promise . resolve ( x . value ) . then ( fulfilled , rejected ) ;
step ( ( generator = generator . apply ( _ _this , _ _arguments ) ) . next ( ) ) ;
} ) ;
} ;
// src/main.ts
_ _export ( exports , {
default : ( ) => CustomFramesPlugin
} ) ;
var import _obsidian4 = _ _toModule ( require ( "obsidian" ) ) ;
// src/frame.ts
var import _obsidian = _ _toModule ( require ( "obsidian" ) ) ;
var CustomFrame = class {
constructor ( settings , data ) {
this . settings = settings ;
this . data = data ;
}
create ( additionalStyle = void 0 , urlSuffix = void 0 ) {
let style = ` padding: ${ this . settings . padding } px; ` ;
if ( additionalStyle )
style += additionalStyle ;
if ( import _obsidian . Platform . isDesktopApp && ! this . data . forceIframe ) {
this . frame = document . createElement ( "webview" ) ;
this . frame . setAttribute ( "allowpopups" , "" ) ;
this . frame . addEventListener ( "dom-ready" , ( ) => {
this . frame . setZoomFactor ( this . data . zoomLevel ) ;
this . frame . insertCSS ( this . data . customCss ) ;
} ) ;
} else {
this . frame = document . createElement ( "iframe" ) ;
this . frame . setAttribute ( "sandbox" , "allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts allow-top-navigation-by-user-activation" ) ;
this . frame . setAttribute ( "allow" , "encrypted-media; fullscreen; oversized-images; picture-in-picture; sync-xhr; geolocation;" ) ;
style += ` transform: scale( ${ this . data . zoomLevel } ); transform-origin: 0 0; ` ;
}
this . frame . addClass ( "custom-frames-frame" ) ;
this . frame . setAttribute ( "style" , style ) ;
let src = this . data . url ;
if ( urlSuffix ) {
if ( ! urlSuffix . startsWith ( "/" ) )
src += "/" ;
src += urlSuffix ;
}
this . frame . setAttribute ( "src" , src ) ;
return this . frame ;
}
refresh ( ) {
if ( this . frame instanceof HTMLIFrameElement ) {
this . frame . contentWindow . location . reload ( ) ;
} else {
this . frame . reload ( ) ;
}
}
return ( ) {
if ( this . frame instanceof HTMLIFrameElement ) {
this . frame . contentWindow . open ( this . data . url ) ;
} else {
this . frame . loadURL ( this . data . url ) ;
}
}
goBack ( ) {
if ( this . frame instanceof HTMLIFrameElement ) {
this . frame . contentWindow . history . back ( ) ;
} else {
this . frame . goBack ( ) ;
}
}
goForward ( ) {
if ( this . frame instanceof HTMLIFrameElement ) {
this . frame . contentWindow . history . forward ( ) ;
} else {
this . frame . goForward ( ) ;
}
}
toggleDevTools ( ) {
if ( ! ( this . frame instanceof HTMLIFrameElement ) ) {
if ( ! this . frame . isDevToolsOpened ( ) ) {
this . frame . openDevTools ( ) ;
} else {
this . frame . closeDevTools ( ) ;
}
}
}
getCurrentUrl ( ) {
return this . frame instanceof HTMLIFrameElement ? this . frame . contentWindow . location . href : this . frame . getURL ( ) ;
}
} ;
// src/settings.ts
var defaultSettings = {
frames : [ ] ,
padding : 5
} ;
var presets = {
"obsidian" : {
url : "https://forum.obsidian.md/" ,
displayName : "Obsidian Forum" ,
icon : "edit" ,
hideOnMobile : true ,
addRibbonIcon : true ,
openInCenter : true ,
zoomLevel : 1 ,
forceIframe : false ,
customCss : ""
} ,
"calendar" : {
url : "https://calendar.google.com/calendar/u/0/r/day" ,
displayName : "Google Calendar" ,
icon : "calendar" ,
hideOnMobile : true ,
addRibbonIcon : true ,
openInCenter : true ,
zoomLevel : 1 ,
forceIframe : false ,
customCss : ` /* hide right-side menu, and some buttons */
div . d6McF ,
div . pw6cBb ,
div . gb _Td . gb _Va . gb _Id ,
div . Kk7lMc - QWPxkf - LgbsSe - haAclf ,
div . h8Aqhb ,
div . gboEAb ,
div . dwlvNd {
display : none ! important ;
} `
} ,
"keep" : {
url : "https://keep.google.com" ,
displayName : "Google Keep" ,
icon : "files" ,
hideOnMobile : true ,
addRibbonIcon : false ,
openInCenter : false ,
zoomLevel : 1 ,
forceIframe : false ,
customCss : ` /* hide the menu bar and the "Keep" text */
html > body > div : nth - child ( 2 ) > div : nth - child ( 2 ) > div : first - child ,
html > body > div : first - child > header : first - child > div > div : first - child > div > div : first - child > a : first - child > span {
display : none ! important ;
} `
} ,
"todoist" : {
url : "https://todoist.com" ,
displayName : "Todoist" ,
icon : "list-checks" ,
hideOnMobile : true ,
addRibbonIcon : false ,
openInCenter : false ,
zoomLevel : 1 ,
forceIframe : false ,
customCss : ` /* hide the help, home, search, and productivity overview buttons, create extra space, and prevent toast pop-up from acting weird */
[ aria - label = "Go to Home view" ] , # quick _find , [ aria - label = "Productivity" ] , [ aria - label = "Help & Feedback" ] {
display : none ! important ;
}
. view _content {
padding - left : 15 px ;
}
. view _header {
padding - left : 15 px ;
padding - top : 10 px ;
}
. undo _toast {
width : 95 % ;
} `
} ,
"notion" : {
url : "https://www.notion.so/" ,
displayName : "Notion" ,
icon : "box" ,
hideOnMobile : true ,
addRibbonIcon : true ,
openInCenter : true ,
zoomLevel : 1 ,
forceIframe : false ,
customCss : ""
} ,
"twitter" : {
url : "https://twitter.com" ,
displayName : "Twitter" ,
icon : "twitter" ,
hideOnMobile : true ,
addRibbonIcon : false ,
openInCenter : false ,
zoomLevel : 1 ,
forceIframe : false ,
customCss : ""
}
} ;
function getIcon ( settings ) {
return settings . icon ? ` lucide- ${ settings . icon } ` : "documents" ;
}
// src/settings-tab.ts
var import _obsidian2 = _ _toModule ( require ( "obsidian" ) ) ;
var CustomFramesSettingTab = class extends import _obsidian2 . PluginSettingTab {
constructor ( app , plugin ) {
super ( app , plugin ) ;
this . plugin = plugin ;
}
display ( ) {
this . containerEl . empty ( ) ;
this . containerEl . createEl ( "h2" , { text : "Custom Frames Settings" } ) ;
2022-08-21 17:49:57 +02:00
this . containerEl . createEl ( "p" , { text : "Please note that Obsidian has to be restarted or reloaded for most of these settings to take effect." , cls : "mod-warning" } ) ;
2022-08-21 17:37:20 +02:00
new import _obsidian2 . Setting ( this . containerEl ) . setName ( "Frame Padding" ) . setDesc ( "The padding that should be left around the inside of custom frame panes, in pixels." ) . addText ( ( t ) => {
t . inputEl . type = "number" ;
t . setValue ( String ( this . plugin . settings . padding ) ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
this . plugin . settings . padding = v . length ? Number ( v ) : defaultSettings . padding ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
for ( let frame of this . plugin . settings . frames ) {
let heading = this . containerEl . createEl ( "h3" , { text : frame . displayName || "Unnamed Frame" } ) ;
let toggle = new import _obsidian2 . ButtonComponent ( this . containerEl ) . setButtonText ( "Show Settings" ) . setClass ( "custom-frames-show" ) . onClick ( ( ) => _ _async ( this , null , function * ( ) {
content . hidden = ! content . hidden ;
toggle . setButtonText ( content . hidden ? "Show Settings" : "Hide Settings" ) ;
} ) ) ;
let content = this . containerEl . createDiv ( ) ;
content . hidden = true ;
new import _obsidian2 . Setting ( content ) . setName ( "Display Name" ) . setDesc ( "The display name that this frame should have." ) . addText ( ( t ) => {
t . setValue ( frame . displayName ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
frame . displayName = v ;
heading . setText ( frame . displayName || "Unnamed Frame" ) ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
new import _obsidian2 . Setting ( content ) . setName ( "Icon" ) . setDesc ( createFragment ( ( f ) => {
f . createSpan ( { text : "The icon that this frame's pane should have. The names of any " } ) ;
f . createEl ( "a" , { text : "Lucide icons" , href : "https://lucide.dev/" } ) ;
f . createSpan ( { text : " can be used." } ) ;
} ) ) . addText ( ( t ) => {
t . setValue ( frame . icon ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
frame . icon = v ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
new import _obsidian2 . Setting ( content ) . setName ( "URL" ) . setDesc ( "The URL that should be opened in this frame." ) . addText ( ( t ) => {
t . setValue ( frame . url ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
frame . url = v ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
new import _obsidian2 . Setting ( content ) . setName ( "Disable on Mobile" ) . setDesc ( "Custom Frames is a lot more restricted on mobile devices and doesn't allow for the same types of content to be displayed. If a frame doesn't work as expected on mobile, it can be disabled." ) . addToggle ( ( t ) => {
t . setValue ( frame . hideOnMobile ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
frame . hideOnMobile = v ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
new import _obsidian2 . Setting ( content ) . setName ( "Add Ribbon Icon" ) . setDesc ( "Whether a button to open this frame should be added to the ribbon." ) . addToggle ( ( t ) => {
t . setValue ( frame . addRibbonIcon ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
frame . addRibbonIcon = v ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
new import _obsidian2 . Setting ( content ) . setName ( "Open in Center" ) . setDesc ( "Whether this frame should be opened in the unpinned center editor rather than one of the panes on the side. This is useful for sites that don't work well in a narrow view, or sites that don't require a note to be open when viewed." ) . addToggle ( ( t ) => {
t . setValue ( frame . openInCenter ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
frame . openInCenter = v ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
new import _obsidian2 . Setting ( content ) . setName ( "Force iframe" ) . setDesc ( createFragment ( ( f ) => {
f . createSpan ( { text : "Whether this frame should use iframes on desktop as opposed to Electron webviews." } ) ;
f . createEl ( "br" ) ;
f . createEl ( "em" , { text : "Only enable this setting if the frame is causing issues or frequent crashes. This setting causes all Desktop-only settings to be ignored." } ) ;
} ) ) . addToggle ( ( t ) => {
t . setValue ( frame . forceIframe ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
frame . forceIframe = v ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
new import _obsidian2 . Setting ( content ) . setName ( "Page Zoom" ) . setDesc ( "The zoom that this frame's page should be displayed with, as a percentage." ) . addText ( ( t ) => {
t . inputEl . type = "number" ;
t . setValue ( String ( frame . zoomLevel * 100 ) ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
frame . zoomLevel = v . length ? Number ( v ) / 100 : 1 ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
new import _obsidian2 . Setting ( content ) . setName ( "Additional CSS" ) . setDesc ( createFragment ( ( f ) => {
f . createSpan ( { text : "A snippet of additional CSS that should be applied to this frame." } ) ;
f . createEl ( "br" ) ;
f . createEl ( "em" , { text : "Note that this is only applied on Desktop." } ) ;
} ) ) . addTextArea ( ( t ) => {
t . inputEl . rows = 5 ;
t . inputEl . cols = 50 ;
t . setValue ( frame . customCss ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
frame . customCss = v ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
new import _obsidian2 . ButtonComponent ( content ) . setButtonText ( "Remove Frame" ) . onClick ( ( ) => _ _async ( this , null , function * ( ) {
this . plugin . settings . frames . remove ( frame ) ;
yield this . plugin . saveSettings ( ) ;
this . display ( ) ;
} ) ) ;
}
this . containerEl . createEl ( "hr" ) ;
2022-08-21 17:49:57 +02:00
this . containerEl . createEl ( "p" , { text : ` Create a new frame, either from a preset shipped with the plugin, or a custom one that you can edit yourself. Each frame's pane can be opened using the "Custom Frames: Open" command. ` } ) ;
2022-08-21 17:37:20 +02:00
let addDiv = this . containerEl . createDiv ( ) ;
let dropdown = new import _obsidian2 . DropdownComponent ( addDiv ) ;
dropdown . addOption ( "new" , "Custom" ) ;
for ( let key of Object . keys ( presets ) )
dropdown . addOption ( key , presets [ key ] . displayName ) ;
new import _obsidian2 . ButtonComponent ( addDiv ) . setButtonText ( "Add Frame" ) . setClass ( "custom-frames-add" ) . onClick ( ( ) => _ _async ( this , null , function * ( ) {
let option = dropdown . getValue ( ) ;
if ( option == "new" ) {
this . plugin . settings . frames . push ( {
url : "" ,
displayName : "New Frame" ,
icon : "" ,
hideOnMobile : true ,
addRibbonIcon : false ,
openInCenter : false ,
zoomLevel : 1 ,
forceIframe : false ,
customCss : ""
} ) ;
} else {
this . plugin . settings . frames . push ( presets [ option ] ) ;
}
yield this . plugin . saveSettings ( ) ;
this . display ( ) ;
} ) ) ;
this . containerEl . createEl ( "hr" ) ;
this . containerEl . createEl ( "p" , { text : "If you like this plugin and want to support its development, you can do so through my website by clicking this fancy image!" } ) ;
this . containerEl . createEl ( "a" , { href : "https://ellpeck.de/support" } ) . createEl ( "img" , { attr : { src : "https://ellpeck.de/res/generalsupport.png" } , cls : "custom-frames-support" } ) ;
}
} ;
// src/view.ts
var import _obsidian3 = _ _toModule ( require ( "obsidian" ) ) ;
var _CustomFrameView = class extends import _obsidian3 . ItemView {
constructor ( leaf , settings , data , name ) {
super ( leaf ) ;
this . data = data ;
this . name = name ;
this . frame = new CustomFrame ( settings , data ) ;
for ( let action of _CustomFrameView . actions )
this . addAction ( action . icon , action . name , ( ) => action . action ( this ) ) ;
}
onload ( ) {
this . contentEl . empty ( ) ;
this . contentEl . addClass ( "custom-frames-view" ) ;
this . contentEl . appendChild ( this . frame . create ( ) ) ;
}
onHeaderMenu ( menu ) {
super . onHeaderMenu ( menu ) ;
for ( let action of _CustomFrameView . actions ) {
menu . addItem ( ( i ) => {
i . setTitle ( action . name ) ;
i . setIcon ( action . icon ) ;
i . onClick ( ( ) => action . action ( this ) ) ;
} ) ;
}
}
getViewType ( ) {
return this . name ;
}
getDisplayText ( ) {
return this . data . displayName ;
}
getIcon ( ) {
return getIcon ( this . data ) ;
}
} ;
var CustomFrameView = _CustomFrameView ;
CustomFrameView . actions = [
{
name : "Return to original page" ,
icon : "home" ,
action : ( v ) => v . frame . return ( )
} ,
{
name : "Open dev tools" ,
icon : "binary" ,
action : ( v ) => v . frame . toggleDevTools ( )
} ,
{
name : "Copy link" ,
icon : "link" ,
action : ( v ) => navigator . clipboard . writeText ( v . frame . getCurrentUrl ( ) )
} ,
{
name : "Open in browser" ,
icon : "globe" ,
action : ( v ) => open ( v . frame . getCurrentUrl ( ) )
} ,
{
name : "Refresh" ,
icon : "refresh-cw" ,
action : ( v ) => v . frame . refresh ( )
} ,
{
name : "Go back" ,
icon : "arrow-left" ,
action : ( v ) => v . frame . goBack ( )
} ,
{
name : "Go forward" ,
icon : "arrow-right" ,
action : ( v ) => v . frame . goForward ( )
}
] ;
// src/main.ts
var CustomFramesPlugin = class extends import _obsidian4 . Plugin {
onload ( ) {
return _ _async ( this , null , function * ( ) {
yield this . loadSettings ( ) ;
for ( let frame of this . settings . frames ) {
if ( ! frame . url || ! frame . displayName )
continue ;
let name = ` custom-frames- ${ frame . displayName . toLowerCase ( ) . replace ( /\s/g , "-" ) } ` ;
if ( import _obsidian4 . Platform . isMobileApp && frame . hideOnMobile ) {
console . log ( ` Skipping frame ${ name } which is hidden on mobile ` ) ;
continue ;
}
try {
console . log ( ` Registering frame ${ name } for URL ${ frame . url } ` ) ;
this . registerView ( name , ( l ) => new CustomFrameView ( l , this . settings , frame , name ) ) ;
this . addCommand ( {
id : ` open- ${ name } ` ,
name : ` Open ${ frame . displayName } ` ,
callback : ( ) => this . openLeaf ( name , frame . openInCenter , false )
} ) ;
if ( frame . addRibbonIcon )
this . addRibbonIcon ( getIcon ( frame ) , ` Open ${ frame . displayName } ` , ( e ) => this . openLeaf ( name , frame . openInCenter , import _obsidian4 . Platform . isMacOS ? e . metaKey : e . ctrlKey ) ) ;
} catch ( e ) {
console . error ( ` Couldn't register frame ${ name } , is there already one with the same name? ` ) ;
}
}
this . addSettingTab ( new CustomFramesSettingTab ( this . app , this ) ) ;
this . registerMarkdownCodeBlockProcessor ( "custom-frames" , ( s , e ) => {
e . empty ( ) ;
e . addClass ( "custom-frames-view-file" ) ;
let frameMatch = /frame:([^\n]+)/gi . exec ( s ) ;
let frameName = frameMatch && frameMatch [ 1 ] . trim ( ) ;
if ( ! frameName ) {
e . createSpan ( { text : "Couldn't parse frame name" } ) ;
return ;
}
let data = this . settings . frames . find ( ( f ) => f . displayName == frameName ) ;
if ( ! data ) {
e . createSpan ( { text : ` Couldn't find a frame with name ${ frameName } ` } ) ;
return ;
}
if ( import _obsidian4 . Platform . isMobileApp && data . hideOnMobile ) {
e . createSpan ( { text : ` ${ frameName } is hidden on mobile ` } ) ;
return ;
}
let styleMatch = /style:([^\n]+)/gi . exec ( s ) ;
let style = styleMatch && styleMatch [ 1 ] . trim ( ) ;
style || ( style = "height: 600px;" ) ;
let urlSuffixMatch = /urlsuffix:([^\n]+)/gi . exec ( s ) ;
let urlSuffix = urlSuffixMatch && urlSuffixMatch [ 1 ] . trim ( ) ;
urlSuffix || ( urlSuffix = "" ) ;
let frame = new CustomFrame ( this . settings , data ) ;
e . appendChild ( frame . create ( style , urlSuffix ) ) ;
} ) ;
} ) ;
}
loadSettings ( ) {
return _ _async ( this , null , function * ( ) {
this . settings = Object . assign ( { } , defaultSettings , yield this . loadData ( ) ) ;
} ) ;
}
saveSettings ( ) {
return _ _async ( this , null , function * ( ) {
yield this . saveData ( this . settings ) ;
} ) ;
}
openLeaf ( name , center , split ) {
return _ _async ( this , null , function * ( ) {
if ( center ) {
this . app . workspace . detachLeavesOfType ( name ) ;
let leaf = this . app . workspace . getLeaf ( split ) ;
yield leaf . setViewState ( { type : name , active : true } ) ;
} else {
if ( ! this . app . workspace . getLeavesOfType ( name ) . length )
yield this . app . workspace . getRightLeaf ( false ) . setViewState ( { type : name , active : true } ) ;
this . app . workspace . revealLeaf ( this . app . workspace . getLeavesOfType ( name ) [ 0 ] ) ;
}
} ) ;
}
} ;
2022-08-21 17:49:57 +02:00
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjL21haW4udHMiLCAic3JjL2ZyYW1lLnRzIiwgInNyYy9zZXR0aW5ncy50cyIsICJzcmMvc2V0dGluZ3MtdGFiLnRzIiwgInNyYy92aWV3LnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJpbXBvcnQgeyBQbHVnaW4sIFBsYXRmb3JtIH0gZnJvbSBcIm9ic2lkaWFuXCI7XHJcbmltcG9ydCB7IEN1c3RvbUZyYW1lIH0gZnJvbSBcIi4vZnJhbWVcIjtcclxuaW1wb3J0IHsgQ3VzdG9tRnJhbWVzU2V0dGluZ3MsIGRlZmF1bHRTZXR0aW5ncywgZ2V0SWNvbiB9IGZyb20gXCIuL3NldHRpbmdzXCI7XHJcbmltcG9ydCB7IEN1c3RvbUZyYW1lc1NldHRpbmdUYWIgfSBmcm9tIFwiLi9zZXR0aW5ncy10YWJcIjtcclxuaW1wb3J0IHsgQ3VzdG9tRnJhbWVWaWV3IH0gZnJvbSBcIi4vdmlld1wiO1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQ3VzdG9tRnJhbWVzUGx1Z2luIGV4dGVuZHMgUGx1Z2luIHtcclxuXHJcblx0c2V0dGluZ3M6IEN1c3RvbUZyYW1lc1NldHRpbmdzO1xyXG5cclxuXHRhc3luYyBvbmxvYWQoKTogUHJvbWlzZTx2b2lkPiB7XHJcblx0XHRhd2FpdCB0aGlzLmxvYWRTZXR0aW5ncygpO1xyXG5cclxuXHRcdGZvciAobGV0IGZyYW1lIG9mIHRoaXMuc2V0dGluZ3MuZnJhbWVzKSB7XHJcblx0XHRcdGlmICghZnJhbWUudXJsIHx8ICFmcmFtZS5kaXNwbGF5TmFtZSlcclxuXHRcdFx0XHRjb250aW51ZTtcclxuXHRcdFx0bGV0IG5hbWUgPSBgY3VzdG9tLWZyYW1lcy0ke2ZyYW1lLmRpc3BsYXlOYW1lLnRvTG93ZXJDYXNlKCkucmVwbGFjZSgvXFxzL2csIFwiLVwiKX1gO1xyXG5cdFx0XHRpZiAoUGxhdGZvcm0uaXNNb2JpbGVBcHAgJiYgZnJhbWUuaGlkZU9uTW9iaWxlKSB7XHJcblx0XHRcdFx0Y29uc29sZS5sb2coYFNraXBwaW5nIGZyYW1lICR7bmFtZX0gd2hpY2ggaXMgaGlkZGVuIG9uIG1vYmlsZWApO1xyXG5cdFx0XHRcdGNvbnRpbnVlO1xyXG5cdFx0XHR9XHJcblx0XHRcdHRyeSB7XHJcblx0XHRcdFx0Y29uc29sZS5sb2coYFJlZ2lzdGVyaW5nIGZyYW1lICR7bmFtZX0gZm9yIFVSTCAke2ZyYW1lLnVybH1gKTtcclxuXHJcblx0XHRcdFx0dGhpcy5yZWdpc3RlclZpZXcobmFtZSwgbCA9PiBuZXcgQ3VzdG9tRnJhbWVWaWV3KGwsIHRoaXMuc2V0dGluZ3MsIGZyYW1lLCBuYW1lKSk7XHJcblx0XHRcdFx0dGhpcy5hZGRDb21tYW5kKHtcclxuXHRcdFx0XHRcdGlkOiBgb3Blbi0ke25hbWV9YCxcclxuXHRcdFx0XHRcdG5hbWU6IGBPcGVuICR7ZnJhbWUuZGlzcGxheU5hbWV9YCxcclxuXHRcdFx0XHRcdGNhbGxiYWNrOiAoKSA9PiB0aGlzLm9wZW5MZWFmKG5hbWUsIGZyYW1lLm9wZW5JbkNlbnRlciwgZmFsc2UpLFxyXG5cdFx0XHRcdH0pO1xyXG5cclxuXHRcdFx0XHRpZiAoZnJhbWUuYWRkUmliYm9uSWNvbilcclxuXHRcdFx0XHRcdHRoaXMuYWRkUmliYm9uSWNvbihnZXRJY29uKGZyYW1lKSwgYE9wZW4gJHtmcmFtZS5kaXNwbGF5TmFtZX1gLFxyXG5cdFx0XHRcdFx0XHRlID0+IHRoaXMub3BlbkxlYWYobmFtZSwgZnJhbWUub3BlbkluQ2VudGVyLCBQbGF0Zm9ybS5pc01hY09TID8gZS5tZXRhS2V5IDogZS5jdHJsS2V5KSk7XHJcblx0XHRcdH0gY2F0Y2gge1xyXG5cdFx0XHRcdGNvbnNvbGUuZXJyb3IoYENvdWxkbid0IHJlZ2lzdGVyIGZyYW1lICR7bmFtZX0sIGlzIHRoZXJlIGFscmVhZHkgb25lIHdpdGggdGhlIHNhbWUgbmFtZT9gKTtcclxuXHRcdFx0fVxyXG5cdFx0fVxyXG5cclxuXHRcdHRoaXMuYWRkU2V0dGluZ1RhYihuZXcgQ3VzdG9tRnJhbWVzU2V0dGluZ1RhYih0aGlzLmFwcCwgdGhpcykpO1xyXG5cclxuXHRcdHRoaXMucmVnaXN0ZXJNYXJrZG93bkNvZGVCbG9ja1Byb2Nlc3NvcihcImN1c3RvbS1mcmFtZXNcIiwgKHMsIGUpID0+IHtcclxuXHRcdFx0ZS5lbXB0eSgpO1xyXG5cdFx0XHRlLmFkZENsYXNzKFwiY3VzdG9tLWZyYW1lcy12aWV3LWZpbGVcIik7XHJcblxyXG5cdFx0XHRsZXQgZnJhbWVNYXRjaCA9IC9mcmFtZTooW15cXG5dKykvZ2kuZXhlYyhzKTtcclxuXHRcdFx0bGV0IGZyYW1lTmFtZSA9IGZyYW1lTWF0Y2ggJiYgZnJhbWVNYXRjaFsxXS50cmltKCk7XHJcblx0XHRcdGlmICghZnJhbWVOYW1lKSB7XHJcblx0XHRcdFx0ZS5jcmVhdGVTcGFuKHsgdGV4dDogXCJDb3VsZG4ndCBwYXJzZSBmcmFtZSBuYW1lXCIgfSk7XHJcblx0XHRcdFx0cmV0dXJuO1xyXG5cdFx0XHR9XHJcblx0XHRcdGxldCBkYXRhID0gdGhpcy5zZXR0aW5ncy5mcmFtZXMuZmluZChmID0+IGYuZGlzcGxheU5hbWUgPT0gZnJhbWVOYW1lKTtcclxuXHRcdFx0aWYgKCFkYXRhKSB7XHJcblx0XHRcdFx0ZS5jcmVhdGVTcGFuKHsgdGV4dDogYENvdWxkbid0IGZpbmQgYSBmcmFtZSB3aXRoIG5hbWUgJHtmcmFtZU5hbWV9YCB9KTtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHRcdFx0aWYgKFBsYXRmb3JtLmlzTW9iaWxlQXBwICYmIGRhdGEuaGlkZU9uTW9iaWxlKSB7XHJcblx0XHRcdFx0ZS5jcmVhdGVTcGFuKHsgdGV4dDogYCR7ZnJhbWVOYW1lfSBpcyBoaWRkZW4gb24gbW9iaWxlYCB9KTtcclxuXHRcdFx0XHRyZXR1cm47XHJcblx0XHRcdH1cclxuXHJcblx0XHRcdGxldCBzdHlsZU1hdGNoID0gL3N0eWxlOihbXlxcbl0rKS9naS5leGVjKHMpO1xyXG5cdFx0XHRsZXQgc3R5bGUgPSBzdHlsZU1hdGNoICYmIHN0eWxlTWF0Y2hbMV0udHJpbSgpO1xyXG5cdFx0XHRzdHlsZSB8fD0gXCJoZWlnaHQ6IDYwMHB4O1wiO1xyXG5cclxuXHRcdFx0bGV0IHVybFN1ZmZpeE1hdGNoID0gL3VybHN1ZmZpeDooW15cXG5dKykvZ2kuZXhlYyhzKTtcclxuXHRcdFx0bGV0IHVybFN1ZmZpeCA9IHVybFN1ZmZpeE1hdGNoICYmIHVybFN1ZmZpeE1hdGNoWzFdLnRyaW0oKTtcclxuXHRcdFx0dXJsU3VmZml4IHx8PSBcIlwiO1xyXG5cclxuXHRcdFx0bGV0IGZyYW1lID0gbmV3IEN1c3RvbUZyYW1lKHRoaXMuc2V0dGluZ3MsIGRhdGEpO1xyXG5cdFx0XHRlLmFwcGVuZENoaWxkKGZyYW1lLmNyZWF0ZShzdHlsZSwgdXJsU3VmZml4KSk7XHJcblx0XHR9KTtcc