2022-09-29 15:36:06 +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 ;
2022-10-19 16:35:21 +02:00
var _ _defProps = Object . defineProperties ;
2022-09-29 15:36:06 +02:00
var _ _getOwnPropDesc = Object . getOwnPropertyDescriptor ;
2022-10-19 16:35:21 +02:00
var _ _getOwnPropDescs = Object . getOwnPropertyDescriptors ;
2022-09-29 15:36:06 +02:00
var _ _getOwnPropNames = Object . getOwnPropertyNames ;
2022-10-19 16:35:21 +02:00
var _ _getOwnPropSymbols = Object . getOwnPropertySymbols ;
2022-09-29 15:36:06 +02:00
var _ _getProtoOf = Object . getPrototypeOf ;
var _ _hasOwnProp = Object . prototype . hasOwnProperty ;
2022-10-19 16:35:21 +02:00
var _ _propIsEnum = Object . prototype . propertyIsEnumerable ;
var _ _defNormalProp = ( obj , key , value ) => key in obj ? _ _defProp ( obj , key , { enumerable : true , configurable : true , writable : true , value } ) : obj [ key ] = value ;
var _ _spreadValues = ( a , b ) => {
for ( var prop in b || ( b = { } ) )
if ( _ _hasOwnProp . call ( b , prop ) )
_ _defNormalProp ( a , prop , b [ prop ] ) ;
if ( _ _getOwnPropSymbols )
for ( var prop of _ _getOwnPropSymbols ( b ) ) {
if ( _ _propIsEnum . call ( b , prop ) )
_ _defNormalProp ( a , prop , b [ prop ] ) ;
}
return a ;
} ;
var _ _spreadProps = ( a , b ) => _ _defProps ( a , _ _getOwnPropDescs ( b ) ) ;
2022-09-29 15:36:06 +02:00
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 : ( ) => SimpleTimeTrackerPlugin
} ) ;
var import _obsidian3 = _ _toModule ( require ( "obsidian" ) ) ;
// src/settings.ts
var defaultSettings = {
timestampFormat : "YY-MM-DD hh:mm:ss" ,
csvDelimiter : ","
} ;
// src/settings-tab.ts
var import _obsidian = _ _toModule ( require ( "obsidian" ) ) ;
var SimpleTimeTrackerSettingsTab = class extends import _obsidian . PluginSettingTab {
constructor ( app , plugin ) {
super ( app , plugin ) ;
this . plugin = plugin ;
}
display ( ) {
this . containerEl . empty ( ) ;
this . containerEl . createEl ( "h2" , { text : "Super Simple Time Tracker Settings" } ) ;
new import _obsidian . Setting ( this . containerEl ) . setName ( "Timestamp Display Format" ) . setDesc ( createFragment ( ( f ) => {
f . createSpan ( { text : "The way that timestamps in time tracker tables should be displayed. Uses " } ) ;
f . createEl ( "a" , { text : "moment.js" , href : "https://momentjs.com/docs/#/parsing/string-format/" } ) ;
f . createSpan ( { text : " syntax." } ) ;
} ) ) . addText ( ( t ) => {
t . setValue ( String ( this . plugin . settings . timestampFormat ) ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
this . plugin . settings . timestampFormat = v . length ? v : defaultSettings . timestampFormat ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
new import _obsidian . Setting ( this . containerEl ) . setName ( "CSV Delimiter" ) . setDesc ( "The delimiter character that should be used when copying a tracker table as CSV. For example, some languages use a semicolon instead of a comma." ) . addText ( ( t ) => {
t . setValue ( String ( this . plugin . settings . csvDelimiter ) ) ;
t . onChange ( ( v ) => _ _async ( this , null , function * ( ) {
this . plugin . settings . csvDelimiter = v . length ? v : defaultSettings . csvDelimiter ;
yield this . plugin . saveSettings ( ) ;
} ) ) ;
} ) ;
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 : "simple-time-tracker-support" } ) ;
}
} ;
// src/tracker.ts
var import _obsidian2 = _ _toModule ( require ( "obsidian" ) ) ;
function saveTracker ( tracker , app , section ) {
return _ _async ( this , null , function * ( ) {
let file = app . workspace . getActiveFile ( ) ;
2022-09-29 16:05:39 +02:00
if ( ! file )
return ;
let content = yield app . vault . read ( file ) ;
2022-09-29 15:36:06 +02:00
let lines = content . split ( "\n" ) ;
let prev = lines . filter ( ( _ , i ) => i <= section . lineStart ) . join ( "\n" ) ;
let next = lines . filter ( ( _ , i ) => i >= section . lineEnd ) . join ( "\n" ) ;
content = ` ${ prev }
$ { JSON . stringify ( tracker ) }
$ { next } ` ;
yield app . vault . modify ( file , content ) ;
} ) ;
}
function loadTracker ( json ) {
if ( json ) {
try {
return JSON . parse ( json ) ;
} catch ( e ) {
console . log ( ` Failed to parse Tracker from ${ json } ` ) ;
}
}
return { entries : [ ] } ;
}
function displayTracker ( tracker , element , getSectionInfo , settings ) {
let running = isRunning ( tracker ) ;
2022-10-10 14:51:11 +02:00
let btn = new import _obsidian2 . ButtonComponent ( element ) . setClass ( "clickable-icon" ) . setIcon ( ` lucide- ${ running ? "stop" : "play" } -circle ` ) . setTooltip ( running ? "End" : "Start" ) . onClick ( ( ) => _ _async ( this , null , function * ( ) {
2022-09-29 15:36:06 +02:00
if ( running ) {
2022-10-19 16:35:21 +02:00
endRunningEntry ( tracker ) ;
2022-09-29 15:36:06 +02:00
} else {
2022-10-19 16:35:21 +02:00
startNewEntry ( tracker , newSegmentNameBox . getValue ( ) ) ;
2022-09-29 15:36:06 +02:00
}
yield saveTracker ( tracker , this . app , getSectionInfo ( ) ) ;
} ) ) ;
btn . buttonEl . addClass ( "simple-time-tracker-btn" ) ;
2022-10-19 16:35:21 +02:00
let newSegmentNameBox = new import _obsidian2 . TextComponent ( element ) . setPlaceholder ( "Segment name" ) . setDisabled ( running ) ;
newSegmentNameBox . inputEl . addClass ( "simple-time-tracker-txt" ) ;
2022-09-29 15:36:06 +02:00
let timer = element . createDiv ( { cls : "simple-time-tracker-timers" } ) ;
let currentDiv = timer . createEl ( "div" , { cls : "simple-time-tracker-timer" } ) ;
let current = currentDiv . createEl ( "span" , { cls : "simple-time-tracker-timer-time" } ) ;
currentDiv . createEl ( "span" , { text : "Current" } ) ;
let totalDiv = timer . createEl ( "div" , { cls : "simple-time-tracker-timer" } ) ;
let total = totalDiv . createEl ( "span" , { cls : "simple-time-tracker-timer-time" , text : "0s" } ) ;
totalDiv . createEl ( "span" , { text : "Total" } ) ;
if ( tracker . entries . length > 0 ) {
let table = element . createEl ( "table" , { cls : "simple-time-tracker-table" } ) ;
2022-10-10 14:40:36 +02:00
table . createEl ( "tr" ) . append ( createEl ( "th" , { text : "Segment" } ) , createEl ( "th" , { text : "Start time" } ) , createEl ( "th" , { text : "End time" } ) , createEl ( "th" , { text : "Duration" } ) , createEl ( "th" ) ) ;
2022-10-19 16:35:21 +02:00
for ( let entry of tracker . entries )
addEditableTableRow ( tracker , entry , table , newSegmentNameBox , running , getSectionInfo , settings , 0 ) ;
2022-09-29 15:36:06 +02:00
let buttons = element . createEl ( "div" , { cls : "simple-time-tracker-bottom" } ) ;
new import _obsidian2 . ButtonComponent ( buttons ) . setButtonText ( "Copy as table" ) . onClick ( ( ) => navigator . clipboard . writeText ( createMarkdownTable ( tracker , settings ) ) ) ;
new import _obsidian2 . ButtonComponent ( buttons ) . setButtonText ( "Copy as CSV" ) . onClick ( ( ) => navigator . clipboard . writeText ( createCsv ( tracker , settings ) ) ) ;
}
setCountdownValues ( tracker , current , total , currentDiv ) ;
let intervalId = window . setInterval ( ( ) => {
if ( ! element . isConnected ) {
window . clearInterval ( intervalId ) ;
return ;
}
setCountdownValues ( tracker , current , total , currentDiv ) ;
} , 1e3 ) ;
}
2022-10-19 16:35:21 +02:00
function startSubEntry ( entry , name ) {
if ( ! entry . subEntries ) {
entry . subEntries = [ _ _spreadProps ( _ _spreadValues ( { } , entry ) , { name : ` Part 1 ` } ) ] ;
entry . startTime = null ;
entry . endTime = null ;
2022-09-29 15:36:06 +02:00
}
2022-10-19 16:35:21 +02:00
if ( ! name )
name = ` Part ${ entry . subEntries . length + 1 } ` ;
entry . subEntries . push ( { name , startTime : ( 0 , import _obsidian2 . moment ) ( ) . unix ( ) , endTime : null , subEntries : null } ) ;
2022-09-29 15:36:06 +02:00
}
2022-10-19 16:35:21 +02:00
function startNewEntry ( tracker , name ) {
if ( ! name )
name = ` Segment ${ tracker . entries . length + 1 } ` ;
let entry = { name , startTime : ( 0 , import _obsidian2 . moment ) ( ) . unix ( ) , endTime : null , subEntries : null } ;
tracker . entries . push ( entry ) ;
}
function endRunningEntry ( tracker ) {
let entry = getRunningEntry ( tracker . entries ) ;
entry . endTime = ( 0 , import _obsidian2 . moment ) ( ) . unix ( ) ;
}
function removeEntry ( entries , toRemove ) {
if ( entries . contains ( toRemove ) ) {
entries . remove ( toRemove ) ;
return true ;
} else {
for ( let entry of entries ) {
if ( entry . subEntries && removeEntry ( entry . subEntries , toRemove ) ) {
if ( entry . subEntries . length == 1 ) {
let single = entry . subEntries [ 0 ] ;
entry . startTime = single . startTime ;
entry . endTime = single . endTime ;
entry . subEntries = null ;
}
return true ;
}
}
}
return false ;
}
function isRunning ( tracker ) {
return ! ! getRunningEntry ( tracker . entries ) ;
}
function getRunningEntry ( entries ) {
for ( let entry of entries ) {
if ( entry . subEntries ) {
let running = getRunningEntry ( entry . subEntries ) ;
if ( running )
return running ;
} else {
if ( ! entry . endTime )
return entry ;
}
}
return null ;
}
function getDuration ( entry ) {
if ( entry . subEntries ) {
return getTotalDuration ( entry . subEntries ) ;
} else {
2022-09-29 15:36:06 +02:00
let endTime = entry . endTime ? import _obsidian2 . moment . unix ( entry . endTime ) : ( 0 , import _obsidian2 . moment ) ( ) ;
2022-10-19 16:35:21 +02:00
return endTime . diff ( import _obsidian2 . moment . unix ( entry . startTime ) ) ;
}
}
function getTotalDuration ( entries ) {
let ret = 0 ;
for ( let entry of entries )
ret += getDuration ( entry ) ;
return ret ;
}
function setCountdownValues ( tracker , current , total , currentDiv ) {
let running = getRunningEntry ( tracker . entries ) ;
if ( running && ! running . endTime ) {
current . setText ( formatDuration ( getDuration ( running ) ) ) ;
currentDiv . hidden = false ;
} else {
currentDiv . hidden = true ;
2022-09-29 15:36:06 +02:00
}
2022-10-19 16:35:21 +02:00
total . setText ( formatDuration ( getTotalDuration ( tracker . entries ) ) ) ;
2022-09-29 15:36:06 +02:00
}
function formatTimestamp ( timestamp , settings ) {
return import _obsidian2 . moment . unix ( timestamp ) . format ( settings . timestampFormat ) ;
}
function formatDuration ( totalTime ) {
let duration = import _obsidian2 . moment . duration ( totalTime ) ;
let ret = "" ;
2022-11-23 18:05:51 +01:00
if ( duration . years ( ) > 0 )
ret += duration . years ( ) + "y " ;
if ( duration . months ( ) > 0 )
ret += duration . months ( ) + "m " ;
if ( duration . days ( ) > 0 )
ret += duration . days ( ) + "d " ;
2022-09-29 15:36:06 +02:00
if ( duration . hours ( ) > 0 )
ret += duration . hours ( ) + "h " ;
if ( duration . minutes ( ) > 0 )
ret += duration . minutes ( ) + "m " ;
ret += duration . seconds ( ) + "s" ;
return ret ;
}
function createMarkdownTable ( tracker , settings ) {
let table = [ [ "Segment" , "Start time" , "End time" , "Duration" ] ] ;
for ( let entry of tracker . entries )
2022-10-19 16:35:21 +02:00
table . push ( ... createTableSection ( entry , settings ) ) ;
table . push ( [ "**Total**" , "" , "" , ` ** ${ formatDuration ( getTotalDuration ( tracker . entries ) ) } ** ` ] ) ;
2022-09-29 15:36:06 +02:00
let ret = "" ;
let widths = Array . from ( Array ( 4 ) . keys ( ) ) . map ( ( i ) => Math . max ( ... table . map ( ( a ) => a [ i ] . length ) ) ) ;
for ( let r = 0 ; r < table . length ; r ++ ) {
if ( r == 1 )
ret += Array . from ( Array ( 4 ) . keys ( ) ) . map ( ( i ) => "-" . repeat ( widths [ i ] ) ) . join ( " | " ) + "\n" ;
let row = [ ] ;
for ( let i = 0 ; i < 4 ; i ++ )
row . push ( table [ r ] [ i ] . padEnd ( widths [ i ] , " " ) ) ;
ret += row . join ( " | " ) + "\n" ;
}
return ret ;
}
function createCsv ( tracker , settings ) {
let ret = "" ;
2022-10-19 16:35:21 +02:00
for ( let entry of tracker . entries ) {
for ( let row of createTableSection ( entry , settings ) )
ret += row . join ( settings . csvDelimiter ) + "\n" ;
}
2022-09-29 15:36:06 +02:00
return ret ;
}
2022-10-19 16:35:21 +02:00
function createTableSection ( entry , settings ) {
let ret = [ [
2022-09-29 15:36:06 +02:00
entry . name ,
2022-10-19 16:35:21 +02:00
entry . startTime ? formatTimestamp ( entry . startTime , settings ) : "" ,
2022-09-29 15:36:06 +02:00
entry . endTime ? formatTimestamp ( entry . endTime , settings ) : "" ,
2022-10-19 16:35:21 +02:00
entry . endTime || entry . subEntries ? formatDuration ( getDuration ( entry ) ) : ""
] ] ;
if ( entry . subEntries ) {
for ( let sub of entry . subEntries )
ret . push ( ... createTableSection ( sub , settings ) ) ;
}
return ret ;
}
function addEditableTableRow ( tracker , entry , table , newSegmentNameBox , running , getSectionInfo , settings , indent ) {
let row = table . createEl ( "tr" ) ;
let name = row . createEl ( "td" ) ;
let namePar = name . createEl ( "span" , { text : entry . name } ) ;
namePar . style . marginLeft = ` ${ indent } em ` ;
let nameBox = new import _obsidian2 . TextComponent ( name ) . setValue ( entry . name ) ;
nameBox . inputEl . hidden = true ;
row . createEl ( "td" , { text : entry . startTime ? formatTimestamp ( entry . startTime , settings ) : "" } ) ;
row . createEl ( "td" , { text : entry . endTime ? formatTimestamp ( entry . endTime , settings ) : "" } ) ;
row . createEl ( "td" , { text : entry . endTime || entry . subEntries ? formatDuration ( getDuration ( entry ) ) : "" } ) ;
let entryButtons = row . createEl ( "td" ) ;
if ( ! running ) {
new import _obsidian2 . ButtonComponent ( entryButtons ) . setClass ( "clickable-icon" ) . setIcon ( ` lucide-play ` ) . setTooltip ( "Continue" ) . onClick ( ( ) => _ _async ( this , null , function * ( ) {
startSubEntry ( entry , newSegmentNameBox . getValue ( ) ) ;
yield saveTracker ( tracker , this . app , getSectionInfo ( ) ) ;
} ) ) ;
}
let editButton = new import _obsidian2 . ButtonComponent ( entryButtons ) . setClass ( "clickable-icon" ) . setTooltip ( "Edit" ) . setIcon ( "lucide-pencil" ) . onClick ( ( ) => _ _async ( this , null , function * ( ) {
if ( namePar . hidden ) {
namePar . hidden = false ;
nameBox . inputEl . hidden = true ;
editButton . setIcon ( "lucide-pencil" ) ;
if ( nameBox . getValue ( ) ) {
entry . name = nameBox . getValue ( ) ;
namePar . setText ( entry . name ) ;
yield saveTracker ( tracker , this . app , getSectionInfo ( ) ) ;
}
} else {
namePar . hidden = true ;
nameBox . inputEl . hidden = false ;
nameBox . setValue ( entry . name ) ;
editButton . setIcon ( "lucide-check" ) ;
}
} ) ) ;
new import _obsidian2 . ButtonComponent ( entryButtons ) . setClass ( "clickable-icon" ) . setTooltip ( "Remove" ) . setIcon ( "lucide-trash" ) . onClick ( ( ) => _ _async ( this , null , function * ( ) {
removeEntry ( tracker . entries , entry ) ;
yield saveTracker ( tracker , this . app , getSectionInfo ( ) ) ;
} ) ) ;
if ( entry . subEntries ) {
for ( let sub of entry . subEntries )
addEditableTableRow ( tracker , sub , table , newSegmentNameBox , running , getSectionInfo , settings , indent + 1 ) ;
}
2022-09-29 15:36:06 +02:00
}
// src/main.ts
var SimpleTimeTrackerPlugin = class extends import _obsidian3 . Plugin {
onload ( ) {
return _ _async ( this , null , function * ( ) {
yield this . loadSettings ( ) ;
this . addSettingTab ( new SimpleTimeTrackerSettingsTab ( this . app , this ) ) ;
this . registerMarkdownCodeBlockProcessor ( "simple-time-tracker" , ( s , e , i ) => {
let tracker = loadTracker ( s ) ;
e . empty ( ) ;
displayTracker ( tracker , e , ( ) => i . getSectionInfo ( e ) , this . settings ) ;
} ) ;
this . addCommand ( {
id : ` insert ` ,
name : ` Insert Time Tracker ` ,
editorCallback : ( e , _ ) => {
e . replaceSelection ( "```simple-time-tracker\n```\n" ) ;
}
} ) ;
} ) ;
}
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 ) ;
} ) ;
}
} ;
2022-11-23 18:05:51 +01:00
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjL21haW4udHMiLCAic3JjL3NldHRpbmdzLnRzIiwgInNyYy9zZXR0aW5ncy10YWIudHMiLCAic3JjL3RyYWNrZXIudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImltcG9ydCB7IFBsdWdpbiB9IGZyb20gXCJvYnNpZGlhblwiO1xuaW1wb3J0IHsgZGVmYXVsdFNldHRpbmdzLCBTaW1wbGVUaW1lVHJhY2tlclNldHRpbmdzIH0gZnJvbSBcIi4vc2V0dGluZ3NcIjtcbmltcG9ydCB7IFNpbXBsZVRpbWVUcmFja2VyU2V0dGluZ3NUYWIgfSBmcm9tIFwiLi9zZXR0aW5ncy10YWJcIjtcbmltcG9ydCB7IGRpc3BsYXlUcmFja2VyLCBsb2FkVHJhY2tlciB9IGZyb20gXCIuL3RyYWNrZXJcIjtcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU2ltcGxlVGltZVRyYWNrZXJQbHVnaW4gZXh0ZW5kcyBQbHVnaW4ge1xuXG5cdHNldHRpbmdzOiBTaW1wbGVUaW1lVHJhY2tlclNldHRpbmdzO1xuXG5cdGFzeW5jIG9ubG9hZCgpOiBQcm9taXNlPHZvaWQ+IHtcblx0XHRhd2FpdCB0aGlzLmxvYWRTZXR0aW5ncygpO1xuXG5cdFx0dGhpcy5hZGRTZXR0aW5nVGFiKG5ldyBTaW1wbGVUaW1lVHJhY2tlclNldHRpbmdzVGFiKHRoaXMuYXBwLCB0aGlzKSk7XG5cblx0XHR0aGlzLnJlZ2lzdGVyTWFya2Rvd25Db2RlQmxvY2tQcm9jZXNzb3IoXCJzaW1wbGUtdGltZS10cmFja2VyXCIsIChzLCBlLCBpKSA9PiB7XG5cdFx0XHRsZXQgdHJhY2tlciA9IGxvYWRUcmFja2VyKHMpO1xuXHRcdFx0ZS5lbXB0eSgpO1xuXHRcdFx0ZGlzcGxheVRyYWNrZXIodHJhY2tlciwgZSwgKCkgPT4gaS5nZXRTZWN0aW9uSW5mbyhlKSwgdGhpcy5zZXR0aW5ncyk7XG5cdFx0fSk7XG5cblx0XHR0aGlzLmFkZENvbW1hbmQoe1xuXHRcdFx0aWQ6IGBpbnNlcnRgLFxuXHRcdFx0bmFtZTogYEluc2VydCBUaW1lIFRyYWNrZXJgLFxuXHRcdFx0ZWRpdG9yQ2FsbGJhY2s6IChlLCBfKSA9PiB7XG5cdFx0XHRcdGUucmVwbGFjZVNlbGVjdGlvbihcImBgYHNpbXBsZS10aW1lLXRyYWNrZXJcXG5gYGBcXG5cIik7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdH1cblxuXHRhc3luYyBsb2FkU2V0dGluZ3MoKSB7XG5cdFx0dGhpcy5zZXR0aW5ncyA9IE9iamVjdC5hc3NpZ24oe30sIGRlZmF1bHRTZXR0aW5ncywgYXdhaXQgdGhpcy5sb2FkRGF0YSgpKTtcblx0fVxuXG5cdGFzeW5jIHNhdmVTZXR0aW5ncygpIHtcblx0XHRhd2FpdCB0aGlzLnNhdmVEYXRhKHRoaXMuc2V0dGluZ3MpO1xuXHR9XG59XG4iLCAiZXhwb3J0IGNvbnN0IGRlZmF1bHRTZXR0aW5nczogU2ltcGxlVGltZVRyYWNrZXJTZXR0aW5ncyA9IHtcbiAgICB0aW1lc3RhbXBGb3JtYXQ6IFwiWVktTU0tREQgaGg6bW06c3NcIixcbiAgICBjc3ZEZWxpbWl0ZXI6IFwiLFwiXG59O1xuXG5leHBvcnQgaW50ZXJmYWNlIFNpbXBsZVRpbWVUcmFja2VyU2V0dGluZ3Mge1xuXG4gICAgdGltZXN0YW1wRm9ybWF0OiBzdHJpbmc7XG4gICAgY3N2RGVsaW1pdGVyOiBzdHJpbmc7XG5cbn1cbiIsICJpbXBvcnQgeyBBcHAsIFBsdWdpblNldHRpbmdUYWIsIFNldHRpbmcgfSBmcm9tIFwib2JzaWRpYW5cIjtcbmltcG9ydCBTaW1wbGVUaW1lVHJhY2tlclBsdWdpbiBmcm9tIFwiLi9tYWluXCI7XG5pbXBvcnQgeyBkZWZhdWx0U2V0dGluZ3MgfSBmcm9tIFwiLi9zZXR0aW5nc1wiO1xuXG5leHBvcnQgY2xhc3MgU2ltcGxlVGltZVRyYWNrZXJTZXR0aW5nc1RhYiBleHRlbmRzIFBsdWdpblNldHRpbmdUYWIge1xuXG4gICAgcGx1Z2luOiBTaW1wbGVUaW1lVHJhY2tlclBsdWdpbjtcblxuICAgIGNvbnN0cnVjdG9yKGFwcDogQXBwLCBwbHVnaW46IFNpbXBsZVRpbWVUcmFja2VyUGx1Z2luKSB7XG4gICAgICAgIHN1cGVyKGFwcCwgcGx1Z2luKTtcbiAgICAgICAgdGhpcy5wbHVnaW4gPSBwbHVnaW47XG4gICAgfVxuXG4gICAgZGlzcGxheSgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5jb250YWluZXJFbC5lbXB0eSgpO1xuICAgICAgICB0aGlzLmNvbnRhaW5lckVsLmNyZWF0ZUVsKFwiaDJcIiwgeyB0ZXh0OiBcIlN1cGVyIFNpbXBsZSBUaW1lIFRyYWNrZXIgU2V0dGluZ3NcIiB9KTtcblxuICAgICAgICBuZXcgU2V0dGluZyh0aGlzLmNvbnRhaW5lckVsKVxuICAgICAgICAgICAgLnNldE5hbWUoXCJUaW1lc3RhbXAgRGlzcGxheSBGb3JtYXRcIilcbiAgICAgICAgICAgIC5zZXREZXNjKGNyZWF0ZUZyYWdtZW50KGYgPT4ge1xuICAgICAgICAgICAgICAgIGYuY3JlYXRlU3Bhbih7IHRleHQ6IFwiVGhlIHdheSB0aGF0IHRpbWVzdGFtcHMgaW4gdGltZSB0cmFja2VyIHRhYmxlcyBzaG91bGQgYmUgZGlzcGxheWVkLiBVc2VzIFwiIH0pO1xuICAgICAgICAgICAgICAgIGYuY3JlYXRlRWwoXCJhXCIsIHsgdGV4dDogXCJtb21lbnQuanNcIiwgaHJlZjogXCJodHRwczovL21vbWVudGpzLmNvbS9kb2NzLyMvcGFyc2luZy9zdHJpbmctZm9ybWF0L1wiIH0pO1xuICAgICAgICAgICAgICAgIGYuY3JlYXRlU3Bhbih7IHRleHQ6IFwiIHN5bnRheC5cIiB9KTtcbiAgICAgICAgICAgIH0pKVxuICAgICAgICAgICAgLmFkZFRleHQodCA9PiB7XG4gICAgICAgICAgICAgICAgdC5zZXRWYWx1ZShTdHJpbmcodGhpcy5wbHVnaW4uc2V0dGluZ3MudGltZXN0YW1wRm9ybWF0KSk7XG4gICAgICAgICAgICAgICAgdC5vbkNoYW5nZShhc3luYyB2ID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wbHVnaW4uc2V0dGluZ3MudGltZXN0YW1wRm9ybWF0ID0gdi5sZW5ndGggPyB2IDogZGVmYXVsdFNldHRpbmdzLnRpbWVzdGFtcEZvcm1hdDtcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5wbHVnaW4uc2F2ZVNldHRpbmdzKCk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICBuZXcgU2V0dGluZyh0aGlzLmNvbnRhaW5lckVsKVxuICAgICAgICAgICAgLnNldE5hbWUoXCJDU1YgRGVsaW1pdGVyXCIpXG4gICAgICAgICAgICAuc2V0RGVzYyhcIlRoZSBkZWxpbWl0ZXIgY2hhcmFjdGVyIHRoYXQgc2hvdWxkIGJlIHVzZWQgd2hlbiBjb3B5aW5nIGEgdHJhY2tlciB0YWJsZ