diff --git a/src/background/background.js b/src/background/background.js index 63a29e65..2647b50d 100755 --- a/src/background/background.js +++ b/src/background/background.js @@ -1,3 +1,8 @@ +/** + * @todo Better error PAT error handling (e.g. when PAT is invalid or expired) + * @todo Better docstrings for background.js + */ + try { importScripts( "../shared/js/utils/octokit.bundle.js", @@ -61,7 +66,7 @@ const initGist = async () => { logOk(`Sync successfully enabled (${duration}s).`); badgeOk(); } else { - logError("[initGist]", error); + logError("[initGist]", error || payload); badgeError(); } badgeClear(); @@ -257,7 +262,9 @@ const pullSyncPapers = async () => { const start = Date.now(); consoleHeader(`Pulling ${String.fromCodePoint("0x23EC")}`); log("Pulling from Github..."); - global.state.gistData = await getDataForGistFile(global.state.gistFile); + global.state.gistData = await getDataForGistFile({ + file: global.state.gistFile, + }); const remotePapers = global.state.gistData; log("Pulled papers:", remotePapers); const duration = (Date.now() - start) / 1e3; @@ -284,7 +291,11 @@ const pushSyncPapers = async () => { chrome.action.setBadgeBackgroundColor({ color: "rgb(189, 127, 10)" }); const papers = (await getStorage("papers")) ?? {}; log("Papers to write: ", papers); - await updateGist(global.state.gistFile, papers, global.state.gistId); + await updateGistFile({ + file: global.state.gistFile, + content: papers, + gistid: global.state.gistId, + }); const duration = (Date.now() - start) / 1e3; log(`Writing to Github... Done (${duration}s)!`); badgeOk(); diff --git a/src/options/options.js b/src/options/options.js index 1cb2441a..78a66842 100644 --- a/src/options/options.js +++ b/src/options/options.js @@ -925,7 +925,7 @@ const setupSync = async () => { return; } const { file, pat, gistId } = payload; - const data = await getDataForGistFile(file); + const data = await getDataForGistFile({ file }); let userChoice = "no-remote"; if (data) { console.log("Existing data file content:", data); @@ -949,7 +949,7 @@ const setupSync = async () => { "text/json" ); } - await updateGist(file, global.state.papers, gistId); + await updateGistFile({ file, content: global.state.papers, gistId }); await setSyncOk(); } else if (userChoice === "local-remote") { // overwrite local data with remote data @@ -1033,7 +1033,7 @@ const setupSync = async () => { ); } await setStorage("papers", papersToWrite); - await updateGist(file, papersToWrite, gistId); + await updateGistFile({ file, content: papersToWrite, gistId }); await setSyncOk(); } else { setHTML("overwriteRemoteFeedback", message); diff --git a/src/shared/js/utils/sync.js b/src/shared/js/utils/sync.js index e7184a50..8b45eafc 100644 --- a/src/shared/js/utils/sync.js +++ b/src/shared/js/utils/sync.js @@ -1,24 +1,25 @@ +/** + * Gets the default content of the PaperMemory sync Gist file. + * If the file does not exist, it will be created. + * @param {string} pat - Personal Access Token (will be retrieved if not provided) + * @param {boolean} store - Whether to store the PAT in the browser storage + * @returns {Promise<{ ok: boolean, payload: { file: { filename: string, raw_url: string, content: object }, pat: string, gistId: string } }>} + */ const getGist = async (pat, store = true) => { try { - // await githubGist.touch(); if (!pat) { pat = await getStorage("syncPAT"); } if (!pat) { setStorage("syncState", false); - return { - ok: false, - payload: "noPAT", - }; + return { ok: false, payload: "noPAT" }; } const isTest = await getStorage("syncTest"); - const fileName = isTest ? "TestsPaperMemorySync" : "DO_NO_EDIT__PaperMemorySyncV2.json"; const description = "Automated PaperMemory sync Gist. Do not edit manually."; - const requestWithAuth = octokitRequest.defaults({ headers: { authorization: `token ${pat}`, @@ -34,16 +35,14 @@ const getGist = async (pat, store = true) => { if (!gist) { info("No Gist found. Creating new one..."); - const response = await requestWithAuth("POST /gists", { + const papers = await getStorage("papers"); + gist = await createGistWithFile({ + filename: fileName, + pat, description, - files: { - [fileName]: { - content: JSON.stringify(await getStorage("papers")), - }, - }, - public: false, + content: papers, }); - gist = response.data; + if (!gist) return { ok: false, payload: "wrongPAT" }; } file = gist.files[fileName]; const gistId = gist.id; @@ -52,16 +51,77 @@ const getGist = async (pat, store = true) => { } catch (e) { console.log(e); warn("Because of the error ^ syncing is now disabled."); - // setStorage("syncState", false); - return { - ok: false, - payload: "wrongPAT", - error: e, - }; + setStorage("syncState", false); + return { ok: false, payload: "wrongPAT", error: e }; } }; -const getDataForGistFile = async (file) => { +/** + * Create a new Gist with a file and some content. The content will be stringified if not + * already a string. The PAT will be retrieved from the browser storage if not provided. + * @param {object} options - Options object + * @param {string} options.file - File name or object with `filename` property + * @param {string} options.pat - Personal Access Token (will be retrieved if not provided) + * @param {string} options.content - Content of the file (will be stringified if not already a string) + * @param {string} options.description - Description of the gist + * @returns {Promise} - The Gist object + */ +const createGistWithFile = async ({ + file, + pat, + content, + description = "Automated PaperMemory sync Gist. Do not edit manually.", +}) => { + if (typeof content !== "string") { + content = JSON.stringify(content, null, ""); + } + if (typeof file === "string") { + file = { filename: file }; + } + if (!pat) { + pat = await getStorage("syncPAT"); + } + if (!pat) { + warn("(createGistWithFile) No PAT found. Aborting and disabling sync."); + setStorage("syncState", false); + return; + } + const requestWithAuth = octokitRequest.defaults({ + headers: { + authorization: `token ${pat}`, + "X-GitHub-Api-Version": "2022-11-28", + }, + }); + const response = await requestWithAuth("POST /gists", { + description, + files: { [filename]: { content } }, + public: false, + }); + return response.data; +}; + +/** + * Get the content of a Gist file. Accept `file:{filename, raw_url}` object or `filename` and `url` strings. + * The PAT will be retrieved from the browser storage if not provided. + * @param {object} options - Options object + * @param {string} options.file - File name or object with `filename` property and `raw_url` property + * @param {string} options.filename - File Name (will be ignored if `file` is provided) + * @param {string} options.url - Raw URL of the file (will be ignored if `file` is provided) + * @param {string} options.pat - Personal Access Token (will be retrieved if not provided) + * @returns {Promise} - The content of the file + */ +const getDataForGistFile = async ({ file, filename, url }) => { + if (typeof file === "undefined") { + if (typeof url === "undefined") { + throw new Error("No file or url provided."); + } + if (typeof filename === "undefined") { + throw new Error("No filename provided."); + } + file = { filename: file }; + } else if (typeof filename !== "undefined" || typeof url !== "undefined") { + warn("`file` argument has precedence over `filename` or `url`."); + } if (file.filename.endsWith(".json")) { const { data, status } = await fetchJSON(file.raw_url); return data; @@ -69,15 +129,28 @@ const getDataForGistFile = async (file) => { return await fetchText(file.raw_url); }; -const updateGist = async (file, papers, gistId) => { - const pat = await getStorage("syncPAT"); +/** + * Update a Gist file with some content. The content will be stringified if not + * already a string. The PAT will be retrieved from the browser storage if not provided. + * @param {object} options - Options object + * @param {string} options.file - File name or object with `filename` property + * @param {string} options.pat - Personal Access Token (will be retrieved if not provided) + * @param {string} options.content - Content of the file (will be stringified if not already a string) + * @param {string} options.gistId - ID of the Gist + * @returns {Promise} - The Gist object + */ +const updateGistFile = async ({ file, content, gistId, pat }) => { + if (!pat) pat = await getStorage("syncPAT"); if (!pat) { - info("(updateGist) No PAT found. Syncing is now disabled."); + warn("(updateGistFile) No PAT found. Aborting and disabling sync."); setStorage("syncState", false); - return { - ok: false, - payload: "noPAT", - }; + return { ok: false, payload: "noPAT" }; + } + if (typeof content !== "string") { + content = JSON.stringify(content, null, ""); + } + if (typeof file === "string") { + file = { filename: file }; } const requestWithAuth = octokitRequest.defaults({ headers: { @@ -87,16 +160,23 @@ const updateGist = async (file, papers, gistId) => { }); return await requestWithAuth(`PATCH /gists/${gistId}`, { gist_id: gistId, - files: { - [file.filename]: { - content: JSON.stringify(papers, null, ""), - }, - }, + files: { [file.filename]: { content } }, }); }; +/** + * Writes the current `papers` Memory to the default sync Gist file. + * @returns {Promise} + */ const pushToRemote = async () => await sendMessageToBackground({ type: "writeSync" }); +/** + * Pulls the current `papers` from the default sync Gist file. + * If remote papers are found, the local `papers` will be updated. and set to storage. + * @param {object} papers - The current `papers` in the user memory + * @param {boolean} isContentScript - Whether the function is called from a content script + * @returns {Promise} - The remote `papers` from the Gist file + */ const pullFromRemote = async (papers, isContentScript) => { const start = Date.now(); const remotePapers = await sendMessageToBackground({ type: "pullSync" }); @@ -116,8 +196,22 @@ const pullFromRemote = async (papers, isContentScript) => { return remotePapers; }; +/** + * Whether the user has enabled sync. + * @returns {Promise} + */ const shouldSync = async () => !!(await getStorage("syncState")); +/** + * Initialize the sync state. + * @param {object} options - Options object + * @param {object} options.papers - The current `papers` in the user memory + * @param {boolean} options.isContentScript - Whether the function is called from a content script + * @param {boolean} options.forceInit - Whether to force the initialization + * @param {function} options.stateIsReady - Callback function to be called when the state is ready + * @param {function} options.remoteIsReady - Callback function to be called when the remote is ready + * @returns {Promise} + */ const initSyncAndState = async ({ papers = null, isContentScript = false, @@ -157,12 +251,20 @@ const initSyncAndState = async ({ remoteIsReady(); }; +/** + * Show the sync loader in the popup. + * @returns {Promise} + */ const startSyncLoader = async () => { showId("sync-popup-feedback"); hideId("sync-popup-error"); hideId("sync-popup-synced"); showId("sync-popup-syncing", "flex"); }; +/** + * Hide the sync loader in the popup and display the success message. + * @returns {Promise} + */ const successSyncLoader = async () => { showId("sync-popup-feedback"); hideId("sync-popup-syncing"); @@ -172,6 +274,10 @@ const successSyncLoader = async () => { hideId("sync-popup-feedback"); }, 2000); }; +/** + * Hide the sync loader in the popup and display the error message. + * @returns {Promise} + */ const errorSyncLoader = async () => { showId("sync-popup-feedback"); hideId("sync-popup-syncing"); diff --git a/src/shared/min/utils.min.js b/src/shared/min/utils.min.js index f3f7e692..06b1d67d 100644 --- a/src/shared/min/utils.min.js +++ b/src/shared/min/utils.min.js @@ -4,7 +4,7 @@ /!\\ If you change the default title function in the Advanced Options and do not include a paper's title in the file name, PaperMemory may not be able to open the file and will instead open the pdf url. /!\\ Unfortunately, PaperMemory cannot detect papers that have not been *downloaded there* so putting papers in this folder will not make them discoverable by the \`browser.downloads\` API PaperMemory uses.`,global.englishStopWords=new Set(["i","me","my","myself","we","our","ours","ourselves","you","your","yours","yourself","yourselves","he","him","his","himself","she","her","hers","herself","it","its","itself","they","them","their","theirs","themselves","what","which","who","whom","this","that","these","those","am","is","are","was","were","be","been","being","have","has","had","having","do","does","did","doing","a","an","the","and","but","if","or","because","as","until","while","of","at","by","for","with","about","against","between","into","through","during","before","after","above","below","to","from","up","down","in","out","on","off","over","under","again","further","then","once","here","there","when","where","why","how","all","any","both","each","few","more","most","other","some","such","no","nor","not","only","own","same","so","than","too","very","s","t","can","will","just","don","should","now"]),global.journalAbbreviations=null,"undefined"!=typeof module&&null!=module.exports&&((dummyModule=module).exports={state:global.state,descendingSortKeys:global.descendingSortKeys,select2Options:global.select2Options,prefsCheckNames:global.prefsCheckNames,prefsCheckDefaultFalse:global.prefsCheckDefaultFalse,prefsStorageKeys:global.prefsStorageKeys,sourceExtras:global.sourceExtras,preprintSources:global.preprintSources,knownPaperPages:global.knownPaperPages,overrideORConfs:global.overrideORConfs,overridePMLRConfs:global.overridePMLRConfs,overrideDBLPVenues:global.overrideDBLPVenues,fuzzyTitleMatchMinDist:global.fuzzyTitleMatchMinDist,storeReadme:global.storeReadme,englishStopWords:global.englishStopWords,journalAbbreviations:global.journalAbbreviations,consolHeaderStyle:global.consolHeaderStyle});const levenshtein=(e,t)=>{if(e===t)return 0;e.length>t.length&&(r=e,e=t,t=r);for(var r,a=e.length,i=t.length;0 expected "+e+", found "+this.input.substring(this.pos));this.pos+=e.length,this.skipWhitespace(t)},this.tryMatch=function(e,t){return this.skipWhitespace(t=null==t?!0:t),this.input.substring(this.pos,this.pos+e.length)==e},this.matchAt=function(){for(;this.input.length>this.pos&&"@"!=this.input[this.pos];)this.pos++;return"@"==this.input[this.pos]},this.skipWhitespace=function(e){for(;this.isWhitespace(this.input[this.pos]);)this.pos++;if("%"==this.input[this.pos]&&1==e){for(;"\n"!=this.input[this.pos];)this.pos++;this.skipWhitespace(e)}},this.value_braces=function(){for(var e,t=0,r=(this.match("{",!1),this.pos),a=!1;;){if(!a)if("}"==this.input[this.pos]){if(!(0=this.input.length-1)throw TypeError("Unterminated value: value_braces");a="\\"==this.input[this.pos]&&0==a,this.pos++}},this.value_comment=function(){for(var e="",t=0;!this.tryMatch("}",!1)||0!=t;){if(e+=this.input[this.pos],"{"==this.input[this.pos]&&t++,"}"==this.input[this.pos]&&t--,this.pos>=this.input.length-1)throw TypeError("Unterminated value: value_comment",+this.input.substring(start));this.pos++}return e},this.value_quotes=function(){this.match('"',!1);for(var e,t=this.pos,r=!1;;){if(!r){if('"'==this.input[this.pos])return e=this.pos,this.match('"',!1),this.input.substring(t,e);if(this.pos>=this.input.length-1)throw TypeError("Unterminated value: value_quotes",this.input.substring(t))}r="\\"==this.input[this.pos]&&0==r,this.pos++}},this.single_value=function(){var e=this.pos;if(this.tryMatch("{"))return this.value_braces();if(this.tryMatch('"'))return this.value_quotes();var t=this.key();if(t.match("^[0-9]+$"))return t;if(0<=this.months.indexOf(t.toLowerCase()))return t.toLowerCase();throw"Value expected: single_value"+this.input.substring(e)+" for key: "+t},this.value=function(){var e=[];for(e.push(this.single_value());this.tryMatch("#");)this.match("#"),e.push(this.single_value());return e.join("")},this.key=function(e){for(var t=this.pos;;){if(this.pos>=this.input.length)throw TypeError("Runaway key: key");if(0<=this.notKey.indexOf(this.input[this.pos]))return e&&","!=this.input[this.pos]?(this.pos=t,null):this.input.substring(t,this.pos);this.pos++}},this.key_equals_value=function(){var e,t=this.key();if(this.tryMatch("="))return this.match("="),e=this.value(),[t=t.trim(),e];throw TypeError("Value expected, equals sign missing: key_equals_value",this.input.substring(this.pos))},this.key_value_list=function(){var e=this.key_equals_value();for(this.currentEntry.entryTags={},this.currentEntry.entryTags[e[0]]=e[1];this.tryMatch(",")&&(this.match(","),!this.tryMatch("}"));)e=this.key_equals_value(),this.currentEntry.entryTags[e[0]]=e[1]},this.entry_body=function(e){this.currentEntry={},this.currentEntry.citationKey=this.key(!0),this.currentEntry.entryType=e.substring(1),null!=this.currentEntry.citationKey&&this.match(","),this.key_value_list(),this.entries.push(this.currentEntry)},this.directive=function(){return this.match("@"),"@"+this.key()},this.preamble=function(){this.currentEntry={},this.currentEntry.entryType="PREAMBLE",this.currentEntry.entry=this.value_comment(),this.entries.push(this.currentEntry)},this.comment=function(){this.currentEntry={},this.currentEntry.entryType="COMMENT",this.currentEntry.entry=this.value_comment(),this.entries.push(this.currentEntry)},this.entry=function(e){this.entry_body(e)},this.alernativeCitationKey=function(){this.entries.forEach(function(e){!e.citationKey&&e.entryTags&&(e.citationKey="",e.entryTags.author&&(e.citationKey+=e.entryTags.author.split(",")[0]+=", "),e.citationKey+=e.entryTags.year)})},this.cleanCitationKey=function(){var e=this.pos,t=e+this.input.slice(e).indexOf(","),r=this.input.slice(0,e),a=this.input.slice(t),i=this.input.slice(e,t).split("{");let o=i[0];for(var n=1;n{let t=0,r=0,a=!0;for(const i of e.slice(1,-1))if("{"===i&&t++,"}"===i&&r++,r>t){a=!1;break}return a?e.slice(1,-1):e},bibtexToObject=e=>{var t,r,a=new BibtexParser,e=(a.setInput(e),a.bibtex(),a.getEntries()[0]),i={...e.entryTags,entryType:e.entryType,citationKey:e.citationKey};for([t,r]of Object.entries(i))r.startsWith("{")&&r.endsWith("}")&&(i[t]=safeRemoveSurroundingBraces(r)),t===t.toUpperCase()&&(i[t.toLowerCase()]=i[t],delete i[t]);return i},bibtexToString=t=>{let r=`@${(t={...t=(t="string"==typeof t?bibtexToObject(t):t).hasOwnProperty("entryTags")?{...t.entryTags,entryType:t.entryType,citationKey:t.citationKey}:t}).entryType.toLowerCase()}{${t.citationKey}, `;delete t.entryType,delete t.citationKey;var a=Math.max(...Object.keys(t).map(e=>e.length));for(const o in t)if(t.hasOwnProperty(o)&&t[o]){let e=t[o].replaceAll(/\s+/g," ").trim();e.startsWith("{")&&e.endsWith("}")&&(e=safeRemoveSurroundingBraces(e));var i=o+" ".repeat(a-o.length);r+=` ${i} = {${e}}, -`}return(r.slice(0,-2)+"\n}").replaceAll("\t"," ").replaceAll("--","-")},extractBibtexValue=(e,t)=>{e=bibtexToObject(e);return e.hasOwnProperty(t)?e[t]:""},extractAuthor=e=>extractBibtexValue(e,"author").replaceAll("{","").replaceAll("}","").replaceAll("\\","").split(" and ").map(e=>e.split(", ").reverse().join(" ")).join(" and "),logTrace=("undefined"!=typeof module&&null!=module.exports&&((dummyModule=module).exports={bibtexToObject:bibtexToObject,bibtexToString:bibtexToString,extractBibtexValue:extractBibtexValue,extractAuthor:extractAuthor}),"undefined"!=typeof LOGTRACE&&LOGTRACE),log=(...e)=>{var t;logTrace&&(t=(new Error).stack,e.push("\n\nLog trace:\n"+t.split("\n").slice(2).join("\n")));let r="%c%s ",a=!1,i=!1,o=!1,n=!1,s=!1;"[info]"===e[0]?(a=!0,e=e.slice(1)):"[warn]"===e[0]?(i=!0,e=e.slice(1)):"[error]"===e[0]?(o=!0,e=e.slice(1)):"[ok]"===e[0]?(s=!0,e=e.slice(1)):"[debug]"===e[0]&&(n=!0,e=e.slice(1)),e.forEach(e=>{switch(typeof e){case"bigint":case"number":r+="%d ";break;case"string":r+="%s ";break;default:r+="%o "}}),console.log(r,"color: "+(a?"#8BB4F7; font-weight:bold;":i?"#f3bd1e; font-weight:bold;":o?"#FF4F54; font-weight:bold;":s?"#23F62B; font-weight:bold;":n?"#BA357E; font-weight:bold;":"tan"),"[PM]",...e)},info=(...e)=>log("[info]",...e),warn=(...e)=>log("[warn]",...e),debug=(...e)=>log("[debug]",...e),logOk=(...e)=>log("[ok]",...e),logError=(...e)=>log("[error]",...e),consoleHeader=e=>console.groupCollapsed("%c"+e,global.consolHeaderStyle),getDisplayId=e=>{var t=e;return(e=e.split("_")[0].split(".")[0]).startsWith("OR-")||(e=e.split("-").slice(0,2).join("-")),global.state.papers.hasOwnProperty(t)&&("nature"===(t=global.state.papers[t]).source&&(t.note.match(/^Published\ @.+\(\d+\)$/)&&(e+="-"+t.note.split("@")[1].split("(")[0].trim().split(" ").map(e=>e[0].toUpperCase()).join("")),e.includes(t.year+"")||(e+="-"+t.year)),"acs"!==t.source||e.includes(t.year+"")||(e+="-"+t.year),"iop"!==t.source||e.includes(t.year+"")||(e+="-"+t.year)),e},isObject=e=>"object"==typeof e&&!Array.isArray(e)&&null!==e,isPdfUrl=e=>e.endsWith(".pdf")||e.endsWith("/pdf")||e.includes("openreview.net/pdf")||e.match(/\/e?pdf\//g)||e.includes("ieee.org/stamp/stamp.jsp?tp=&arnumber=")||e.includes("articlepdf");function delay(t,r){let a=0;return function(...e){clearTimeout(a),a=setTimeout(t.bind(this,...e),r||0)}}const cleanPapers=e=>{e={...e};return delete e.__dataVersion,e},firstNonStopLowercase=e=>{var e=e.toLowerCase().split(" ").map(miniHash),t=e.filter(e=>!global.englishStopWords.has(e));return(0e.toLowerCase().replace(/\W/g,""),fallbackCopyTextToClipboard=e=>{var t=document.createElement("textarea");t.value=e,t.style.top="0",t.style.left="0",t.style.position="fixed",document.body.appendChild(t),t.focus(),t.select();try{var r=document.execCommand("copy")?"successful":"unsuccessful";log("Fallback: Copying text command was "+r)}catch(e){console.error("Fallback: Oops, unable to copy",e)}document.body.removeChild(t)},copyTextToClipboard=e=>{navigator.clipboard?navigator.clipboard.writeText(e).then(()=>{log("Async: Copying to clipboard was successful!")},e=>{console.error("Async: Could not copy text: ",e)}):fallbackCopyTextToClipboard(e)};async function pasteRich(t,r){var e,a;"undefined"!=typeof ClipboardItem?(e=new Blob([t],{type:"text/html"}),a=new Blob([r],{type:"text/plain"}),e=new ClipboardItem({"text/html":e,"text/plain":a}),await navigator.clipboard.write([e])):(a=e=>{e.clipboardData.setData("text/html",t),e.clipboardData.setData("text/plain",r),e.preventDefault()},document.addEventListener("copy",a),document.execCommand("copy"),document.removeEventListener("copy",a))}const copyHyperLinkToClipboard=(e,t)=>{pasteRich(`${t}`,t+" "+e)},padRight=(e,t,r)=>(void 0===r&&(r=" "),e.length{var t=document.createElement("a");return t.href=e,t},downloadTextFile=(e,t,r)=>{var a=document.createElement("a");"text/plain"===r?(e=e.replace(/\\n/g,"%0D%0A").replace(/"/g,""),a.download=t,a.href="data:text/plain,"+e):(e=new Blob([e],{type:r}),a.href=URL.createObjectURL(e),a.download=t),a.click()},eventId=e=>e.target.closest(".memory-container").id.split("--")[1],downloadFile=(e,t)=>{var r;window.ActiveXObject?window.ActiveXObject&&document.execCommand&&((r=window.open(e,"_blank")).document.close(),r.document.execCommand("SaveAs",!0,t||e),r.close()):((r=document.createElement("a")).href=e,r.target="_blank",e=e.substring(e.lastIndexOf("/")+1),r.download=t||e,navigator.userAgent.toLowerCase().match(/(ipad|iphone|safari)/)&&navigator.userAgent.search("Chrome")<0?document.location=r.href:(t=new MouseEvent("click",{view:window,bubbles:!0,cancelable:!1}),r.dispatchEvent(t),(window.URL||window.webkitURL).revokeObjectURL(r.href)))},hashCode=e=>e.split("").reduce((e,t)=>(e=(e<<5)-e+t.charCodeAt(0))&e,0),parseCVFUrl=e=>{var t=e.replace("https://openaccess.thecvf.com/content","").slice(1).split("/")[0].split("_");let r,a;a=1===t.length?(r=t[0].slice(0,-4),t[0].slice(-4)):(r=t[0].toUpperCase(),t[1]);t=e.split("/").last().split(".")[0],e=(hashCode(t)+"").replace("-","").slice(0,8),t=`${r}-${a}_`+e;return{conf:r,year:a,id:t}},cleanBiorxivURL=e=>e=(e=e.replace(".full.pdf","")).match(/\d$/)?e:e.split(".").slice(0,-1).join("."),textareaFocusEnd=e=>{setTimeout(()=>{e.selectionStart=e.selectionEnd=1e4},0)},tablerSvg=(e,t,r)=>{switch(t=(t=void 0===t?"":t)&&`id="${t}"`,r=(r=(r=void 0===r?[]:r).filter(e=>e))&&`class="${r.join(" ")}"`,e){case"adjustments":return``;case"circle-x":return``;case"star":return``;case"writing":return``;case"file-symlink":return``;case"link":return``;case"clipboard-list":return``;case"archive":return``;case"external-link":return``;case"file-download":return``;case"cirlce-x":return``;case"settings":return``;case"messages":return``;case"vanity":return``;case"ar5iv":return``;case"vocabulary":return``;case"database-export":return``;case"eyeglass":return``;case"markdown":return``;case"math-function":return``;case"device-desktop-code":return``;case"info-square-rounded":return``;case"huggingface":return``;default:return""}},stringifyError=e=>{const t=chrome.runtime.id;return e.stack.split("\n").map(e=>e.split(" ").map(e=>e.split(t).last()).join(" ")).join("
")},arraysIdentical=(e,t)=>{var r=e.length;if(r!=t.length)return!1;for(;r--;)if(e[r]!==t[r])return!1;return!0},parseTags=e=>{e=Array.from(e.selectedOptions,e=>e.value.trim()).filter(e=>e);return e.sort(),e},getPaperEdits=(e,t)=>{let r,a,i,o;return o=t?(r=val("popup-form-note-textarea--"+e),i=val(document.getElementById("popup-form-note--"+e).querySelector(".form-code-input")),a=parseTags(findEl("popup-item-tags--"+e)),findEl("checkFavorite--"+e).checked):(r=val(findEl(e,"form-note-textarea")),i=val(findEl(e,"form-code-input")),a=parseTags(findEl(e,"memory-item-tags")),hasClass("memory-container--"+e,"favorite")),{note:r,tags:a,codeLink:i,favorite:o}},setFormChangeListener=(e,t)=>{let r,a,i,o;t?(r="#popup-item-tags--"+e.replace(".","\\."),i="popup-form-codeLink--"+e,a="popup-form-note-textarea--"+e,o="checkFavorite--"+e,$(r).on("change",delay(monitorPaperEdits(e,t),300)),addListener(i,"keyup",delay(monitorPaperEdits(e,t),300)),addListener(a,"keyup",delay(monitorPaperEdits(e,t),300)),addListener(o,"change",delay(monitorPaperEdits(e,t),300))):(r=".memory-item-tags",i=".form-code-input",a=".form-note-textarea",addEventToClass(i,"keyup",delay(monitorPaperEdits(void 0,t),300)),addEventToClass(a,"keyup",delay(monitorPaperEdits(void 0,t),300)))},monitorPaperEdits=(l,p)=>e=>{let t;t=void 0===l?eventId(e):l;var r=getPaperEdits(t,p),a=global.state.papers[t];let i=!1;for(const s in r){var o=a[s],n=(s,r[s]);"tags"===s?arraysIdentical(o,n)||(i=!0):o!==n&&(i=!0)}i&&(console.log("Updating meta data for",t),(p?handlePopupSaveEdits:handleMemorySaveEdits)(t))},cutAuthors=(e,t,r)=>{void 0===t&&(t=140),void 0===r&&(r=", ");let a="";var i,o,n=e.split(" and "),s=n[n.length-1];for([i,o]of n.entries()){if(!(5+a.length+r.length+o.length+s.length... "+s;break}a?a+=", "+o:a=o}return a},sendMessageToBackground=e=>new Promise(t=>{chrome.runtime.sendMessage(e,e=>{t(e)})}),getStoredFiles=()=>new Promise(t=>{chrome.downloads.search({filenameRegex:"(PaperMemoryStore/)?.*.pdf"},e=>t(e.filter(e=>e.exists&&"complete"===e.state&&!e.filename.toLowerCase().includes("readme.txt"))))}),noParamUrl=e=>e.split("?")[0].split("#")[0],urlToWebsiteId=e=>{var t;return e.split("/").last().includes("#")&&(t=e.split("#").length-1,e=e.split("#").slice(0,t).join("#")),miniHash(e.replace("https://","").replace("http://","").replace("www.",""))},silentPromiseTimeout=(e,r=5e3)=>{let a;return Promise.race([e,new Promise((e,t)=>a=setTimeout(e,r))]).finally(()=>clearTimeout(a))},shouldWarn=async(e,t=()=>{})=>t(!1),spaceCamelCase=e=>e.replace(/([A-Z](?=[a-z]+)|[A-Z]+(?![a-z]))/g," $1").trim(),toSingleSpace=e=>e.replace(/\s\s+/g," "),dedent=e=>(""+e).replace(/(\n)\s+/g,"$1"),arxivIdFromPaperID=e=>e.split("-").last().replace("_","/"),cleanStr=e=>e.replace(/[^a-zA-Z0-9 ]/g,""),arxivIdFromURL=e=>e.includes("scirate.com/arxiv/")?e.split("scirate.com/arxiv/")[1].match(/\d+\.\d+/)[0]:e.includes("arxiv-vanity.com/papers/")?e.split("arxiv-vanity.com/papers/")[1].match(/\d+\.\d+/)[0]:e.includes("ar5iv.labs.arxiv.org/html/")?e.split("ar5iv.labs.arxiv.org/html/")[1].match(/\d+\.\d+/)[0]:e.includes("huggingface.co/papers/")?e.split("huggingface.co/papers/")[1].match(/\d+\.\d+/)[0]:noParamUrl(e).replace("/abs/","/pdf/").split("/pdf/")[1].replace(".pdf","").split("v")[0].replace("/","_"),getGist=("undefined"!=typeof module&&null!=module.exports&&((dummyModule=module).exports={log:log,info:info,logError:logError,logOk:logOk,debug:debug,warn:warn,getDisplayId:getDisplayId,isObject:isObject,isPdfUrl:isPdfUrl,delay:delay,cleanPapers:cleanPapers,firstNonStopLowercase:firstNonStopLowercase,fallbackCopyTextToClipboard:fallbackCopyTextToClipboard,copyTextToClipboard:copyTextToClipboard,parseUrl:parseUrl,downloadTextFile:downloadTextFile,eventId:eventId,downloadFile:downloadFile,hashCode:hashCode,parseCVFUrl:parseCVFUrl,cleanBiorxivURL:cleanBiorxivURL,textareaFocusEnd:textareaFocusEnd,tablerSvg:tablerSvg,stringifyError:stringifyError,arraysIdentical:arraysIdentical,parseTags:parseTags,getPaperEdits:getPaperEdits,setFormChangeListener:setFormChangeListener,monitorPaperEdits:monitorPaperEdits,cutAuthors:cutAuthors,sendMessageToBackground:sendMessageToBackground,getStoredFiles:getStoredFiles,miniHash:miniHash,noParamUrl:noParamUrl,silentPromiseTimeout:silentPromiseTimeout,shouldWarn:shouldWarn,spaceCamelCase:spaceCamelCase,toSingleSpace:toSingleSpace}),async(t,r=!0)=>{try{if(!(t=t||await getStorage("syncPAT")))return setStorage("syncState",!1),{ok:!1,payload:"noPAT"};const n=await getStorage("syncTest")?"TestsPaperMemorySync":"DO_NO_EDIT__PaperMemorySyncV2.json",s="Automated PaperMemory sync Gist. Do not edit manually.";var a,i=octokitRequest.defaults({headers:{authorization:"token "+t,"X-GitHub-Api-Version":"2022-11-28"}});let e=(await i("GET /gists")).data?.find(e=>e.description===s&&e.files[n]);e||(info("No Gist found. Creating new one..."),a=await i("POST /gists",{description:s,files:{[n]:{content:JSON.stringify(await getStorage("papers"))}},public:!1}),e=a.data),file=e.files[n];var o=e.id;return r&&await setStorage("syncPAT",t),{ok:!0,payload:{file:file,pat:t,gistId:o}}}catch(e){return console.log(e),warn("Because of the error ^ syncing is now disabled."),{ok:!1,payload:"wrongPAT",error:e}}}),getDataForGistFile=async e=>{var t,r;return e.filename.endsWith(".json")?({data:t,status:r}=await fetchJSON(e.raw_url),t):fetchText(e.raw_url)},updateGist=async(e,t,r)=>{var a=await getStorage("syncPAT");return a?octokitRequest.defaults({headers:{authorization:"token "+a,"X-GitHub-Api-Version":"2022-11-28"}})("PATCH /gists/"+r,{gist_id:r,files:{[e.filename]:{content:JSON.stringify(t,null,"")}}}):(info("(updateGist) No PAT found. Syncing is now disabled."),setStorage("syncState",!1),{ok:!1,payload:"noPAT"})},pushToRemote=async()=>sendMessageToBackground({type:"writeSync"}),pullFromRemote=async(e,t)=>{var r=Date.now(),a=await sendMessageToBackground({type:"pullSync"});return consoleHeader("PaperMemory Pull "+String.fromCodePoint("0x1F504")),log("Remote Papers pulled: ",a),a&&(await initState({isContentScript:t,papers:a??e,print:!1}),t=(Date.now()-r)/1e3,info(`Successfully pulled from Github (${t}s).`),await setStorage("papers",global.state.papers)),console.groupEnd(),a},shouldSync=async()=>!!await getStorage("syncState"),initSyncAndState=async({papers:e=null,isContentScript:t=!1,forceInit:r=!1,stateIsReady:a=()=>{},remoteIsReady:i=()=>{}}={})=>{global.state.dataVersion&&!r||await initState({papers:e,isContentScript:t}),a(),await shouldSync()&&(t||startSyncLoader(),await pullFromRemote(e,t)?t||(r=global.state.sortedPapers.length,setPlaceholder("memory-search",`Search ${r} entries...`),global.state.memoryIsOpen||window.location.href.includes("options.html")||await makeMemoryHTML(),successSyncLoader()):t||errorSyncLoader()),i()},startSyncLoader=async()=>{showId("sync-popup-feedback"),hideId("sync-popup-error"),hideId("sync-popup-synced"),showId("sync-popup-syncing","flex")},successSyncLoader=async()=>{showId("sync-popup-feedback"),hideId("sync-popup-syncing"),hideId("sync-popup-error"),showId("sync-popup-synced"),setTimeout(()=>{hideId("sync-popup-feedback")},2e3)},errorSyncLoader=async()=>{showId("sync-popup-feedback"),hideId("sync-popup-syncing"),hideId("sync-popup-synced"),showId("sync-popup-error"),setTimeout(()=>{hideId("sync-popup-feedback")},2e3)},migrateData=async(r,a,e=!0)=>{if(void 0===r)return chrome.storage.local.set({papers:{__dataVersion:a}}),{papers:{__dataVersion:a},success:!0};var i,o,n,s,l,p,c,d=r.__dataVersion||-1,u=[];let h={...r};try{if(1e4<=d)return log("No migration needed"),{papers:r,success:!0};e&&backupData({...r}),delete r.__dataVersion;let t={};for(const m in r){if(t[m]=[],d<5&&(r[m].hasOwnProperty("bibtex")||(r[m].bibtex="",t[m].push("(m5) bibtex default")),r[m].pdfLink.endsWith(".pdf")||(r[m].pdfLink=r[m].pdfLink+".pdf",t[m].push("(m5) pdfLink to .pdf")),r[m].codeLink||(r[m].codeLink="",t[m].push("(m5) codeLink default")),r[m].source||(r[m].id.includes("NeurIPS")?(r[m].source="neurips",t[m].push("(m5) NeurIPS to neurips")):(r[m].source="arxiv",t[m].push("(m5) source default is arxiv")))),d<208&&("arxiv"!==r[m].source&&r[m].md.includes("https://arxiv.com/abs/")&&(r[m].md=`[${r[m].title}](${r[m].pdfLink})`,t[m].push("(m208) md from title and pdfLink")),"arxiv"!==r[m].source&&r[m].pdfLink.includes("arxiv.org/pdf/")&&(r[m].source="arxiv",t[m].push("(m208) set arxiv source from pdfLink")),m.match(/^\d/))&&"arxiv"===r[m].source&&(i="Arxiv-"+m,o={...r[m],id:i},r[i]=o,u.push(m),t[m].push("(m208) new arxiv id to Arxiv-")),d<209&&(r[m].hasOwnProperty("favorite")||(r[m].favorite=!1,r[m].favoriteDate="",t[m].push("(m209) favorite defaults"))),d<210&&("arxiv"===r[m].source&&(n=r[m].pdfLink.match(/v\d+\.pdf/gi))&&0{delete r[e],log("Deleting "+e)}),(h={...r}).__dataVersion=a,info("Migration summary:"),Object.keys(t).forEach(e=>{0{log("Migrated papers:"),log(h),log("Data version is now "+a)}),{papers:h,success:!0}}catch(e){return log(`Error migrating data from version ${d} to ${a}:`),log(e),{papers:h,success:!1,error:e}}},logStorage=t=>{chrome.storage.local.get(t,e=>{log(e[t])})},getStorage=async r=>new Promise((t,e)=>{chrome.storage.local.get(r,e=>{t("string"==typeof r?e[r]:e)})}),setStorage=async(r,a,i=()=>{})=>new Promise((e,t)=>{chrome.storage.local.set({[r]:a},()=>{i(),e(!0)})}),deletePaperInStorage=async(e,t)=>{let r=!1;(t=t||(await getStorage("papers")??{})).hasOwnProperty(e)&&(updateDuplicatedUrls(null,e,!0),r=(r=delete global.state.titleHashToIds[miniHash(t[e].title)])&&delete t[e],delete global.state.papers[e]),r?(await setStorage("papers",t),global.state.papersList=Object.values(cleanPapers(global.state.papers)),sortMemory(),log("Successfully deleted paper",e)):log("Error: no deletion")},getTheme=async()=>{return await getStorage("checkDarkMode")?"dark":"light"},backupData=async r=>{chrome.storage.local.get("papersBackup",({papersBackup:e})=>{void 0===e&&(e={});for(const t of Object.keys(e).map(e=>parseInt(e)).sort((e,t)=>e{log("Backed up data with version: "+r.__dataVersion)})})};function dateDiffInDays(e,t){e=Date.UTC(e.getFullYear(),e.getMonth(),e.getDate()),t=Date.UTC(t.getFullYear(),t.getMonth(),t.getDate());return Math.floor((t-e)/864e5)}const weeklyBackup=async()=>{var e=await getStorage("weeklyBackups")??{},t=new Date,r=Object.keys(e).map(e=>new Date(e)).sort((e,t)=>e.getTime()-t.getTime());if(0{let e=!1,t;var r=await getStorage("prefs")??{},a=((e=0===Object.keys(r).length?!0:e)&&(t=await getStorage(global.prefsStorageKeys)??{}),{});for(const i of global.prefsCheckNames)a[i]=(t??r).hasOwnProperty(i)?(t??r)[i]:!(0<=global.prefsCheckDefaultFalse.indexOf(i));return a.checkOfficialRepos&&(setStorage("pwcPrefs",{official:!0}),delete a.checkOfficialRepos,setStorage("prefs",a)),e&&setStorage("prefs",a),a},getManifestDataVersion=()=>{return chrome.runtime.getManifest().version.split(".").map((e,t)=>parseInt(e)*10**(4-2*t)).reduce((e,t)=>e+t)},versionToSemantic=e=>(e=(e-=1e4*(major=parseInt(e/1e4,10)))-100*(minor=parseInt(e/100,10)),`${major}.${minor}.`+e),validatePaper=(t,e=!0)=>{var r,a={addDate:{type:"string",desc:"the paper's date of addition to the Memory",default:e=>(new Date).toJSON(),validation:e=>{try{new Date(e)}catch(e){return`Invalid addDate (could not parse as date: ${e})`}}},author:{type:"string",desc:"` and `-separated authors `${firstName} ${lastName}`",validation:e=>{if(!e)throw Error(`No author: ${e} for paper ${t.id}: +`}return(r.slice(0,-2)+"\n}").replaceAll("\t"," ").replaceAll("--","-")},extractBibtexValue=(e,t)=>{e=bibtexToObject(e);return e.hasOwnProperty(t)?e[t]:""},extractAuthor=e=>extractBibtexValue(e,"author").replaceAll("{","").replaceAll("}","").replaceAll("\\","").split(" and ").map(e=>e.split(", ").reverse().join(" ")).join(" and "),logTrace=("undefined"!=typeof module&&null!=module.exports&&((dummyModule=module).exports={bibtexToObject:bibtexToObject,bibtexToString:bibtexToString,extractBibtexValue:extractBibtexValue,extractAuthor:extractAuthor}),"undefined"!=typeof LOGTRACE&&LOGTRACE),log=(...e)=>{var t;logTrace&&(t=(new Error).stack,e.push("\n\nLog trace:\n"+t.split("\n").slice(2).join("\n")));let r="%c%s ",a=!1,i=!1,o=!1,n=!1,s=!1;"[info]"===e[0]?(a=!0,e=e.slice(1)):"[warn]"===e[0]?(i=!0,e=e.slice(1)):"[error]"===e[0]?(o=!0,e=e.slice(1)):"[ok]"===e[0]?(s=!0,e=e.slice(1)):"[debug]"===e[0]&&(n=!0,e=e.slice(1)),e.forEach(e=>{switch(typeof e){case"bigint":case"number":r+="%d ";break;case"string":r+="%s ";break;default:r+="%o "}}),console.log(r,"color: "+(a?"#8BB4F7; font-weight:bold;":i?"#f3bd1e; font-weight:bold;":o?"#FF4F54; font-weight:bold;":s?"#23F62B; font-weight:bold;":n?"#BA357E; font-weight:bold;":"tan"),"[PM]",...e)},info=(...e)=>log("[info]",...e),warn=(...e)=>log("[warn]",...e),debug=(...e)=>log("[debug]",...e),logOk=(...e)=>log("[ok]",...e),logError=(...e)=>log("[error]",...e),consoleHeader=e=>console.groupCollapsed("%c"+e,global.consolHeaderStyle),getDisplayId=e=>{var t=e;return(e=e.split("_")[0].split(".")[0]).startsWith("OR-")||(e=e.split("-").slice(0,2).join("-")),global.state.papers.hasOwnProperty(t)&&("nature"===(t=global.state.papers[t]).source&&(t.note.match(/^Published\ @.+\(\d+\)$/)&&(e+="-"+t.note.split("@")[1].split("(")[0].trim().split(" ").map(e=>e[0].toUpperCase()).join("")),e.includes(t.year+"")||(e+="-"+t.year)),"acs"!==t.source||e.includes(t.year+"")||(e+="-"+t.year),"iop"!==t.source||e.includes(t.year+"")||(e+="-"+t.year)),e},isObject=e=>"object"==typeof e&&!Array.isArray(e)&&null!==e,isPdfUrl=e=>e.endsWith(".pdf")||e.endsWith("/pdf")||e.includes("openreview.net/pdf")||e.match(/\/e?pdf\//g)||e.includes("ieee.org/stamp/stamp.jsp?tp=&arnumber=")||e.includes("articlepdf");function delay(t,r){let a=0;return function(...e){clearTimeout(a),a=setTimeout(t.bind(this,...e),r||0)}}const cleanPapers=e=>{e={...e};return delete e.__dataVersion,e},firstNonStopLowercase=e=>{var e=e.toLowerCase().split(" ").map(miniHash),t=e.filter(e=>!global.englishStopWords.has(e));return(0e.toLowerCase().replace(/\W/g,""),fallbackCopyTextToClipboard=e=>{var t=document.createElement("textarea");t.value=e,t.style.top="0",t.style.left="0",t.style.position="fixed",document.body.appendChild(t),t.focus(),t.select();try{var r=document.execCommand("copy")?"successful":"unsuccessful";log("Fallback: Copying text command was "+r)}catch(e){console.error("Fallback: Oops, unable to copy",e)}document.body.removeChild(t)},copyTextToClipboard=e=>{navigator.clipboard?navigator.clipboard.writeText(e).then(()=>{log("Async: Copying to clipboard was successful!")},e=>{console.error("Async: Could not copy text: ",e)}):fallbackCopyTextToClipboard(e)};async function pasteRich(t,r){var e,a;"undefined"!=typeof ClipboardItem?(e=new Blob([t],{type:"text/html"}),a=new Blob([r],{type:"text/plain"}),e=new ClipboardItem({"text/html":e,"text/plain":a}),await navigator.clipboard.write([e])):(a=e=>{e.clipboardData.setData("text/html",t),e.clipboardData.setData("text/plain",r),e.preventDefault()},document.addEventListener("copy",a),document.execCommand("copy"),document.removeEventListener("copy",a))}const copyHyperLinkToClipboard=(e,t)=>{pasteRich(`${t}`,t+" "+e)},padRight=(e,t,r)=>(void 0===r&&(r=" "),e.length{var t=document.createElement("a");return t.href=e,t},downloadTextFile=(e,t,r)=>{var a=document.createElement("a");"text/plain"===r?(e=e.replace(/\\n/g,"%0D%0A").replace(/"/g,""),a.download=t,a.href="data:text/plain,"+e):(e=new Blob([e],{type:r}),a.href=URL.createObjectURL(e),a.download=t),a.click()},eventId=e=>e.target.closest(".memory-container").id.split("--")[1],downloadFile=(e,t)=>{var r;window.ActiveXObject?window.ActiveXObject&&document.execCommand&&((r=window.open(e,"_blank")).document.close(),r.document.execCommand("SaveAs",!0,t||e),r.close()):((r=document.createElement("a")).href=e,r.target="_blank",e=e.substring(e.lastIndexOf("/")+1),r.download=t||e,navigator.userAgent.toLowerCase().match(/(ipad|iphone|safari)/)&&navigator.userAgent.search("Chrome")<0?document.location=r.href:(t=new MouseEvent("click",{view:window,bubbles:!0,cancelable:!1}),r.dispatchEvent(t),(window.URL||window.webkitURL).revokeObjectURL(r.href)))},hashCode=e=>e.split("").reduce((e,t)=>(e=(e<<5)-e+t.charCodeAt(0))&e,0),parseCVFUrl=e=>{var t=e.replace("https://openaccess.thecvf.com/content","").slice(1).split("/")[0].split("_");let r,a;a=1===t.length?(r=t[0].slice(0,-4),t[0].slice(-4)):(r=t[0].toUpperCase(),t[1]);t=e.split("/").last().split(".")[0],e=(hashCode(t)+"").replace("-","").slice(0,8),t=`${r}-${a}_`+e;return{conf:r,year:a,id:t}},cleanBiorxivURL=e=>e=(e=e.replace(".full.pdf","")).match(/\d$/)?e:e.split(".").slice(0,-1).join("."),textareaFocusEnd=e=>{setTimeout(()=>{e.selectionStart=e.selectionEnd=1e4},0)},tablerSvg=(e,t,r)=>{switch(t=(t=void 0===t?"":t)&&`id="${t}"`,r=(r=(r=void 0===r?[]:r).filter(e=>e))&&`class="${r.join(" ")}"`,e){case"adjustments":return``;case"circle-x":return``;case"star":return``;case"writing":return``;case"file-symlink":return``;case"link":return``;case"clipboard-list":return``;case"archive":return``;case"external-link":return``;case"file-download":return``;case"cirlce-x":return``;case"settings":return``;case"messages":return``;case"vanity":return``;case"ar5iv":return``;case"vocabulary":return``;case"database-export":return``;case"eyeglass":return``;case"markdown":return``;case"math-function":return``;case"device-desktop-code":return``;case"info-square-rounded":return``;case"huggingface":return``;default:return""}},stringifyError=e=>{const t=chrome.runtime.id;return e.stack.split("\n").map(e=>e.split(" ").map(e=>e.split(t).last()).join(" ")).join("
")},arraysIdentical=(e,t)=>{var r=e.length;if(r!=t.length)return!1;for(;r--;)if(e[r]!==t[r])return!1;return!0},parseTags=e=>{e=Array.from(e.selectedOptions,e=>e.value.trim()).filter(e=>e);return e.sort(),e},getPaperEdits=(e,t)=>{let r,a,i,o;return o=t?(r=val("popup-form-note-textarea--"+e),i=val(document.getElementById("popup-form-note--"+e).querySelector(".form-code-input")),a=parseTags(findEl("popup-item-tags--"+e)),findEl("checkFavorite--"+e).checked):(r=val(findEl(e,"form-note-textarea")),i=val(findEl(e,"form-code-input")),a=parseTags(findEl(e,"memory-item-tags")),hasClass("memory-container--"+e,"favorite")),{note:r,tags:a,codeLink:i,favorite:o}},setFormChangeListener=(e,t)=>{let r,a,i,o;t?(r="#popup-item-tags--"+e.replace(".","\\."),i="popup-form-codeLink--"+e,a="popup-form-note-textarea--"+e,o="checkFavorite--"+e,$(r).on("change",delay(monitorPaperEdits(e,t),300)),addListener(i,"keyup",delay(monitorPaperEdits(e,t),300)),addListener(a,"keyup",delay(monitorPaperEdits(e,t),300)),addListener(o,"change",delay(monitorPaperEdits(e,t),300))):(r=".memory-item-tags",i=".form-code-input",a=".form-note-textarea",addEventToClass(i,"keyup",delay(monitorPaperEdits(void 0,t),300)),addEventToClass(a,"keyup",delay(monitorPaperEdits(void 0,t),300)))},monitorPaperEdits=(l,p)=>e=>{let t;t=void 0===l?eventId(e):l;var r=getPaperEdits(t,p),a=global.state.papers[t];let i=!1;for(const s in r){var o=a[s],n=(s,r[s]);"tags"===s?arraysIdentical(o,n)||(i=!0):o!==n&&(i=!0)}i&&(console.log("Updating meta data for",t),(p?handlePopupSaveEdits:handleMemorySaveEdits)(t))},cutAuthors=(e,t,r)=>{void 0===t&&(t=140),void 0===r&&(r=", ");let a="";var i,o,n=e.split(" and "),s=n[n.length-1];for([i,o]of n.entries()){if(!(5+a.length+r.length+o.length+s.length... "+s;break}a?a+=", "+o:a=o}return a},sendMessageToBackground=e=>new Promise(t=>{chrome.runtime.sendMessage(e,e=>{t(e)})}),getStoredFiles=()=>new Promise(t=>{chrome.downloads.search({filenameRegex:"(PaperMemoryStore/)?.*.pdf"},e=>t(e.filter(e=>e.exists&&"complete"===e.state&&!e.filename.toLowerCase().includes("readme.txt"))))}),noParamUrl=e=>e.split("?")[0].split("#")[0],urlToWebsiteId=e=>{var t;return e.split("/").last().includes("#")&&(t=e.split("#").length-1,e=e.split("#").slice(0,t).join("#")),miniHash(e.replace("https://","").replace("http://","").replace("www.",""))},silentPromiseTimeout=(e,r=5e3)=>{let a;return Promise.race([e,new Promise((e,t)=>a=setTimeout(e,r))]).finally(()=>clearTimeout(a))},shouldWarn=async(e,t=()=>{})=>t(!1),spaceCamelCase=e=>e.replace(/([A-Z](?=[a-z]+)|[A-Z]+(?![a-z]))/g," $1").trim(),toSingleSpace=e=>e.replace(/\s\s+/g," "),dedent=e=>(""+e).replace(/(\n)\s+/g,"$1"),arxivIdFromPaperID=e=>e.split("-").last().replace("_","/"),cleanStr=e=>e.replace(/[^a-zA-Z0-9 ]/g,""),arxivIdFromURL=e=>e.includes("scirate.com/arxiv/")?e.split("scirate.com/arxiv/")[1].match(/\d+\.\d+/)[0]:e.includes("arxiv-vanity.com/papers/")?e.split("arxiv-vanity.com/papers/")[1].match(/\d+\.\d+/)[0]:e.includes("ar5iv.labs.arxiv.org/html/")?e.split("ar5iv.labs.arxiv.org/html/")[1].match(/\d+\.\d+/)[0]:e.includes("huggingface.co/papers/")?e.split("huggingface.co/papers/")[1].match(/\d+\.\d+/)[0]:noParamUrl(e).replace("/abs/","/pdf/").split("/pdf/")[1].replace(".pdf","").split("v")[0].replace("/","_"),getGist=("undefined"!=typeof module&&null!=module.exports&&((dummyModule=module).exports={log:log,info:info,logError:logError,logOk:logOk,debug:debug,warn:warn,getDisplayId:getDisplayId,isObject:isObject,isPdfUrl:isPdfUrl,delay:delay,cleanPapers:cleanPapers,firstNonStopLowercase:firstNonStopLowercase,fallbackCopyTextToClipboard:fallbackCopyTextToClipboard,copyTextToClipboard:copyTextToClipboard,parseUrl:parseUrl,downloadTextFile:downloadTextFile,eventId:eventId,downloadFile:downloadFile,hashCode:hashCode,parseCVFUrl:parseCVFUrl,cleanBiorxivURL:cleanBiorxivURL,textareaFocusEnd:textareaFocusEnd,tablerSvg:tablerSvg,stringifyError:stringifyError,arraysIdentical:arraysIdentical,parseTags:parseTags,getPaperEdits:getPaperEdits,setFormChangeListener:setFormChangeListener,monitorPaperEdits:monitorPaperEdits,cutAuthors:cutAuthors,sendMessageToBackground:sendMessageToBackground,getStoredFiles:getStoredFiles,miniHash:miniHash,noParamUrl:noParamUrl,silentPromiseTimeout:silentPromiseTimeout,shouldWarn:shouldWarn,spaceCamelCase:spaceCamelCase,toSingleSpace:toSingleSpace}),async(t,r=!0)=>{try{if(!(t=t||await getStorage("syncPAT")))return setStorage("syncState",!1),{ok:!1,payload:"noPAT"};const o=await getStorage("syncTest")?"TestsPaperMemorySync":"DO_NO_EDIT__PaperMemorySyncV2.json",n="Automated PaperMemory sync Gist. Do not edit manually.";let e=(await octokitRequest.defaults({headers:{authorization:"token "+t,"X-GitHub-Api-Version":"2022-11-28"}})("GET /gists")).data?.find(e=>e.description===n&&e.files[o]);if(!e){info("No Gist found. Creating new one...");var a=await getStorage("papers");if(!(e=await createGistWithFile({filename:o,pat:t,description:n,content:a})))return{ok:!1,payload:"wrongPAT"}}file=e.files[o];var i=e.id;return r&&await setStorage("syncPAT",t),{ok:!0,payload:{file:file,pat:t,gistId:i}}}catch(e){return console.log(e),warn("Because of the error ^ syncing is now disabled."),setStorage("syncState",!1),{ok:!1,payload:"wrongPAT",error:e}}}),createGistWithFile=async({file:e,pat:t,content:r,description:a="Automated PaperMemory sync Gist. Do not edit manually."})=>{if("string"!=typeof r&&(r=JSON.stringify(r,null,"")),"string"==typeof e&&(e={filename:e}),t=t||await getStorage("syncPAT"))return(await octokitRequest.defaults({headers:{authorization:"token "+t,"X-GitHub-Api-Version":"2022-11-28"}})("POST /gists",{description:a,files:{[filename]:{content:r}},public:!1})).data;warn("(createGistWithFile) No PAT found. Aborting and disabling sync."),setStorage("syncState",!1)},getDataForGistFile=async({file:e,filename:t,url:r})=>{if(void 0===e){if(void 0===r)throw new Error("No file or url provided.");if(void 0===t)throw new Error("No filename provided.");e={filename:e}}else void 0===t&&void 0===r||warn("`file` argument has precedence over `filename` or `url`.");var a;return e.filename.endsWith(".json")?({data:t,status:a}=await fetchJSON(e.raw_url),t):fetchText(e.raw_url)},updateGistFile=async({file:e,content:t,gistId:r,pat:a})=>{return(a=a||await getStorage("syncPAT"))?("string"!=typeof t&&(t=JSON.stringify(t,null,"")),"string"==typeof e&&(e={filename:e}),octokitRequest.defaults({headers:{authorization:"token "+a,"X-GitHub-Api-Version":"2022-11-28"}})("PATCH /gists/"+r,{gist_id:r,files:{[e.filename]:{content:t}}})):(warn("(updateGistFile) No PAT found. Aborting and disabling sync."),setStorage("syncState",!1),{ok:!1,payload:"noPAT"})},pushToRemote=async()=>sendMessageToBackground({type:"writeSync"}),pullFromRemote=async(e,t)=>{var r=Date.now(),a=await sendMessageToBackground({type:"pullSync"});return consoleHeader("PaperMemory Pull "+String.fromCodePoint("0x1F504")),log("Remote Papers pulled: ",a),a&&(await initState({isContentScript:t,papers:a??e,print:!1}),t=(Date.now()-r)/1e3,info(`Successfully pulled from Github (${t}s).`),await setStorage("papers",global.state.papers)),console.groupEnd(),a},shouldSync=async()=>!!await getStorage("syncState"),initSyncAndState=async({papers:e=null,isContentScript:t=!1,forceInit:r=!1,stateIsReady:a=()=>{},remoteIsReady:i=()=>{}}={})=>{global.state.dataVersion&&!r||await initState({papers:e,isContentScript:t}),a(),await shouldSync()&&(t||startSyncLoader(),await pullFromRemote(e,t)?t||(r=global.state.sortedPapers.length,setPlaceholder("memory-search",`Search ${r} entries...`),global.state.memoryIsOpen||window.location.href.includes("options.html")||await makeMemoryHTML(),successSyncLoader()):t||errorSyncLoader()),i()},startSyncLoader=async()=>{showId("sync-popup-feedback"),hideId("sync-popup-error"),hideId("sync-popup-synced"),showId("sync-popup-syncing","flex")},successSyncLoader=async()=>{showId("sync-popup-feedback"),hideId("sync-popup-syncing"),hideId("sync-popup-error"),showId("sync-popup-synced"),setTimeout(()=>{hideId("sync-popup-feedback")},2e3)},errorSyncLoader=async()=>{showId("sync-popup-feedback"),hideId("sync-popup-syncing"),hideId("sync-popup-synced"),showId("sync-popup-error"),setTimeout(()=>{hideId("sync-popup-feedback")},2e3)},migrateData=async(r,a,e=!0)=>{if(void 0===r)return chrome.storage.local.set({papers:{__dataVersion:a}}),{papers:{__dataVersion:a},success:!0};var i,o,n,s,l,p,c,d=r.__dataVersion||-1,u=[];let h={...r};try{if(1e4<=d)return log("No migration needed"),{papers:r,success:!0};e&&backupData({...r}),delete r.__dataVersion;let t={};for(const m in r){if(t[m]=[],d<5&&(r[m].hasOwnProperty("bibtex")||(r[m].bibtex="",t[m].push("(m5) bibtex default")),r[m].pdfLink.endsWith(".pdf")||(r[m].pdfLink=r[m].pdfLink+".pdf",t[m].push("(m5) pdfLink to .pdf")),r[m].codeLink||(r[m].codeLink="",t[m].push("(m5) codeLink default")),r[m].source||(r[m].id.includes("NeurIPS")?(r[m].source="neurips",t[m].push("(m5) NeurIPS to neurips")):(r[m].source="arxiv",t[m].push("(m5) source default is arxiv")))),d<208&&("arxiv"!==r[m].source&&r[m].md.includes("https://arxiv.com/abs/")&&(r[m].md=`[${r[m].title}](${r[m].pdfLink})`,t[m].push("(m208) md from title and pdfLink")),"arxiv"!==r[m].source&&r[m].pdfLink.includes("arxiv.org/pdf/")&&(r[m].source="arxiv",t[m].push("(m208) set arxiv source from pdfLink")),m.match(/^\d/))&&"arxiv"===r[m].source&&(i="Arxiv-"+m,o={...r[m],id:i},r[i]=o,u.push(m),t[m].push("(m208) new arxiv id to Arxiv-")),d<209&&(r[m].hasOwnProperty("favorite")||(r[m].favorite=!1,r[m].favoriteDate="",t[m].push("(m209) favorite defaults"))),d<210&&("arxiv"===r[m].source&&(n=r[m].pdfLink.match(/v\d+\.pdf/gi))&&0{delete r[e],log("Deleting "+e)}),(h={...r}).__dataVersion=a,info("Migration summary:"),Object.keys(t).forEach(e=>{0{log("Migrated papers:"),log(h),log("Data version is now "+a)}),{papers:h,success:!0}}catch(e){return log(`Error migrating data from version ${d} to ${a}:`),log(e),{papers:h,success:!1,error:e}}},logStorage=t=>{chrome.storage.local.get(t,e=>{log(e[t])})},getStorage=async r=>new Promise((t,e)=>{chrome.storage.local.get(r,e=>{t("string"==typeof r?e[r]:e)})}),setStorage=async(r,a,i=()=>{})=>new Promise((e,t)=>{chrome.storage.local.set({[r]:a},()=>{i(),e(!0)})}),deletePaperInStorage=async(e,t)=>{let r=!1;(t=t||(await getStorage("papers")??{})).hasOwnProperty(e)&&(updateDuplicatedUrls(null,e,!0),r=(r=delete global.state.titleHashToIds[miniHash(t[e].title)])&&delete t[e],delete global.state.papers[e]),r?(await setStorage("papers",t),global.state.papersList=Object.values(cleanPapers(global.state.papers)),sortMemory(),log("Successfully deleted paper",e)):log("Error: no deletion")},getTheme=async()=>{return await getStorage("checkDarkMode")?"dark":"light"},backupData=async r=>{chrome.storage.local.get("papersBackup",({papersBackup:e})=>{void 0===e&&(e={});for(const t of Object.keys(e).map(e=>parseInt(e)).sort((e,t)=>e{log("Backed up data with version: "+r.__dataVersion)})})};function dateDiffInDays(e,t){e=Date.UTC(e.getFullYear(),e.getMonth(),e.getDate()),t=Date.UTC(t.getFullYear(),t.getMonth(),t.getDate());return Math.floor((t-e)/864e5)}const weeklyBackup=async()=>{var e=await getStorage("weeklyBackups")??{},t=new Date,r=Object.keys(e).map(e=>new Date(e)).sort((e,t)=>e.getTime()-t.getTime());if(0{let e=!1,t;var r=await getStorage("prefs")??{},a=((e=0===Object.keys(r).length?!0:e)&&(t=await getStorage(global.prefsStorageKeys)??{}),{});for(const i of global.prefsCheckNames)a[i]=(t??r).hasOwnProperty(i)?(t??r)[i]:!(0<=global.prefsCheckDefaultFalse.indexOf(i));return a.checkOfficialRepos&&(setStorage("pwcPrefs",{official:!0}),delete a.checkOfficialRepos,setStorage("prefs",a)),e&&setStorage("prefs",a),a},getManifestDataVersion=()=>{return chrome.runtime.getManifest().version.split(".").map((e,t)=>parseInt(e)*10**(4-2*t)).reduce((e,t)=>e+t)},versionToSemantic=e=>(e=(e-=1e4*(major=parseInt(e/1e4,10)))-100*(minor=parseInt(e/100,10)),`${major}.${minor}.`+e),validatePaper=(t,e=!0)=>{var r,a={addDate:{type:"string",desc:"the paper's date of addition to the Memory",default:e=>(new Date).toJSON(),validation:e=>{try{new Date(e)}catch(e){return`Invalid addDate (could not parse as date: ${e})`}}},author:{type:"string",desc:"` and `-separated authors `${firstName} ${lastName}`",validation:e=>{if(!e)throw Error(`No author: ${e} for paper ${t.id}: ${JSON.stringify(t,null,2)} Fix the json file and try again. `)}},bibtex:{type:"string",desc:"BibTex citation with new lines (`\n`)",validation:t=>{try{bibtexToObject(t)}catch(e){return`Invalid BibTex: ${e} :