-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.min.js
1 lines (1 loc) · 50.1 KB
/
index.min.js
1
javascript:((e,t)=>{"use strict";const s="NgSlackLinkifier",n="0.4.4",i="nsl-github-commit",r="nsl-github-issue",o="nsl-jira",a="nsl-processed",l="nsl-post-processed",c=Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty),h=e=>e.replace(/\{\{P}}/g,"%");class p{static get TOKEN_NAME(){return this._notImplemented()}static get TOKEN_DESCRIPTION_HTML(){return this._notImplemented()}static validateToken(e){if(!e||"string"!=typeof e)throw new Error(`Empty or invalid token (${typeof e}: ${e}). Please, provide a non-empty string.`)}constructor(){this._cacheMaxAge=6e4,this._cache=new Map,this.setToken(null)}cleanUp(){this.setToken(null),this._cache.clear()}hasToken(){return void 0!==this._token}requiresToken(){return this._notImplemented()}setToken(e){this._token=e||void 0,this._headers=this._token&&this._generateHeaders(this._token)}_generateHeaders(e){this._notImplemented(e)}_getErrorConstructorExtending(e){const t=this;return class extends e{get provider(){return t}}}_getErrorForResponse(e){this._notImplemented(e)}_getFromCache(e){if(!this._cache.has(e))return;const{date:t,response:s}=this._cache.get(e);if(!(Date.now()-t>this._cacheMaxAge))return s;this._cache.delete(e)}async _getJson(t){let s=this._getFromCache(t);return s||(s=e.fetch(t,{headers:{Accept:"application/json",...this._headers}}).then(async e=>e.ok?{data:await e.json(),headers:e.headers}:Promise.reject(await this._getErrorForResponse(e))).catch(e=>{throw this._getFromCache(t)===s&&this._cache.delete(t),e}),this._cache.set(t,{date:Date.now(),response:s})),s}_notImplemented(){throw new Error("Not implemented.")}_wrapError(e,t){return new(e instanceof Error?e.constructor:Error)(`${t}\n${e.message||e}`)}}class d extends Error{get provider(){throw new Error("Not implemented.")}}class u extends Error{constructor(){super("Cleaning up.")}}class g{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}}class m extends p{static get TOKEN_NAME(){return"GitHub access token"}static get TOKEN_DESCRIPTION_HTML(){const e=this.TOKEN_NAME,t="https://github.com/settings/tokens/new";return`\n <p>\n A ${e} can be used to make authenticated requests to GitHub's API, when retrieving info for links to\n issues and PRs. Authenticated requests have a much higher limit for requests per hour (at the time of writing\n 5000 vs 60 for anonymous requests).\n </p>\n <p>\n To create a ${e} visit: <a href="${t}?description=${s}" target="_blank">${t}</a>\n <i>(no scopes required)</i>\n </p>\n `}constructor(){super(),this._baseUrl="https://api.github.com/repos",this._rateLimitResetTime=0}async getCommitInfo(e,t,s){try{const n=`${this._baseUrl}/${e}/${t}/commits/${s}`,{data:i}=await this._getJson(n),{files:r,stats:o}=this._extractFileInfo(i.files);return{sha:i.sha,message:i.commit.message,author:this._extractUserInfo(i.author),committer:i.commiter&&this._extractUserInfo(i.committer),authorDate:new Date(i.commit.author.date),committerDate:new Date(i.commit.committer.date),stats:i.stats,files:r,filesUrl:i.html_url,hasMoreFiles:i.stats.total!==o.total}}catch(n){throw this._wrapError(n,`Error getting GitHub info for ${e}/${t}@${s}:`)}}async getIssueInfo(e,t,s){try{const n=`${this._baseUrl}/${e}/${t}/issues/${s}`,{data:i}=await this._getJson(n),r=c(i,"pull_request");let o=null;if(r){const n=`${this._baseUrl}/${e}/${t}/pulls/${s}/files?per_page=50`,{headers:r,data:a}=await this._getJson(n),{files:l,stats:c}=this._extractFileInfo(a);o={stats:c,files:l,filesUrl:i.html_url+"/files",hasMoreFiles:r.has("link")}}return{number:i.number,title:i.title,description:i.body.trim(),author:this._extractUserInfo(i.user),state:i.state,labels:i.labels.map(e=>e.name).sort(),isPr:r,prInfo:o}}catch(n){throw this._wrapError(n,`Error getting GitHub info for ${e}/${t}#${s}:`)}}async getLatestTag(e,t){try{const s=`${this._baseUrl}/${e}/${t}/tags?per_page=1`,{data:n}=await this._getJson(s);return n[0]}catch(s){throw this._wrapError(s,`Error getting latest GitHub tag ${e}/${t}:`)}}requiresToken(){return!this.hasToken()&&this._rateLimitResetTime>Date.now()&&`Anonymous rate-limit reached (until ${new Date(this._rateLimitResetTime).toLocaleString()})`}_extractFileInfo(e){const t={total:0,additions:0,deletions:0};return{files:e.map(e=>{const s={total:e.changes,additions:e.additions,deletions:e.deletions};return t.total+=s.total,t.additions+=s.additions,t.deletions+=s.deletions,{filename:e.filename,patch:void 0===e.patch?null:e.patch,status:e.status,stats:s}}),stats:t}}_extractUserInfo(e){return{avatar:e.avatar_url,username:e.login,url:e.html_url}}_generateHeaders(e){return{Authorization:"token "+e}}async _getErrorForResponse(e){let t=Error;const s=await e.json();let n=s.message||JSON.stringify(s);switch(e.status){case 401:this.hasToken()&&(t=this._getErrorConstructorExtending(d));break;case 403:if("0"===e.headers.get("X-RateLimit-Remaining")){const t=e.headers.get("X-RateLimit-Limit"),s=new Date(1e3*e.headers.get("X-RateLimit-Reset"));this._rateLimitResetTime=s.getTime(),n=`0/${t} API requests remaining until ${s.toLocaleString()}.\n${n}`}}return new t(`${e.status} (${e.statusText}) - ${n}`)}}class _{get length(){return this._keys().length}constructor(){this._resetItems()}clear(){this._resetItems()}getItem(e){return this._has(e)?this._items[e]:null}key(e){const t=this._keys();return e<t.length?t[e]:null}removeItem(e){delete this._items[e]}setItem(e,t){this._items[e]=""+t}_has(e){return Object.prototype.hasOwnProperty.call(this._items,e)}_keys(){return Object.keys(this._items)}_resetItems(){this._items=Object.create(null)}}class y extends p{static get TOKEN_NAME(){return"Jira e-mail and access token"}static get TOKEN_DESCRIPTION_HTML(){const e=this.TOKEN_NAME,t="https://id.atlassian.com/manage/api-tokens",s='<a href="https://cors-anywhere.herokuapp.com/" target="_blank">https://cors-anywhere.herokuapp.com/</a>';return`\n <p>\n A ${e} is required in order to retrieve info for links to Jira issues. Unauthenticated requests are\n <b>not supported</b> by Jira's API, so you will not be able to see any info without providing a ${e}.\n </p>\n <p>To create a Jira access token visit: <a href="${t}" target="_blank">${t}</a></p>\n <br />\n <p style="\n background-color: rgba(255, 0, 0, 0.1);\n border: 2px solid gray;\n border-radius: 6px;\n color: red;\n padding: 7px;\n ">\n <b>WARNING:</b><br />\n Currently, all requests to Jira's API are sent through ${s} in order to work around CORS\n restrictions. There will, hopefully, be a better solution in the future, but for now <b>do not</b> provide a\n ${e}, unless you understand and feel comfortable with the implications of sending the requests\n (including your encoded ${e}) through ${s}.\n </p>\n <br />\n <p>\n <b>IMPORTANT:</b><br />\n Enter the ${e} in the field below in the format <code><email>:<access-token></code> (e.g.\n <code>[email protected]:My4cc3ssT0k3n</code>).\n </p>\n `}static validateToken(e){if(super.validateToken(e),!/^[^:]+@[^:]+:./.test(e)){const t=e.replace(/\w/g,"*");throw new Error(`Invalid token format (${t}). Please, provide the token in the form \`<email>:<access-token>\` (e.g. \`[email protected]:My4cc3ssT0k3n\`).`)}}constructor(){super(),this._baseUrl="https://angular-team.atlassian.net/rest/api/3",this._baseUrl="https://cors-anywhere.herokuapp.com/"+this._baseUrl,this._customFields={PrLink:"customfield_10135"}}async getIssueInfo(e){try{const t=`${this._baseUrl}/issue/${e}?expand=renderedFields&fields=assignee,description,fixVersions,issuelinks,issuetype,project,reporter,status,summary,`+this._customFields.PrLink,{data:s}=await this._getJson(t);return{number:s.key,type:s.fields.issuetype.name,title:s.fields.summary,description:s.renderedFields.description.trim(),reporter:this._extractUserInfo(s.fields.reporter),assignee:s.fields.assignee&&this._extractUserInfo(s.fields.assignee),status:this._extractStatusInfo(s.fields.status),project:s.fields.project.name,fixVersions:s.fields.fixVersions.map(e=>e.name).sort(),prLink:s.fields[this._customFields.PrLink],issueLinks:s.fields.issuelinks.map(e=>this._extractIssueLinkInfo(e)).sort((e,t)=>this._sortIssueLinks(e,t))}}catch(t){throw this._wrapError(t,`Error getting Jira info for ${e}:`)}}requiresToken(){return!this.hasToken()&&"Unauthenticated requests are not supported."}_extractIssueLinkInfo(e){const t=c(e,"inwardIssue"),s=t?e.inwardIssue:e.outwardIssue;return{type:t?e.type.inward:e.type.outward,otherIssue:{number:s.key,url:"https://angular-team.atlassian.net/browse/"+s.key,title:s.fields.summary,status:this._extractStatusInfo(s.fields.status)}}}_extractStatusInfo(e){return{name:e.name,color:e.statusCategory.colorName}}_extractUserInfo(e){return{avatar:e.avatarUrls["32x32"],username:e.name,name:e.displayName,url:"https://angular-team.atlassian.net/people/"+e.accountId}}_generateHeaders(t){return{Authorization:"Basic "+e.btoa(t)}}async _getErrorForResponse(e){let t=Error;const s=e.headers.get("Content-Type").includes("application/json")?await e.json():(await e.text()).trim(),n=Array.isArray(s.errorMessages)?1===s.errorMessages.length?s.errorMessages[0]:["Errors:",...s.errorMessages.map(e=>" - "+e)].join("\n"):JSON.stringify(s);switch(e.status){case 401:this.hasToken()&&(t=this._getErrorConstructorExtending(d))}return new t(`${e.status} (${e.statusText}) - ${n}`)}_sortIssueLinks(e,t){return e.type<t.type?-1:e.type>t.type?1:e.otherIssue.number<t.otherIssue.number?-1:1}}class b{constructor(e=(()=>{})){this._postProcessNode=e,this._observer=new MutationObserver(e=>e.forEach(e=>e.addedNodes&&setTimeout(()=>this.processAll(e.addedNodes),500))),this._regexps={githubCommitShortRe:/(?:([\w.-]+)\/)?([\w.-]+)@([A-Fa-f\d]{7,})\b/,githubCommitUrlRe:/^https:\/\/github\.com\/([\w.-]+)\/([\w.-]+)\/commit\/([A-Fa-f\d]{7,})$/,githubCommitUrlRedirRe:new RegExp(h("^https://slack-redir\\.net/link\\?url=https{{P}}3A{{P}}2F{{P}}2Fgithub\\.com{{P}}2F([\\w.-]+){{P}}2F([\\w.-]+){{P}}2Fcommit{{P}}2F([A-Fa-f\\d]{7,})$")),githubIssueShortRe:/(?:(?:([\w.-]+)\/)?([\w.-]+))?#(\d+)\b/,githubIssueUrlRe:/^https:\/\/github\.com\/([\w.-]+)\/([\w.-]+)\/(?:issues|pull)\/(\d+)$/,githubIssueUrlRedirRe:new RegExp(h("^https://slack-redir\\.net/link\\?url=https{{P}}3A{{P}}2F{{P}}2Fgithub\\.com{{P}}2F([\\w.-]+){{P}}2F([\\w.-]+){{P}}2F(?:issues|pull){{P}}2F(\\d+)$")),jiraIssueShortRe:/(?<!https:\/\/angular-team\.atlassian\.net\/browse\/)\b([A-Z]+-\d+)\b/,jiraIssueUrlRe:/^https:\/\/angular-team\.atlassian\.net\/browse\/([A-Z]+-\d+)$/,jiraIssueUrlRedirRe:new RegExp(h("^https://slack-redir\\.net/link\\?url=https{{P}}3A{{P}}2F{{P}}2Fangular-team\\.atlassian\\.net{{P}}2Fbrowse{{P}}2F([A-Z]+-\\d+)$")),mdLinkRe:/\[([^[\]]+|[^[]*(?:\[[^\]]+][^[]*)*)]\($/}}cleanUp(){this._observer.disconnect()}observe(e){this._observer.observe(e,{childList:!0,subtree:!0})}processAll(e,t=!1){const s=".c-message__body, .c-message_attachment__body, .c-message_kit__text, .p-rich_text_block",n=new Set;e.forEach(e=>{if(n.has(e.parentNode))return;(e.closest?e.closest(s):e.parentNode&&e.parentNode.closest(s))?this._processNode(e.parentNode,t):e.querySelectorAll&&e.querySelectorAll(s).forEach(e=>this._processNode(e,t))})}_acceptNodeInTextNodeWalker(e){return!e.parentNode||"A"===e.parentNode.nodeName||e.parentNode.parentNode&&"A"===e.parentNode.parentNode.nodeName?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}_getEffectiveSiblingTextNode(e,t){const s=["B","I"],n=e=>s.includes(e.tagName)&&(1===e.childNodes.length||1===e.childElementCount&&e.textContent.trim()===e.firstElementChild.textContent.trim()),i=t?"nextSibling":"previousSibling";let r=e[i];if(!r){for(;n(e.parentNode);)e=e.parentNode;r=e[i]}if(r)for(;n(r);)r=r.firstElementChild||r.firstChild;return r&&r.nodeType===Node.TEXT_NODE?r:null}_processNode(e,t){const s=new Set([...this._processNodeMdLinks(e),...this._processNodeGithubCommits(e),...this._processNodeGithubIssues(e),...this._processNodeJira(e)]);s.forEach(e=>e.classList.add(a)),(t||s.size)&&this._postProcessNode(e)}_processNodeGithubCommits(e){const s=new Set,{githubCommitShortRe:n,githubCommitUrlRe:r,githubCommitUrlRedirRe:o}=this._regexps,a=t.createTreeWalker(e,NodeFilter.SHOW_TEXT,{acceptNode:e=>this._acceptNodeInTextNodeWalker(e)},!1);let l;for(;l=a.nextNode();){const e=n.exec(l.textContent);if(e){const[,n="angular",i="angular",r]=e,o=`https://github.com/${n}/${i}/commit/${r}`,a=Object.assign(t.createElement("a"),{href:o,target:"_blank",textContent:o}),c=t.createTextNode(l.textContent.slice(e.index+e[0].length));l.textContent=l.textContent.slice(0,e.index),l.after(a),a.after(c),s.add(a)}}return e.querySelectorAll("a:not(.nsl-processed)").forEach(e=>{const t=r.exec(e.href)||o.exec(e.href);if(t){const[,n,r,o]=t;e.classList.add(i),e.dataset.nslOwner=n,e.dataset.nslRepo=r,e.dataset.nslCommit=o,s.add(e)}const n=r.exec(e.innerHTML);if(n){const[,t,i,r]=n,o=`${"angular"===t?"":t+"/"}${i}`;e.innerHTML=`<b>${o}@${r.slice(0,7)}</b>`,s.add(e)}}),s}_processNodeGithubIssues(e){const s=new Set,{githubIssueShortRe:n,githubIssueUrlRe:i,githubIssueUrlRedirRe:o}=this._regexps,a=t.createTreeWalker(e,NodeFilter.SHOW_TEXT,{acceptNode:e=>this._acceptNodeInTextNodeWalker(e)},!1);let l;for(;l=a.nextNode();){const e=n.exec(l.textContent);if(e){const[,n="angular",i="angular",r]=e,o=`https://github.com/${n}/${i}/issues/${r}`,a=Object.assign(t.createElement("a"),{href:o,target:"_blank",textContent:o}),c=t.createTextNode(l.textContent.slice(e.index+e[0].length));l.textContent=l.textContent.slice(0,e.index),l.after(a),a.after(c),s.add(a)}}return e.querySelectorAll("a:not(.nsl-processed)").forEach(e=>{const t=i.exec(e.href)||o.exec(e.href);if(t){const[,n,i,o]=t;e.classList.add(r),e.dataset.nslOwner=n,e.dataset.nslRepo=i,e.dataset.nslNumber=o,s.add(e)}const n=i.exec(e.innerHTML);if(n){const[,t,i,r]=n,o="angular"===t,a="angular"===i,l=`${o?"":t+"/"}${o&&a?"":i}`;e.innerHTML=`<b>${l}#${r}</b>`,s.add(e)}}),s}_processNodeJira(e){const s=new Set,{jiraIssueShortRe:n,jiraIssueUrlRe:i,jiraIssueUrlRedirRe:r}=this._regexps,a=t.createTreeWalker(e,NodeFilter.SHOW_TEXT,{acceptNode:e=>this._acceptNodeInTextNodeWalker(e)},!1);let l;for(;l=a.nextNode();){const e=n.exec(l.textContent);if(e){const n="https://angular-team.atlassian.net/browse/"+e[1],i=Object.assign(t.createElement("a"),{href:n,target:"_blank",textContent:n}),r=t.createTextNode(l.textContent.slice(e.index+e[0].length));l.textContent=l.textContent.slice(0,e.index),l.after(i),i.after(r),s.add(i)}}return e.querySelectorAll("a:not(.nsl-processed)").forEach(e=>{const t=i.exec(e.href)||r.exec(e.href);t&&(e.classList.add(o),e.dataset.nslNumber=t[1],s.add(e));const n=i.exec(e.innerHTML);n&&(e.innerHTML=`<b>${n[1]}</b>`,s.add(e))}),s}_processNodeMdLinks(e){const s=new Set,{mdLinkRe:n}=this._regexps;return e.querySelectorAll("a:not(.nsl-processed)").forEach(e=>{const i=this._getEffectiveSiblingTextNode(e,!1),r=i&&n.exec(i.textContent),o=r&&this._getEffectiveSiblingTextNode(e,!0),a=o?/^\)/.exec(o.textContent):e.lastChild&&"…"===e.lastChild.textContent;if(a){if(e.childNodes.forEach(e=>e.textContent=""),e.appendChild(Object.assign(t.createElement("b"),{textContent:r[1]})),i.textContent=i.textContent.slice(0,-r[0].length),o)o.textContent=o.textContent.slice(a[0].length);else{const t=e.appendChild;e.appendChild=s=>t.call(e,Object.assign(s,{textContent:""}))}s.add(e)}}),s}}class f{constructor(e){this._prefix=`[${e}]`}cleanUp(){}log(...e){console.log(this._prefix,...e)}warn(...e){console.warn(this._prefix,...e)}error(...e){console.error(this._prefix,...e)}}class x{get ready(){return this._passwordKey.then(()=>this)}constructor(t){this._crypto=e.crypto,this._cryptoSub=this._crypto.subtle,this._version="3",this._pbkdf2Iterations=25e4,this._pbkdf2SaltLength=32,this._aesIvLength=12,this._aesKeyLength=256,this._decoder=new e.TextDecoder,this._encoder=new e.TextEncoder,this._passwordKey=Promise.resolve(this._cryptoSub.importKey("raw",this._encoder.encode(t),"PBKDF2",!1,["deriveKey"]))}cleanUp(){}async decrypt(e){const[t,s,...n]=e.split(":");if(n.length||!s)throw new Error(`Unable to decrypt \`${e}\`: Invalid or unknown format.`);if(t!==this._version)throw new Error(`Unable to decrypt \`${e}\`: Version mismatch (expected: ${this._version}).`);const i=this._base64ToBytes(s),r=i.slice(0,this._pbkdf2SaltLength),o=i.slice(this._pbkdf2SaltLength,this._pbkdf2SaltLength+this._aesIvLength),a=await this._cryptoSub.decrypt({name:"AES-GCM",iv:o},await this._deriveAesKey(r),i.slice(this._pbkdf2SaltLength+this._aesIvLength));return this._decoder.decode(a)}async encrypt(e){const t=this._crypto.getRandomValues(new Uint8Array(this._pbkdf2SaltLength)),s=this._crypto.getRandomValues(new Uint8Array(this._aesIvLength)),n=await this._cryptoSub.encrypt({name:"AES-GCM",iv:s},await this._deriveAesKey(t),this._encoder.encode(e)),i=new Uint8Array(n),r=Uint8Array.from([...t,...s,...i]);return`${this._version}:${this._bytesToBase64(r)}`}_base64ToBytes(e){const t=atob(e),s=new Uint8Array(t.length);for(let e=0,n=t.length;e<n;++e)s[e]=t.charCodeAt(e);return s}_bytesToBase64(e){const t=new Array(e.length);for(let s=0,n=e.length;s<n;++s)t[s]=String.fromCharCode(e[s]);return btoa(t.join(""))}async _deriveAesKey(e){const t={name:"PBKDF2",hash:"SHA-"+this._aesKeyLength,iterations:this._pbkdf2Iterations,salt:e},s={name:"AES-GCM",length:this._aesKeyLength};return this._cryptoSub.deriveKey(t,await this._passwordKey,s,!1,["decrypt","encrypt"])}}class w{constructor(e,t){this._prefix=e+":",this._store=t}clear(){this.keys().forEach(e=>this.delete(e))}delete(e){this._store.removeItem(this._keyFor(e))}get(e){const t=this._store.getItem(this._keyFor(e));return null===t?void 0:JSON.parse(t)}has(e){return null!==this._store.getItem(this._keyFor(e))}keys(){return Array.from(new Array(this._store.length),(e,t)=>this._store.key(t)).filter(e=>e.startsWith(this._prefix))}set(e,t){void 0!==t&&this._store.setItem(this._keyFor(e),JSON.stringify(t))}_keyFor(e){return`${this._prefix}${e}`}}class k{constructor(t){this.local=new w(t,e.localStorage),this.session=new w(t,e.sessionStorage),this.inMemory=new w(t,new _)}cleanUp(){this.inMemory.clear()}clear(){this.local.clear(),this.session.clear(),this.inMemory.clear()}delete(e){this.local.delete(e),this.session.delete(e),this.inMemory.delete(e)}get(e){const t=this.has(e);return t?t.get(e):void 0}has(e){return this.inMemory.has(e)?this.inMemory:this.session.has(e)?this.session:!!this.local.has(e)&&this.local}}class v{static get EMPTY_IMAGE_SRC(){return"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="}constructor(){this.widgetUtils=new U,this._openDialogDeferreds=[],this._popup=null,this._popupAnchor=null,this._hidePopupTimeout=null,this._showPopupTimeout=null,this._snackbarContainer=this._createSnackbarContainer(),this._scratchpad=t.createElement("div");const s=e=>this._onResizeListeners.forEach(t=>t(e));e.addEventListener("resize",s),this._onResizeCleanUp=()=>{e.removeEventListener("resize",s),this._onResizeListeners=[]},this._onResizeListeners=[()=>this._openDialogDeferreds.forEach(({dialog:e})=>this._updateDialogSizing(e)),()=>this._popup&&this._updatePopupPositioning(this._popup,this._popupAnchor)];const n=this;this._DialogDeferred=class extends g{constructor(e){n._openDialogDeferreds.push(super()),this.dialog=e,this.promise=this.promise.finally(()=>{n._fadeOut(e);const t=n._openDialogDeferreds.indexOf(e);-1!==t&&n._openDialogDeferreds.splice(t,1)})}}}cleanUp(){this._onResizeCleanUp(),this._snackbarContainer.remove(),this.hidePopup();const e=new u;for(;this._openDialogDeferreds.length;)this._openDialogDeferreds.pop().reject(e)}copyToClipboard(e){const s=t.createElement("textarea");t.body.appendChild(s),s.textContent=e,s.select();const n=t.execCommand("copy");if(t.body.removeChild(s),!n)throw new Error("Copying to clipboard failed.")}escapeHtml(e){this._scratchpad.textContent=e;const t=this._scratchpad.innerHTML;return this._scratchpad.textContent="",t}hidePopup(){this._cancelHidePopup(),this._popup&&(this._fadeOut(this._popup),this._popup=null,this._popupAnchor=null)}scheduleHidePopup(e){return!this._hidePopupTimeout&&(this._cancelShowPopup(),!!this._popup&&(this._hidePopupTimeout=setTimeout(()=>this.hidePopup(),e),!0))}scheduleShowPopup(e,t,s){return this._cancelHidePopup(),this._cancelShowPopup(),this._showPopupTimeout=setTimeout(()=>this.showPopup(e,t),s),!0}showDialog(e,n,i){const r=Object.assign(t.createElement("div"),{className:"nsl-dialog-backdrop",innerHTML:`\n <div class="nsl-dialog">\n <header class="nsl-dialog-header">${s} v0.4.4</header>\n <section class="nsl-dialog-content"></section>\n <footer class="nsl-dialog-actions">\n <button class="nsl-dialog-btn-ok">${n}</button>\n <button class="nsl-dialog-btn-cancel">${i}</button>\n </footer>\n </div>\n `,style:"\n align-items: center;\n background-color: rgba(0, 0, 0, 0.5);\n bottom: 0;\n display: flex;\n justify-content: center;\n padding: 15px;\n position: fixed;\n left: 0;\n right: 0;\n top: 0;\n z-index: 10200;\n "});Object.assign(r.querySelector(".nsl-dialog"),{style:"\n background-color: white;\n border: 1px solid lightgray;\n border-radius: 6px;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, .08), 0 4px 12px 0 rgba(0, 0, 0, .12);\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n overflow: auto;\n padding: 15px;\n pointer-events: all;\n "}),Object.assign(r.querySelector(".nsl-dialog-header"),{style:"\n color: gray;\n font-size: 0.75em;\n text-align: right;\n "}),this._insertContent(Object.assign(r.querySelector(".nsl-dialog-content"),{style:"\n user-select: text;\n "}),e),Object.assign(r.querySelector(".nsl-dialog-actions"),{style:"\n display: flex;\n justify-content: flex-end;\n user-select: text;\n "}),this.widgetUtils.asButton(this.widgetUtils.withStyles(r.querySelector(".nsl-dialog-btn-ok"),{marginRight:"15px"}),"green",{click:()=>o.resolve(!0)}),this.widgetUtils.asButton(r.querySelector(".nsl-dialog-btn-cancel"),"gray",{click:()=>o.resolve(!1)});const o=new this._DialogDeferred(r);return t.body.appendChild(r),this._updateDialogSizing(r),this._fadeIn(r),o.promise}showPopup(e,s){this._cancelShowPopup(),this.hidePopup();this._popupAnchor=s.target,this._popup=Object.assign(t.createElement("div"),{className:"nsl-popup",onmouseenter:()=>this._cancelHidePopup(),onmouseleave:()=>this.hidePopup(),style:"\n background-color: white;\n border: 1px solid gray;\n border-radius: 6px;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, .08), 0 4px 12px 0 rgba(0, 0, 0, .12);\n overflow: auto;\n padding: 10px;\n position: fixed;\n user-select: text;\n z-index: 10100;\n "}),this._insertContent(this._popup,e),this._updatePopupPositioning(this._popup,this._popupAnchor),t.body.appendChild(this._popup),this._fadeIn(this._popup)}showSnackbar(e,s=2e3){const n=Object.assign(t.createElement("div"),{className:"nsl-snackbar",innerHTML:'\n <section class="nsl-snackbar-content"></section>\n <button class="nsl-snackbar-btn-close">✕</button>\n ',style:"\n background-color: white;\n border: 1px solid lightgray;\n border-radius: 6px;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, .08), 0 4px 12px 0 rgba(0, 0, 0, .12);\n box-sizing: border-box;\n display: flex;\n margin-top: 5px;\n max-height: 500px;\n max-width: 750px;\n min-width: 400px;\n pointer-events: all;\n "});this._insertContent(Object.assign(n.querySelector(".nsl-snackbar-content"),{style:"\n flex: auto;\n overflow: auto;\n padding: 10px 5px 10px 10px;\n user-select: text;\n "}),e),Object.assign(n.querySelector(".nsl-snackbar-btn-close"),{onclick:()=>this._fadeOut(n),onmouseenter:e=>e.target.style.color="red",onmouseleave:e=>e.target.style.color="lightgray",style:"\n align-self: flex-start;\n background-color: transparent;\n border: none;\n color: lightgray;\n cursor: pointer;\n padding: 5px;\n "}),Object.assign(n,{isHovered:!1,onmouseenter:e=>e.target.isHovered=!0,onmouseleave:e=>e.target.isHovered=!1}),this._snackbarContainer.appendChild(n);const i=this._fadeIn(n);return s<0?i:i.then(()=>new Promise(e=>setTimeout(e,s))).then(()=>this._fadeOutOnceNotHovered(n))}_animateProp(e,t,s,n,i=200){const r=e.style;return this._withRafInterval([()=>r.transition="",()=>r[t]=s,()=>r.transition=`${t} ${i}ms linear`,()=>r[t]=n]).then(()=>new Promise(e=>setTimeout(e,i)))}_calculatePopupPositioning(t){const s=900,n=t.getBoundingClientRect(),i=n.top,r=e.innerHeight-n.bottom,o=r<=500&&(i>500||i>r);return{maxHeight:Math.min(750,(o?i:r)-10)+"px",top:o?"auto":n.bottom+"px",bottom:o?e.innerHeight-n.top+"px":"auto",...(()=>{const t=(n.left+n.right)/2;let i=Math.max(10,t-450),r=Math.min(e.innerWidth-10,i+s);return r-i<s&&(i=Math.max(10,r-s)),{left:i+"px",right:e.innerWidth-r+"px"}})()}}_cancelHidePopup(){this._hidePopupTimeout&&(clearTimeout(this._hidePopupTimeout),this._hidePopupTimeout=null)}_cancelShowPopup(){this._showPopupTimeout&&(clearTimeout(this._showPopupTimeout),this._showPopupTimeout=null)}_createSnackbarContainer(){const e=Object.assign(t.createElement("div"),{className:"nsl-snackbar-container",style:"\n bottom: 10px;\n display: flex;\n flex-direction: column-reverse;\n overflow: auto;\n pointer-events: none;\n position: fixed;\n right: 10px;\n top: 10px;\n z-index: 10300;\n "});return t.body.appendChild(e),e}_fadeIn(e,t){return this._animateProp(e,"opacity",.1,1,t)}_fadeOut(e,t){return this._animateProp(e,"opacity",1,.1,t).then(()=>e.remove())}_fadeOutOnceNotHovered(e,t){return new Promise((s,n)=>{const i=()=>this._fadeOut(e,t).then(s,n);e.isHovered?e.addEventListener("mouseleave",i):i()})}_insertContent(e,t){"string"==typeof t?e.innerHTML=t:(e.innerHTML="",e.appendChild(t))}_updateDialogSizing(t){const s=e.innerWidth-30,n=e.innerHeight-30;Object.assign(t.firstElementChild.style,{minWidth:Math.min(750,s)+"px",maxWidth:Math.min(900,s)+"px",minHeight:Math.min(500,n)+"px",maxHeight:Math.min(700,n)+"px"})}_updatePopupPositioning(e,t){const s=this._calculatePopupPositioning(t);Object.assign(e.style,s)}_withRafInterval(t){return new Promise((s,n)=>{if(!t.length)return s();t.shift()(),e.requestAnimationFrame(()=>this._withRafInterval(t).then(s,n))})}}class ${constructor(e){this._owner="gkalpak",this._repo="ng-slack-linkifier",this._versionRe=/^\d+\.\d+\.\d+(?:-(?:alpha|beta|rc)\.\d+)?$/,this._ghUtils=e}async checkForUpdate(t){if(this.isDevelopmentVersion(t))return;if(!this._versionRe.test(t))throw new Error(`Invalid current version format: ${t} (expected: X.Y.Z[-(alpha|beta|rc).K])`);const s=await this._getLatestVersion(),n=s&&this._getDownloadUrl(s);return s&&-1===this._compareVersions(t,s)&&{version:s,url:n,code:await e.fetch(n).then(e=>e.text())}}cleanUp(){}isDevelopmentVersion(e){return/^X\.Y\.Z-VERSION$/.test(e)}_compareVersions(e,t){const s=e.split(/[.-]/),n=t.split(/[.-]/);for(let e=0,t=s.length;e<t;++e){if(n.length===e)return-1;const t=isNaN(s[e])?s[e]:Number(s[e]),i=isNaN(n[e])?n[e]:Number(n[e]);if(t<i)return-1;if(t>i)return 1}return s.length<n.length?1:0}_getDownloadUrl(e){return`https://cdn.jsdelivr.net/gh/${this._owner}/${this._repo}@${e}/dist/index.min.js`}async _getLatestVersion(){const e=await this._ghUtils.getLatestTag(this._owner,this._repo),t=e&&e.name.slice(1);return t&&this._versionRe.test(t)?t:void 0}}class U{asButton(e,t,s={}){return this.withListeners(this.withListeners(e,s),{mouseenter:e=>e.target.style.borderColor="orange",mouseleave:e=>e.target.style.borderColor="white"}),this.withStyles(e,{backgroundColor:t,border:"2px solid white",borderRadius:"6px",color:"white",cursor:"pointer",padding:"10px 15px"})}asButtonLink(e,t={}){return this.withListeners(this.withListeners(e,t),{mouseenter:e=>e.target.style.color="orange",mouseleave:e=>e.target.style.color=null}),this.withStyles(e,{cursor:"pointer",textDecoration:"underline"})}asInputField(e){return this.withStyles(e,{border:"1px solid lightgray",borderRadius:"6px",outline:"none",padding:"15px",WebkitAppearance:"textfield",width:"100%"})}withListeners(e,t){return Object.keys(t).forEach(s=>{const n="string"==typeof t[s]?new Function("event",t[s]):t[s];e.addEventListener(s,n)}),e}withStyles(e,t){return Object.assign(e.style,t),e}}(new class{constructor(){this._KEYS=new Map([[m,1],[y,2]]),this._cleanUpables=[this._logUtils=new f(s+" v0.4.4"),this._storageUtils=new k(s),this._secretUtils=new x("$NgSl@ckL1nk1fy$"),this._linkifier=new b(e=>this._addListeners(e)),this._uiUtils=new v,this._ghUtils=new m,this._jiraUtils=new y,this._updateUtils=new $(this._ghUtils)],this._cleanUpFns=[()=>this._destroyedDeferred.reject(new u)],this._destroyedDeferred=new g}cleanUp(){for(this._logUtils.log("Uninstalling...");this._cleanUpables.length||this._cleanUpFns.length;){for(;this._cleanUpables.length;)this._cleanUpables.shift().cleanUp();for(;this._cleanUpFns.length;)this._cleanUpFns.shift()()}this._logUtils.log("Uninstalled.")}async main(){try{e.__ngSlackLinkifyCleanUp&&e.__ngSlackLinkifyCleanUp(),e.__ngSlackLinkifyCleanUp=()=>{this.cleanUp(),e.__ngSlackLinkifyCleanUp=null},this._logUtils.log("Installing..."),this._ghUtils.setToken(await this._getStoredTokenFor(m)),this._jiraUtils.setToken(await this._getStoredTokenFor(y));const t=this._getRootElement();this._linkifier.processAll([t],!0),this._linkifier.observe(t),this._postInstall(),this._logUtils.log("Installed.")}catch(e){this._onError(e)}finally{this._schedule(()=>this._checkForUpdate(),1e4)}}_addListeners(e){const t=new Set;e.querySelectorAll(".nsl-github-commit:not(.nsl-post-processed)").forEach(e=>{t.add(e),this._addListenersForLink(e,e=>this._getPopupContentForGithubCommit(e))}),e.querySelectorAll(".nsl-github-issue:not(.nsl-post-processed)").forEach(e=>{t.add(e),this._addListenersForLink(e,e=>this._getPopupContentForGithubIssue(e))}),e.querySelectorAll(".nsl-jira:not(.nsl-post-processed)").forEach(e=>{t.add(e),this._addListenersForLink(e,e=>this._getPopupContentForJira(e))}),t.forEach(e=>{e.classList.add(l),this._cleanUpFns.push(()=>e.classList.remove(l))})}_addListenersForLink(e,t){const s=e.style,n=e.dataset,i="help";let r=0;const o=async e=>{try{const o=r;if(await this._whileNotDestroyed(new Promise(e=>setTimeout(e,500))),o!==r)return;s.cursor="progress";const a=await this._whileNotDestroyed(t(n));if(o!==r)return;s.cursor=i,this._uiUtils.showPopup(a,e)}catch(e){this._onError(e)}},a=()=>{++r,s.cursor=i,this._uiUtils.scheduleHidePopup(500)};s.cursor=i,e.addEventListener("mouseenter",o),e.addEventListener("mouseleave",a),this._cleanUpFns.push(()=>e.removeEventListener("mouseenter",o),()=>e.removeEventListener("mouseleave",a))}async _checkForUpdate(e=!1){try{this._logUtils.log("Checking for updates..."),this._schedule(()=>this._checkForUpdate(),864e5);const i=e?"0.0.0":n,r=await this._whileNotDestroyed(this._updateUtils.checkForUpdate(i));if(!r)return this._logUtils.log("No updates available.");this._logUtils.log(`Update available: ${r.version} (${r.url})`);const o="cornflowerblue",a=Object.assign(t.createElement("div"),{innerHTML:`\n <header style="font-size: 0.75em; opacity: 0.5;"><p>${s} v0.4.4</p></header>\n <section style="color: ${o};">\n <div><b>New version of ${s} available: v${r.version}</b></div>\n <div>\n <a class="nsl-update-btn-open" href="${r.url}" target="_blank">See the code</a> or\n <a class="nsl-update-btn-copy" href="">copy it to clipboard</a>.\n </div>\n </section>\n `});this._uiUtils.widgetUtils.asButtonLink(a.querySelector(".nsl-update-btn-open")),this._uiUtils.widgetUtils.asButtonLink(this._uiUtils.widgetUtils.withListeners(a.querySelector(".nsl-update-btn-copy"),{click:e=>{try{e.preventDefault(),this._uiUtils.copyToClipboard(r.code),this._uiUtils.showSnackbar(`\n <div style="color: green;">\n <div><b>Code for v${r.version} successfully copied to clipboard.</b></div>\n <small>(Hopefully you know what to do 🙂)</small>\n </div>\n `,5e3)}catch(e){this._onError(e)}}})),this._uiUtils.showSnackbar(a,-1)}catch(e){if(e instanceof u)return;this._logUtils.warn("Error while checking for updates: "+(e.message||e))}}_checkRequiresToken(e){const s=e.requiresToken();if(!s)return;const n=Object.assign(t.createElement("div"),{innerHTML:`\n <div style="color: orange;">\n <p><b>Fetching info for this link requires a token.</b></p>\n <p style="color: gray;">(Reason: ${s})</p>\n </div>\n <div><button>Provide token now</button></div>\n `});return this._uiUtils.widgetUtils.asButton(n.querySelector("button"),"cornflowerblue",{click:async()=>e.setToken(await this._promptForToken(e.constructor))}),n}_clearTokens(){[this._ghUtils,this._jiraUtils].forEach(e=>{const t=this._KEYS.get(e.constructor);this._storageUtils.delete(t),e.setToken(null)})}async _getPopupContentForGithubCommit(e){const t=this._checkRequiresToken(this._ghUtils);if(t)return t;const s=e.nslOwner,n=e.nslRepo,i=e.nslCommit,r=await this._ghUtils.getCommitInfo(s,n,i),o=this._uiUtils.escapeHtml(r.message.split("\n",1).pop()),a=this._uiUtils.escapeHtml(r.message.slice(o.length).trim());return`\n <p style="\n align-items: center;\n border-bottom: 1px solid lightgray;\n display: flex;\n font-size: 0.9em;\n padding-bottom: 8px;\n ">\n <span style="flex: auto; margin-right: 15px;">\n <img src="${r.author.avatar}" width="25" height="25" style="border-radius: 6px;" />\n <a href="${r.author.url}" target="_blank">@${r.author.username}</a>\n </span>\n <small style="color: gray; text-align: right;">\n Committed on: ${r.committerDate.toLocaleString()}\n </small>\n </p>\n <p style="align-items: center; display: flex; font-size: 1.25em;">\n <b style="flex: auto;">${o}</b>\n <span style="color: gray; margin-left: 30px;">@${r.sha.slice(0,7)}</span>\n </p>\n <pre style="margin-top: 24px;">${a||"<i>No body.</i>"}</pre>\n ${this._getPopupContentForGithubFiles(r.files,r.stats,r.hasMoreFiles,r.filesUrl)}\n `}_getPopupContentForGithubFiles(e,t,s,n){const i={added:"green",modified:"darkorchid",removed:"red",renamed:"blue"},r="Diff too large to display...",o=e=>`\n <small style="text-align: right; white-space: nowrap;">\n <span style="color: ${i.added}; display: inline-block; min-width: 33px;">\n +${e.additions}\n </span>\n <span style="color: ${i.removed}; display: inline-block; min-width: 33px;">\n -${e.deletions}\n </span>\n </small>\n `;return`\n <hr />\n <div>\n <p style="display: flex; justify-content: space-between;">\n <b>Files (${e.length}):</b>\n <small style="color: lightgray;">Click on a file to see the diff.</small>\n <span>${o(t)}</span>\n </p>\n <div style="overflow: auto;">\n <div style="display: flex; flex-direction: column; width: fit-content;">\n ${e.map(e=>{const t=null===e.patch?r:e.patch.replace(/&/g,"&").replace(/'/g,"'").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">"),s=t.replace(/\n/g,"\n"),n=t.split("\n").map(e=>`<span style="${e===r?"font-style: italic;":e.startsWith("+")?"background-color: rgba(0, 255, 0, 0.11);":e.startsWith("-")?"background-color: rgba(255, 0, 0, 0.11);":e.startsWith("@@")?"color: rgba(0, 0, 0, 0.33);":"color: rgba(0, 0, 0, 0.66);"}">${e}</span>`).join("\n");return`\n <details>\n <summary\n style="align-items: baseline; cursor: pointer; display: flex; margin: 0 15px 10px; outline: none;"\n title="${s}">\n <small style="\n background-color: ${i[e.status]};\n border-radius: 6px;\n color: white;\n font-size: 0.75em;\n line-height: 1em;\n margin-right: 5px;\n min-width: 55px;\n opacity: 0.5;\n padding: 2px 4px;\n text-align: center;\n ">\n ${e.status}\n </small>\n <span style="flex: auto; white-space: nowrap;">\n ${e.filename}\n </span>\n ${o(e.stats)}\n </summary>\n <pre style="font-size: 0.9em; line-height: calc(0.9em + 5px);">${n}</pre>\n </details>\n `}).join("")}\n </div>\n </div>\n </div>\n ${s?`\n <p style="text-align: center;">\n <i style="color: gray;">\n ...Only showing the first ${e.length} files -\n <a href="${n}" target="_blank">see all of them on GitHub</a>...\n </i>\n </p>\n `:""}\n `}async _getPopupContentForGithubIssue(e){const t=this._checkRequiresToken(this._ghUtils);if(t)return t;const s=e.nslOwner,n=e.nslRepo,i=e.nslNumber,r=await this._ghUtils.getIssueInfo(s,n,i),o=r.prInfo,a=this._uiUtils.escapeHtml(r.title),l=this._uiUtils.escapeHtml(r.description.replace(/^<!--[^]*?-->\s*/,"")),c=r.isPr?this._getPopupContentForGithubFiles(o.files,o.stats,o.hasMoreFiles,o.filesUrl):"";return`\n <p style="\n align-items: center;\n border-bottom: 1px solid lightgray;\n display: flex;\n font-size: 0.9em;\n padding-bottom: 8px;\n ">\n <span style="flex: auto; margin-right: 15px;">\n <img src="${r.author.avatar}" width="25" height="25" style="border-radius: 6px;" />\n <a href="${r.author.url}" target="_blank">@${r.author.username}</a>\n </span>\n <span style="text-align: right;">\n ${r.labels.map(e=>`\n <small style="\n border: 1px solid;\n border-radius: 6px;\n line-height: 2.5em;\n margin-left: 3px;\n padding: 2px 4px;\n text-align: center;\n white-space: nowrap;\n ">${e}</small>\n `).join("\n")}\n </span>\n </p>\n <p style="align-items: center; display: flex; font-size: 1.25em;">\n <span style="\n background-color: ${{closed:"red",draft:"gray",merged:"darkorchid",open:"green"}[r.state]||"black"};\n border-radius: 6px;\n color: white;\n font-size: 0.75em;\n margin-right: 10px;\n padding: 3px 6px;\n text-align: center;\n ">\n ${r.state.toUpperCase()}\n </span>\n <b style="flex: auto;">${a}</b>\n <span style="color: gray; margin-left: 30px; white-space: nowrap;">\n <span style="color: lightgray;">${r.isPr?"PR":"Issue"}:</span>\n #${r.number}\n </span>\n </p>\n <pre style="margin-top: 24px;">${l||'<i style="color: gray;">No description.</i>'}</pre>\n ${c}\n `}async _getPopupContentForJira(e){const t=this._checkRequiresToken(this._jiraUtils);if(t)return t;const s={closed:"red",done:"green","in progress":"blue","in review":"darkorchid",open:"gray",reopened:"gray",resolved:"green","selected for development":"gray"},n=e.nslNumber,i=await this._jiraUtils.getIssueInfo(n),r=i.issueLinks.reduce((e,t)=>((e[t.type]||(e[t.type]=[])).push(t),e),{}),o=(e,t)=>{const n=e.otherIssue,i=n.status.name;return`\n <li style="\n align-items: center;\n ${t%2?"background-color: rgba(0, 0, 0, 0.05);":""}\n display: flex;\n padding: 2px 5px;\n ">\n <span style="flex: auto; ${["closed","done","resolved"].includes(i.toLowerCase())?"text-decoration: line-through;":""}">\n <a href="${n.url}" target="_blank" style="display: flex;">\n <b style="white-space: nowrap;">${n.number}: </b>\n ${this._uiUtils.escapeHtml(n.title)}\n </a>\n </span>\n <small style="\n background-color: ${s[i.toLowerCase()]||"black"};\n border-radius: 6px;\n color: white;\n font-size: 0.75em;\n margin-left: 10px;\n padding: 0 4px;\n text-align: center;\n white-space: nowrap;\n ">\n ${i.toUpperCase()}\n </small>\n </li>\n `};return`\n <p style="\n align-items: center;\n border-bottom: 1px solid lightgray;\n display: flex;\n font-size: 0.9em;\n padding-bottom: 8px;\n ">\n <span style="align-items: center; display: flex; margin-right: 15px;">\n <img src="${i.reporter.avatar}" width="25" height="25" style="border-radius: 6px; margin-right: 5px;" />\n <span style="flex-direction: column; display: flex;">\n <small style="color: gray;">Reported by:</small>\n <a href="${i.reporter.url}" target="_blank">${i.reporter.name}</a>\n </span>\n </span>\n <span style="align-items: center; flex: auto; display: flex; margin-right: 15px;">\n <img src="${i.assignee?i.assignee.avatar:v.EMPTY_IMAGE_SRC}" width="25" height="25"\n style="border-radius: 6px; margin-right: 5px;" />\n <span style="flex-direction: column; display: flex;">\n <small style="color: gray;">Assigned to:</small>\n ${i.assignee?`<a href="${i.assignee.url}" target="_blank">${i.assignee.name}</a>`:"-"}\n </span>\n </span>\n <span style="flex-direction: column; display: flex; text-align: right;">\n <span>\n <span style="color: lightgray;">Project:</span>\n <span style="color: gray;">${i.project}</span>\n </span>\n <span>\n <span style="color: lightgray;">Fix version(s):</span>\n <span style="color: gray;">\n ${i.fixVersions.map(e=>`\n <small style="\n border: 1px solid;\n border-radius: 6px;\n line-height: 2.5em;\n margin-left: 3px;\n padding: 2px 4px;\n text-align: center;\n white-space: nowrap;\n ">${e}</small>\n `).join("\n")||"-"}\n </span>\n </span>\n </span>\n </p>\n <p style="align-items: center; display: flex; font-size: 1.25em;">\n <span style="\n background-color: ${s[i.status.name.toLowerCase()]||"black"};\n border-radius: 6px;\n color: white;\n font-size: 0.75em;\n margin-right: 10px;\n padding: 3px 6px;\n text-align: center;\n white-space: nowrap;\n ">\n ${i.status.name.toUpperCase()}\n </span>\n <b style="flex: auto;">${i.title}</b>\n <span style="color: gray; margin-left: 30px; white-space: nowrap;">\n <span style="color: lightgray;">${i.type}:</span>\n ${i.number}\n </span>\n </p>\n <pre style="margin-top: 24px; white-space: normal;">\n ${this._uiUtils.escapeHtml(i.description)||'<i style="color: gray;">No description.</i>'}\n </pre>\n <hr />\n <p>\n <b>PR:</b>\n ${i.prLink?`\n <a href="${i.prLink}" target="_blank">#${i.prLink.replace(/.*\/(\d+)$/,"$1")}</a>\n `:'<i style="color: gray;">-</i>'}\n </p>\n ${i.issueLinks.length?`\n <hr />\n <div>\n <p><b>Linked issues (${i.issueLinks.length}):</b></p>\n <div style="padding-left: 15px;">\n ${Object.keys(r).map(e=>`\n <div>\n <i style="text-transform: capitalize;">${e} (${r[e].length}):</i>\n <ul style="margin: 5px 0 15px 15px;">\n ${r[e].reverse().map(o).join("")}\n </ul>\n </div>\n `).join("")}\n </div>\n </div>\n `:""}\n `}_getRootElement(){const e=".p-workspace-layout, .p-workspace, #client_body",s=t.querySelector(e);if(!s)throw new Error(`Unable to find root element matching selector '${e}'.`);return s}async _getStoredTokenFor(e){const t=this._KEYS.get(e);try{const s=this._storageUtils.get(t);if(!s)return;const n=await this._whileNotDestroyed(this._secretUtils.decrypt(s));return e.validateToken(n),n}catch(s){if(s instanceof u)throw s;this._storageUtils.delete(t);const n=`Found a corrupted or invalid stored ${e.TOKEN_NAME} and removed it.`;this._logUtils.error(s),this._logUtils.warn(n),this._uiUtils.showSnackbar(`<div style="color: orange;">\n <b>${n}</b><br />\n <small>(See the console for more details.)</small>\n </div>`,3e3)}}async _promptForToken(n,i=2){const r=n.TOKEN_NAME,o=n.TOKEN_DESCRIPTION_HTML,a=`$$${s}-promptForToken-ctx-${Date.now()}-${Math.random()}`,l=e[a]={token:"",storage:"local"},c=`\n <h2>No ${r} detected</h2>\n <hr />\n <p>It seems that you have not provided a ${r}.</p>\n <p>${o}</p>\n <hr />\n <p>Would you like to provide one now?</p>\n <p>\n <form>\n <label style="cursor: default; display: block; margin-bottom: 10px;">\n ${r}:\n <div style="align-items: center; display: flex; position: relative;">\n <input\n type="password"\n class="nsl-input-token-value"\n placeholder="(required)"\n value="${l.token}"\n style="margin: 0;"\n />\n <span\n class="nsl-input-addon-token-value"\n style="cursor: pointer; font-size: 2em; position: absolute; right: 10px;">\n 👁️\n </span>\n </div>\n </label>\n <label style="cursor: default; display: block; margin-bottom: 10px;">\n Store:\n <div style="align-items: center; display: flex; position: relative;">\n <select class="nsl-input-token-store" value="${l.store}" style="cursor: pointer;">\n <option value="local">Permanently (for this browser)</option>\n <option value="session">Only for current session</option>\n </select>\n <span style="\n font-size: 2em;\n pointer-events: none;\n position: absolute;\n right: 24px;\n transform: rotateZ(90deg);\n ">\n ❯\n </span>\n </div>\n </label>\n </form>\n </p>\n `;try{const s=this._uiUtils.widgetUtils,i=Object.assign(t.createElement("div"),{innerHTML:c}),o=i.querySelector(".nsl-input-token-value"),h=i.querySelector(".nsl-input-addon-token-value"),p=i.querySelector(".nsl-input-token-store");s.withListeners(s.asInputField(o),{input:()=>l.token=o.value}),s.withListeners(h,{mousedown:()=>o.type="text",mouseup:()=>o.type="password"}),s.withListeners(s.asInputField(p),{change:()=>l.storage=p.value});if(!await this._uiUtils.showDialog(i,"Store token","Not now").finally(()=>delete e[a]))return;n.validateToken(l.token);const d=this._KEYS.get(n),u=await this._whileNotDestroyed(this._secretUtils.encrypt(l.token));return this._storageUtils[l.storage].set(d,u),this._uiUtils.showSnackbar(`<b style="color: green;">Successfully stored ${r}.</b>`,3e3),l.token}catch(e){if(e instanceof u)throw e;if(i>0)return this._onError(e),this._promptForToken(n,--i);const t=`Unable to acquire a valid ${r}. Giving up for now :(`;this._logUtils.error(e),this._logUtils.warn(t),this._uiUtils.showSnackbar(`<div style="color: orange;">\n <b>${t}</b><br />\n <small>(See the console for more details.)</small>\n </div>`,5e3)}}_onError(e){if(e instanceof u)return;if(e instanceof d){const t=e.provider,s=t.constructor,n=this._KEYS.get(s);t.setToken(null),this._storageUtils.delete(n),this._logUtils.warn(`Removed invalid ${s.TOKEN_NAME}.`)}const t=""+(e.message||e),s=t.length>250?t.slice(0,250)+"...":t;this._logUtils.error(e),this._uiUtils.showSnackbar(`<pre style="background-color: white; border: none; color: red; margin: 0;"><b>${this._uiUtils.escapeHtml(s)}</b><br /><small>(See the console for more details.)</small></pre>`,1e4)}_postInstall(){const e=[this._ghUtils,this._jiraUtils].some(e=>e.hasToken()),i=this._updateUtils.isDevelopmentVersion(n),r=e||i,o=Object.assign(t.createElement("div"),{innerHTML:`\n <b style="color: cornflowerblue;">${s} v0.4.4 is up and running 😎</b>\n ${r?`\n <small style="color: gray; display: block; margin-top: 16px;">\n Available actions:\n <ul style="margin-bottom: 0;">\n ${e?'<li><a class="nsl-install-btn-clear-tokens">Clear stored tokens</a></li>':""}\n ${i?'<li><a class="nsl-install-btn-cdn-update">Update from CDN</a></li>':""}\n </ul>\n </small>\n `:""}\n `});e&&this._uiUtils.widgetUtils.asButtonLink(this._uiUtils.widgetUtils.withListeners(o.querySelector(".nsl-install-btn-clear-tokens"),{click:e=>{this._clearTokens(),this._uiUtils.showSnackbar('<b style="color: green;">Successfully removed stored tokens.</b>',2e3),e.target.parentNode.remove()}})),i&&this._uiUtils.widgetUtils.asButtonLink(this._uiUtils.widgetUtils.withListeners(o.querySelector(".nsl-install-btn-cdn-update"),{click:()=>this._checkForUpdate(!0)})),this._uiUtils.showSnackbar(o,5e3)}_schedule(e,t){const s=()=>clearTimeout(n),n=setTimeout(()=>{const t=this._cleanUpFns.indexOf(s);-1!==t&&this._cleanUpFns.splice(t,1),e()},t);this._cleanUpFns.push(s)}_whileNotDestroyed(e){return Promise.race([e,this._destroyedDeferred.promise])}}).main()})(window,window.document);