From ddfdf4f3c88de068773796404237089c721517ad Mon Sep 17 00:00:00 2001 From: Floris-Jan Willemsen Date: Wed, 31 Jan 2024 21:07:20 +0100 Subject: [PATCH] Fixed an issue with self-signed certificates on macOS --- Scripts/appversion.js | 2 +- bundled_script.js | 2 +- compile-all.sh | 6 ++++-- package-lock.json | 6 +++--- package.json | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Scripts/appversion.js b/Scripts/appversion.js index 1b183aa..e459f00 100644 --- a/Scripts/appversion.js +++ b/Scripts/appversion.js @@ -1 +1 @@ -const appversion = "1.9.1"; +const appversion = "1.9.2"; diff --git a/bundled_script.js b/bundled_script.js index 1d885e7..3a6613f 100644 --- a/bundled_script.js +++ b/bundled_script.js @@ -1 +1 @@ -let comments_watcher_unbind,changes_watcher_unbind,chat_observer,compilation_observer,pdf_change_observer,colorscheme,lib_chartjs_loaded=!1,lib_showdownjs_loaded=!1;const appversion="1.9.1";try{angular}catch(e){if("ReferenceError"==e.name)throw new Error("Angular is not present on this page, Native Overleaf will not be active here")}let notificationCounter=0,lastNotificationResetTimestamp=Date.now(),lastChangeNotificationTimestamp=Date.now();const originalDocumentTitle=document.title;let current_colorscheme_preference;function getLocalDate(){return(new Date).toLocaleDateString("en-CA")}function getTimeInSeconds(){return Math.round((new Date).getTime()/1e3)}async function insertShowdownJS(){return $.ajaxSetup({cache:!0}),$.when($.getScript("https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js")).done((()=>{lib_showdownjs_loaded=!0})).fail((()=>{alert("Unable to dynamically load ShowdownJS, do you have an active internet connection?")}))}function injectDialog(id,innerhtml,insertionselector="body"){const html=`\n ×\n ${innerhtml}\n `;document.querySelector(insertionselector).insertAdjacentHTML("afterend",html);const dialog=document.querySelector(`#${id}`);return dialog.addEventListener("click",(function(event){const rect=dialog.getBoundingClientRect();(event.clientYrect.bottom||event.clientXrect.right)&&dialog.close()})),dialog.querySelector("#closebutton").addEventListener("click",(function(event){dialog.close()})),dialog}function recursiveCheckAndWait(checkFunction,waitTime,numberOfTimesToCheck,multiplyWaitTime=!1,numberOfTimesChecked=0){const checkFunctionResult=checkFunction();return numberOfTimesChecked+=1,0!=checkFunctionResult?checkFunctionResult:!(numberOfTimesToCheck-numberOfTimesChecked<=0)&&new Promise((resolve=>{1==multiplyWaitTime&&(waitTime*=numberOfTimesChecked),setTimeout((()=>{resolve(recursiveCheckAndWait(checkFunction,waitTime,numberOfTimesToCheck,multiplyWaitTime,numberOfTimesChecked))}),waitTime)}))}function waitForElm(selector){return new Promise((resolve=>{if(document.querySelector(selector))return resolve(document.querySelector(selector));const observer=new MutationObserver((mutations=>{document.querySelector(selector)&&(observer.disconnect(),resolve(document.querySelector(selector)))}));observer.observe(document.body,{childList:!0,subtree:!0})}))}window.matchMedia&&(colorscheme=window.matchMedia("(prefers-color-scheme: dark)"),current_colorscheme_preference=colorscheme.matches?"dark":"light"),Storage.prototype.setObject=function(key,value){this.setItem(key,JSON.stringify(value))},Storage.prototype.getObject=function(key,defaultvalue){const value=this.getItem(key);return value&&null!=value?JSON.parse(value):defaultvalue};const deepDiffMapper={VALUE_CREATED:"created",VALUE_UPDATED:"updated",VALUE_DELETED:"deleted",VALUE_UNCHANGED:"---",map:function(obj1,obj2){if(this.isFunction(obj1)||this.isFunction(obj2))throw"Invalid argument. Function given, object expected.";if(this.isValue(obj1)||this.isValue(obj2)){let returnObj={type:this.compareValues(obj1,obj2),original:obj1,updated:obj2};return returnObj.type!=this.VALUE_UNCHANGED?returnObj:void 0}let diff={},foundKeys={};for(let key in obj1){if(this.isFunction(obj1[key]))continue;let value2;void 0!==obj2[key]&&(value2=obj2[key]);let mapValue=this.map(obj1[key],value2);foundKeys[key]=!0,mapValue&&(diff[key]=mapValue)}for(let key in obj2){if(this.isFunction(obj2[key])||void 0!==foundKeys[key])continue;let mapValue=this.map(void 0,obj2[key]);mapValue&&(diff[key]=mapValue)}if(Object.keys(diff).length>0)return diff},compareValues:function(value1,value2){return value1===value2||this.isDate(value1)&&this.isDate(value2)&&value1.getTime()===value2.getTime()?this.VALUE_UNCHANGED:void 0===value1?this.VALUE_CREATED:void 0===value2?this.VALUE_DELETED:this.VALUE_UPDATED},isFunction:function(x){return"[object Function]"===Object.prototype.toString.call(x)},isArray:function(x){return"[object Array]"===Object.prototype.toString.call(x)},isDate:function(x){return"[object Date]"===Object.prototype.toString.call(x)},isObject:function(x){return"[object Object]"===Object.prototype.toString.call(x)},isValue:function(x){return!this.isObject(x)&&!this.isArray(x)}},overallThemes_dark={dark:"Default"},overallThemes_light={light:"Light"},editorThemes_dark={dracula:"Dracula",monokai:"Monokai",cobalt:"Cobalt"},editorThemes_light={textmate:"TextMate",overleaf:"Overleaf",eclipse:"Eclipse"},pdfThemes_dark={dark:"Inverted"},pdfThemes_light={light:"Standard"};let up_notifications_comments=localStorage.getObject("notifications_comment",!0),up_notifications_comment_threads=localStorage.getObject("notifications_comment_response",!0),up_notifications_chats=localStorage.getObject("notifications_chat",!0),up_notifications_tracked_changes_created=localStorage.getObject("notifications_tracked_changes_created",!0),up_notifications_tracked_changes_updated=localStorage.getObject("notifications_tracked_changes_updated",!0),up_notifications_tracked_changes_resolved=localStorage.getObject("notifications_tracked_changes_resolved",!0),up_colormode_switching=localStorage.getObject("colormode_switching",!0),up_overalltheme_dark=localStorage.getObject("overalltheme_dark",Object.keys(overallThemes_dark)[0]),up_overalltheme_light=localStorage.getObject("overalltheme_light",Object.keys(overallThemes_light)[0]),up_editortheme_dark=localStorage.getObject("editortheme_dark",Object.keys(editorThemes_dark)[0]),up_editortheme_light=localStorage.getObject("editortheme_light",Object.keys(editorThemes_light)[0]),up_pdftheme_dark=localStorage.getObject("pdftheme_dark",Object.keys(pdfThemes_dark)[0]),up_pdftheme_light=localStorage.getObject("pdftheme_light",Object.keys(pdfThemes_light)[0]),up_wordcount_tracking=localStorage.getObject("wordcount_tracking",!0),up_wordcount_dailytarget=localStorage.getObject("wordcount_dailytarget",200),up_wordcount_notificationhour=localStorage.getObject("wordcount_notificationhour",18),up_editor_font_family=localStorage.getObject("editor_font_family",""),up_wordcount_dailytarget_min=1,up_wordcount_dailytarget_max=2147483647,up_wordcount_notificationhour_min=-1,up_wordcount_notificationhour_max=23,settings_form;function getFormSelectHTML(category_dicts,category_names){let str="";for(category_index in category_dicts){let endstr="";category_names.length-1>=category_index&&(str+=``,endstr+=""),category_dict=category_dicts[category_index];for(let key in category_dict)str+=`\n`;str+=endstr}return str}function setupPreferencesPane(){const settings_html=`\n

Native Overleaf

\n
\n

Version ${appversion}

\n \n \n
\n
Notifications
\n \n
\n \n
\n \n
\n
\n Suggested Changes
\n Get notifications on tracked changes\n
\n \n
\n \n
\n \n
\n
Customizations
\n \n \n
\n
Dark / Light Mode
\n \n
\n
\n Dark Mode\n
\n \n \n
\n \n \n
\n \n \n
\n
\n Light Mode\n
\n \n \n
\n \n \n
\n \n \n
\n
\n The PDF option "inverted" forces the PDF viewer to inverted colors. This does not change the PDF file itself.\n
\n
\n
Wordcount Tracking
\n

When the document is recompiled, this keeps track of the number of words, allowing you to see your progress.

\n \n
\n \n \n
\n \n \n
\n
\n
Show wordcount graph
\n
\n
\n
Reset wordcount
\n
\n `;$(".editor-menu-icon").first().parent().click((function(){waitForElm("#left-menu").then((elm=>{document.querySelector("#left-menu")?waitForElm(".settings").then((elm=>{console.log(elm.querySelector(".settings")),document.querySelector(".settings")?(document.querySelector("#left-menu").getElementsByTagName("form")[0].insertAdjacentHTML("afterend",settings_html),settings_form=document.querySelector("#native-overleaf-settings"),settings_form.querySelector("#notifications_chat").checked=up_notifications_chats,settings_form.querySelector("#notifications_comment").checked=up_notifications_comments,settings_form.querySelector("#notifications_comment_response").checked=up_notifications_comment_threads,settings_form.querySelector("#notifications_tracked_changes_created").checked=up_notifications_tracked_changes_created,settings_form.querySelector("#notifications_tracked_changes_updated").checked=up_notifications_tracked_changes_updated,settings_form.querySelector("#notifications_tracked_changes_resolved").checked=up_notifications_tracked_changes_resolved,settings_form.querySelector("#colormode_switching").checked=up_colormode_switching,settings_form.querySelector("#overalltheme_dark").value=up_overalltheme_dark,settings_form.querySelector("#overalltheme_light").value=up_overalltheme_light,settings_form.querySelector("#editortheme_dark").value=up_editortheme_dark,settings_form.querySelector("#editortheme_light").value=up_editortheme_light,settings_form.querySelector("#pdftheme_dark").value=up_pdftheme_dark,settings_form.querySelector("#pdftheme_light").value=up_pdftheme_light,settings_form.querySelector("#wordcount_tracking").checked=up_wordcount_tracking,settings_form.querySelector("#wordcount_dailytarget").value=up_wordcount_dailytarget,settings_form.querySelector("#wordcount_notificationhour").value=up_wordcount_notificationhour,settings_form.querySelector("#editor_font_family").value=up_editor_font_family,settings_form.querySelector("#overalltheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#overalltheme_light").disabled=!up_colormode_switching,settings_form.querySelector("#editortheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#editortheme_light").disabled=!up_colormode_switching,settings_form.querySelector("#pdftheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#pdftheme_light").disabled=!up_colormode_switching,settings_form.querySelector("#wordcount_dailytarget").disabled=!up_wordcount_tracking,settings_form.querySelector("#wordcount_notificationhour").disabled=!up_wordcount_tracking,settings_form.addEventListener("change",(function(){for(let id_key in settings_handler)settings_handler[id_key](id_key,settings_form.querySelector(`#${id_key}`))})),document.getElementById("button_show_wordcount_graph").addEventListener("click",(()=>{showWordCountChart()})),document.getElementById("button_reset_wordcount").addEventListener("click",(()=>{let text_number_of_days="";void 0!==wordcounts&&void 0!==wordcounts[this.project_id]&&(text_number_of_days=`You currently have ${Object.keys(wordcounts[this.project_id]).length} tracked days.`);confirm(`Are you sure you want to remove all tracked wordcount history?\n ${text_number_of_days}`)&&(resetWordCounts(),setupWordCount())}))):console.log("No element '.settings'")})):console.log("No element '#left-menu'")}))}))}const settings_handler={notifications_chat:set_notifications_chat,notifications_comment:set_notifications_comment,notifications_comment_response:set_notifications_comment_response,notifications_tracked_changes_created:set_notifications_tracked_changes_created,notifications_tracked_changes_updated:set_notifications_tracked_changes_updated,notifications_tracked_changes_resolved:set_notifications_tracked_changes_resolved,colormode_switching:set_colormode_switching,overalltheme_dark:set_overalltheme_dark,overalltheme_light:set_overalltheme_light,editortheme_dark:set_editortheme_dark,editortheme_light:set_editortheme_light,pdftheme_dark:set_pdftheme_dark,pdftheme_light:set_pdftheme_light,wordcount_tracking:set_wordcount_tracking,wordcount_dailytarget:set_wordcount_dailytarget,wordcount_notificationhour:set_wordcount_notificationhour,editor_font_family:set_editor_font_family};function set_notifications_tracked_changes_created(key,value){value.checked!=up_notifications_tracked_changes_created&&(up_notifications_tracked_changes_created=value.checked,localStorage.setObject(key,value.checked),1==up_notifications_tracked_changes_created&&1==notificationsRequiresSetup()?setupNotifications():1==notificationsRequiresDestruction()&&destructWordCount())}function set_notifications_tracked_changes_updated(key,value){value.checked!=up_notifications_tracked_changes_updated&&(up_notifications_tracked_changes_updated=value.checked,localStorage.setObject(key,value.checked),1==up_notifications_tracked_changes_updated&&1==notificationsRequiresSetup()?setupNotifications():1==notificationsRequiresDestruction()&&destructWordCount())}function set_notifications_tracked_changes_resolved(key,value){value.checked!=up_notifications_tracked_changes_resolved&&(up_notifications_tracked_changes_resolved=value.checked,localStorage.setObject(key,value.checked),1==up_notifications_tracked_changes_resolved&&1==notificationsRequiresSetup()?setupNotifications():1==notificationsRequiresDestruction()&&destructWordCount())}function set_wordcount_tracking(key,value){value.checked!=up_wordcount_tracking&&(up_wordcount_tracking=value.checked,localStorage.setObject(key,value.checked),settings_form.querySelector("#wordcount_dailytarget").disabled=!up_wordcount_tracking,settings_form.querySelector("#wordcount_notificationhour").disabled=!up_wordcount_tracking,1==up_wordcount_tracking?setupWordCount():destructWordCount())}function set_wordcount_dailytarget(key,value){value.valueup_wordcount_dailytarget_max?(alert(`You set ${value.value}, but wordcount daily target must be between ${up_wordcount_dailytarget_min} and ${up_wordcount_dailytarget_max}`),settings_form.querySelector(`#${key}`).value=up_wordcount_dailytarget):value.value!=up_wordcount_dailytarget&&(up_wordcount_dailytarget=value.value,localStorage.setObject(key,value.value),setHasBeenNotified(!1))}function set_wordcount_notificationhour(key,value){(value.valueup_wordcount_notificationhour_max)&&(alert(`You set ${value.value}, but wordcount notification hour must be between ${up_wordcount_notificationhour_min} and ${up_wordcount_notificationhour_max}`),settings_form.querySelector(`#${key}`).value=up_wordcount_notificationhour),value.value!=up_wordcount_notificationhour&&(up_wordcount_notificationhour=value.value,localStorage.setObject(key,value.value),setHasBeenNotified(!1))}function set_notifications_chat(key,value){value!=up_notifications_chats&&(up_notifications_chats=value.checked,localStorage.setObject(key,value.checked),destructNotifications(),setupNotifications())}function set_notifications_comment(key,value){value.checked!=up_notifications_comments&&(up_notifications_comments=value.checked,localStorage.setObject(key,value.checked),destructNotifications(),setupNotifications())}function set_notifications_comment_response(key,value){value.checked!=up_notifications_comment_threads&&(up_notifications_comment_threads=value.checked,localStorage.setObject(key,value.checked),destructNotifications(),setupNotifications())}function set_editor_font_family(key,value){value.value!=up_editor_font_family&&(up_editor_font_family=value.value,localStorage.setObject(key,value.value),setCSS())}function set_colormode_switching(key,value){value.checked!=up_colormode_switching&&(up_colormode_switching=value.checked,localStorage.setObject(key,value.checked),settings_form.querySelector("#overalltheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#overalltheme_light").disabled=!up_colormode_switching,settings_form.querySelector("#editortheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#editortheme_light").disabled=!up_colormode_switching,settings_form.querySelector("#pdftheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#pdftheme_light").disabled=!up_colormode_switching,1==up_colormode_switching?setupColormode():destructColormode())}function themesetter(user_preference_variable_name,key,value){const user_preference_variable=eval(user_preference_variable_name);localStorage.setObject(key,value.value),value.value!=user_preference_variable&&(eval(`${user_preference_variable_name} = "${value.value}"`),switchColorMode())}function set_overalltheme_dark(key,value){themesetter("up_overalltheme_dark",key,value)}function set_overalltheme_light(key,value){themesetter("up_overalltheme_light",key,value)}function set_editortheme_dark(key,value){themesetter("up_editortheme_dark",key,value)}function set_editortheme_light(key,value){themesetter("up_editortheme_light",key,value)}function set_pdftheme_dark(key,value){themesetter("up_pdftheme_dark",key,value)}function set_pdftheme_light(key,value){themesetter("up_pdftheme_light",key,value)}const overallThemeToOverleaf={dark:"",light:"light-"};function switchColorModePDF(){current_pdfcolor="dark"==current_colorscheme_preference?up_pdftheme_dark:up_pdftheme_light,"dark"==current_pdfcolor?$(".pdf-viewer .pdfjs-viewer .page").addClass("conditional-invert-colors"):"light"==current_pdfcolor?$(".pdf-viewer .pdfjs-viewer .page").removeClass("conditional-invert-colors"):console.error(`current pdfcolor preference ${current_pdfcolor} is not a valid value`)}function switchColorMode(){console.log("switchColormode");let scope=angular.element("[ng-controller=IdeController]").scope();console.log(scope),scope&&scope.settings?scope.$applyAsync((function(){"dark"==current_colorscheme_preference?(scope.settings.overallTheme=overallThemeToOverleaf[up_overalltheme_dark],scope.darkTheme=!up_overalltheme_light,scope.settings.editorTheme=up_editortheme_dark):"light"==current_colorscheme_preference?(scope.settings.overallTheme=overallThemeToOverleaf[up_overalltheme_light],scope.darkTheme=!up_overalltheme_light,scope.settings.editorTheme=up_editortheme_light):console.error(`current colorscheme preference ${current_colorscheme_preference} is not a valid value`),console.log("Applied scope change"),console.log(angular.element("[ng-controller=IdeController]").scope()),switchColorModePDF()})):(console.log("No scope settings:"),console.log(scope))}function autoChangeColorMode(event){current_colorscheme_preference=event.matches?"dark":"light",switchColorMode()}async function setupColormode(){if(void 0!==colorscheme&&1==up_colormode_switching&&(switchColorMode(),colorscheme.addEventListener("change",autoChangeColorMode,!0)),0!=await waitUntilPDFCompiled()){switchColorModePDF(),void 0===pdf_change_observer&&(pdf_change_observer=new MutationObserver((function(mutations){console.log("PDF changed, applying color mode"),switchColorModePDF()})));let pdf_viewer=document.getElementsByClassName("pdfViewer")[0];void 0!==pdf_viewer?pdf_change_observer.observe(pdf_viewer,{attributes:!0,childList:!0,subTree:!0}):console.warn("Element .pdfViewer was undefined, have you set the PDF viewer to something other than 'Overleaf'?")}}function destructColormode(){void 0!==colorscheme&&0==up_colormode_switching&&colorscheme.removeEventListener("change",autoChangeColorMode,!0),void 0!==pdf_change_observer&&(pdf_change_observer.disconnect(),$(".pdf-viewer .pdfjs-viewer .page").removeClass("conditional-invert-colors"))}function sendNotification(text){new Notification(`${text}`),updateCounter(1)}function cleanAndTruncateText(text,max_characters=15){return void 0===text||text.length<=0||(text=text.replace(/(\r\n|\n|\r)/gm,"")).length>max_characters&&(text=text.substring(0,max_characters)+"..."),text}function notificationsCooledDown(seconds=5,timestamp=lastNotificationResetTimestamp){return Date.now()-timestamp>1e3*seconds}async function setupNotifications(){if(1==(1==up_notifications_chats||1==up_notifications_comments||1==up_notifications_comment_threads||1==up_notifications_tracked_changes_created||1==up_notifications_tracked_changes_updated||1==up_notifications_tracked_changes_resolved)){if("Notification"in window?"granted"===Notification.permission||"denied"!==Notification.permission&&Notification.requestPermission((function(permission){"granted"===permission&&sendNotification("Notifications are now enabled")})):alert("This browser does not support notifications"),addEventListener("focus",resetCounter),1==up_notifications_tracked_changes_created||1==up_notifications_tracked_changes_updated||1==up_notifications_tracked_changes_resolved){let changes_scope=angular.element("[ng-controller=ReviewPanelController]").scope();if(changes_scope&&void 0!==changes_scope){if(void 0!==changes_watcher_unbind)throw"changes_watcher_unbind should be undefined at this point";changes_watcher_unbind=changes_scope.$watch("reviewPanel.entries",(function(newVal,oldVal){oldVal=oldVal[Object.keys(oldVal)[0]],newVal=newVal[Object.keys(newVal)[0]];const diffs=deepDiffMapper.map(oldVal,newVal),users=angular.element("[ng-controller=ReviewPanelController]").scope().reviewPanel.formattedProjectMembers;for(const diff_key in diffs){let payload=diffs[diff_key];if(null!=payload&&(payload.content&&void 0!==payload.content&&(payload=payload.content),payload.type&&void 0!==payload.type))if("created"==payload.type){let message=payload.updated;if(void 0!==message&&void 0!==message.content&&void 0!==message.metadata){message.content=cleanAndTruncateText(message.content);const user=users[message.metadata.user_id];1==up_notifications_tracked_changes_created&&1!=user.isSelf&&new Date(message.metadata.ts)>new Date(lastNotificationResetTimestamp)&¬ificationsCooledDown(1,lastChangeNotificationTimestamp)&&("aggregate-change"==message.type?sendNotification(`${user.name} suggests changing "${cleanAndTruncateText(message.metadata.replaced_content)}" to "${message.content}"`):"insert"==message.type?sendNotification(`${user.name} suggests adding "${message.content}"`):"delete"==message.type&&sendNotification(`${user.name} suggests removing "${message.content}"`),lastChangeNotificationTimestamp=Date.now())}}else if("updated"==payload.type)1==up_notifications_tracked_changes_updated&&void 0!==payload.original&&"string"==typeof payload.original&&void 0!==payload.updated&&"string"==typeof payload.updated&&0==document.hasFocus()&&(notificationsCooledDown(60,lastChangeNotificationTimestamp)&&sendNotification(`Suggested change "${cleanAndTruncateText(payload.original)}" was updated to "${cleanAndTruncateText(payload.updated)}"`),lastChangeNotificationTimestamp=Date.now());else if("deleted"==payload.type){if(1==up_notifications_tracked_changes_resolved&&void 0!==payload.original&¬ificationsCooledDown(1)&&0==document.hasFocus()){let message=payload.original;message.content=cleanAndTruncateText(message.content),"aggregate-change"==payload.original.type?sendNotification(`Resolved suggestion to change "${cleanAndTruncateText(message.metadata.replaced_content)}" to "${message.content}"`):"insert"==message.type?sendNotification(`Resolved suggestion to add "${message.content}"`):"delete"==message.type&&sendNotification(`Resolved suggestion to delete "${message.content}"`),lastChangeNotificationTimestamp=Date.now()}}else console.warn("Unrecognized payload type",payload)}}),!0)}}if(1==up_notifications_comments){let comments_scope=angular.element("[ng-controller=ReviewPanelController]").scope();if(comments_scope&&void 0!==comments_scope){if(void 0!==comments_watcher_unbind)throw"comments_watcher_unbind should be undefined at this point";comments_watcher_unbind=comments_scope.$watch("reviewPanel.commentThreads",(function(newVal,oldVal){const diffs=deepDiffMapper.map(oldVal,newVal);for(const diff_key in diffs){let payload=diffs[diff_key];if(payload.resolved&&payload.resolved_at&&payload.resolved_by_user){const user=payload.resolved_by_user.updated;new Date(payload.resolved_at.updated)>lastNotificationResetTimestamp&&!user.isSelf&&sendNotification(`${user.name} resolved a comment`)}let actionText="responded to a comment";payload.updated&&(payload=payload.updated,actionText="commented");const messages=payload.messages;for(const message_index in messages){let message=messages[message_index];message.updated&&(message=message.updated,!up_notifications_comment_threads)||message.timestamp>lastNotificationResetTimestamp&&message.user&&message.content&&!message.user.isSelf&&sendNotification(`${message.user.name} ${actionText}: ${message.content}`)}}}),!0)}}if(1==up_notifications_chats){let chat_scope=angular.element('[class="infinite-scroll messages"]').children().children();chat_scope&&chat_scope.length&&chat_scope[1]&&(void 0===chat_observer&&(chat_observer=new MutationObserver((function(mutations){if(mutations.length&&(mutations=mutations[mutations.length-1]),notificationsCooledDown(2))for(const message_index in mutations.addedNodes)message=mutations.addedNodes[message_index],message.getElementsByClassName&&(wrapper=message.getElementsByClassName("message-wrapper")[0],wrapper.getElementsByClassName("name").length&&(sendername=wrapper.getElementsByClassName("name")[0].getElementsByTagName("span")[0].innerHTML,contents=wrapper.getElementsByClassName("message")[0].getElementsByClassName("message-content"),last_texts=contents[contents.length-1].getElementsByTagName("p"),last_text=last_texts[last_texts.length-1].innerHTML,sendNotification(`${sendername} in chat: ${last_text}`)))}))),chat_observer.observe(chat_scope[1],{childList:!0,subtree:!0}))}}}function destructNotifications(){void 0!==comments_watcher_unbind&&(comments_watcher_unbind(),comments_watcher_unbind=void 0),void 0!==changes_watcher_unbind&&(changes_watcher_unbind(),changes_watcher_unbind=void 0),void 0!==chat_observer&&chat_observer.disconnect(),removeEventListener("focus",resetCounter)}function countEnabledNotificationPreferences(){return!!up_notifications_chats+!!up_notifications_comments+!!up_notifications_comment_threads+!!up_notifications_tracked_changes_created+!!up_notifications_tracked_changes_updated+!!up_notifications_tracked_changes_resolved}function notificationsRequiresSetup(){return 1==countEnabledNotificationPreferences()}function notificationsRequiresDestruction(){return 0==countEnabledNotificationPreferences()}function updateCounter(countToAdd){if(notificationCounter+=countToAdd,notificationCounter<=0)return resetCounter();const replaceOldCounter=/^(\(\d*\))\W/;replaceOldCounter.test(document.title)?document.title=document.title.replace(replaceOldCounter,`(${notificationCounter}) `):document.title=`(${notificationCounter}) ${originalDocumentTitle}`}function resetCounter(event){notificationCounter=0,document.title=originalDocumentTitle,lastNotificationResetTimestamp=Date.now()}function setCSS(){var css_text='\n body {\n background-color: #fff;\n color: black;\n }\n\n #left-menu {\n color: black;\n }\n\n .loading-screen {\n background-color: #fff;\n }\n\n .settings-toggle {\n cursor: pointer;\n display: inline-block;\n }\n .settings-toggle-switch {\n display: inline-block;\n background: #2e3644;\n border-radius: 16px;\n width: 58px;\n height: 32px;\n position: relative;\n vertical-align: middle;\n transition: background 0.25s;\n }\n .settings-toggle-switch:before, .settings-toggle-switch:after {\n content: "";\n }\n .settings-toggle-switch:before {\n display: block;\n background: linear-gradient(to bottom, #fff 0%, #eee 100%);\n border-radius: 50%;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);\n width: 24px;\n height: 24px;\n position: absolute;\n top: 4px;\n left: 4px;\n transition: left 0.25s;\n }\n .settings-toggle:hover .settings-toggle-switch:before {\n background: linear-gradient(to bottom, #fff 0%, #fff 100%);\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);\n }\n .settings-toggle-checkbox:checked + .settings-toggle-switch {\n background: #408827;\n }\n .settings-toggle-checkbox:checked + .settings-toggle-switch:before {\n left: 30px;\n }\n .settings-toggle-checkbox {\n position: absolute;\n visibility: hidden;\n }\n .settings-toggle-label {\n margin-left: 5px;\n position: relative;\n top: 2px;\n }\n dialog {\n width: 80vw;\n background: #EEEFEE;\n color: black;\n border-color: #E9E9E9;\n margin: auto;\n position: fixed;\n box-shadow: 5px;\n border-radius: 10px;\n }\n dialog img {\n max-width: 80%; \n display: block;\n margin-left: auto;\n margin-right: auto;\n }\n dialog::backdrop {\n background: black;\n opacity: 0.7;\n backdrop-filter: blur(25px);\n }\n\n #review-panel {\n background-color: #dadfed;\n color: #6b7797;\n border-left: 0 solid #d9d9d9;\n }\n\n .review-panel-toolbar {\n background-color: #fafafa;\n }\n\n .rp-entry {\n background-color: #fff;\n color: #6b7797;\n }\n\n .rp-comment-input {\n background-color: #fff;\n }\n\n .rp-nav {\n background-color: #fafafa;\n }\n\n .file-view {\n background-color: #f0f0f0;\n }\n\n .history-entry-details {\n background-color: #fff;\n color: #5d6879;\n }\n\n .history-entry-change-doc {\n color: #3f3f3f;\n }\n\n .history-labels-list, .history-labels-list-compare {\n background-color: #fff;\n }\n\n .conditional-invert-colors {\n filter: invert(100%) hue-rotate(180deg);\n }\n \n @media (prefers-color-scheme: dark) {\n body {\n background-color: #282a35;\n color: white;\n }\n\n .loading-screen {\n background-color: #282a35;\n }\n\n .loading-panel {\n background-color: #282a35;\n color: white;\n }\n\n dialog {\n background: #282A35;\n color: white;\n border-color: #485263;\n }\n dialog #closebutton {\n color: white;\n }\n #wordcountchart {\n filter: invert(1) hue-rotate(180deg);\n }\n\n .pdf-viewer {\n background-color: #485263;\n }\n\n #review-panel {\n background-color: #485263 !important;\n color: white !important;\n border-left: 0 solid black !important;\n }\n\n .review-panel-toolbar {\n background-color: #282a35 !important;\n }\n\n .rp-entry {\n background-color: #d1cfbc !important;\n color: darkslategray !important;\n }\n\n .rp-comment-input {\n background-color: floralwhite !important;\n }\n\n .rp-nav {\n background-color: floralwhite !important;\n }\n\n .file-view {\n background-color: #282a35 !important;\n }\n\n .history-entry-details {\n background-color: #485263 !important;\n color: floralwhite !important;\n }\n\n .history-entry-change-doc {\n color: floralwhite !important;\n }\n\n .history-labels-list, .history-labels-list-compare {\n background-color: #485263 !important;\n }\n\n .project-list-main {\n background-color: #485263;\n }\n\n .project-list-card {\n background-color: #282a35;\n color: white;\n }\n\n .project-list-table-name-link {\n color: lightskyblue;\n }\n\n .project-list-table-row:hover {\n background-color: darkslategray;\n }\n\n .current-plan a.current-plan-label {\n color: floralwhite;\n }\n\n footer.site-footer {\n background-color: black;\n }\n }\n ';let editor_font_family=up_editor_font_family;up_editor_font_family.length<=0&&(editor_font_family="inherit"),css_text+=`\n .cm-editor {\n --source-font-family: ${editor_font_family} !important;\n }`;let styleSheet=document.createElement("style");styleSheet.innerText=css_text,document.head.appendChild(styleSheet)}async function fetchAsync(url){let response=await fetch(url);return await response.json()}function semanticVersionCompare(a,b){return a.startsWith(b+"-")?-1:b.startsWith(a+"-")?1:a.localeCompare(b,void 0,{numeric:!0,sensitivity:"case",caseFirst:"upper"})}async function getTags(){const tags=await fetchAsync("https://api.github.com/repos/fjwillemsen/NativeOverleaf/tags");return null!=tags&&tags.length&&void 0!==tags.length||console.error("Can not retrieve latest version for update checking"),tags}function tagToVersion(tag){return tag.replace("v","")}function versionToTag(version){return`v${version}`}async function getTagsAfter(previous_version){let tags=await getTags();if(tags&&null!=tags&&tags.length)return tags=tags.filter((tag=>1==semanticVersionCompare(tagToVersion(tag.name),previous_version))),tags.map((tag=>tag.name));console.error("Could not get tags: ",tags)}async function checkForUpdate(reportAll=!1){const latest_version=tagToVersion((await getTags())[0].name),previous_version_checked=localStorage.getObject("previous_version_checked");if(0==reportAll&&void 0!==previous_version_checked&&0==semanticVersionCompare(latest_version,previous_version_checked))return void console.log("User already notified, skipping update notification");localStorage.setObject("previous_version_checked",latest_version);const comparison=semanticVersionCompare(latest_version,appversion);if(0==comparison&&""!==comparison)console.log("Update check completed, no update available."),1==reportAll&&alert("You're up to date with the latest version!");else if(1==comparison){confirm(`Update available! \n Current: ${appversion}, latest: ${latest_version}.\n Go to downloads page?`)&&window.open("https://github.com/fjwillemsen/NativeOverleaf/releases/latest/")}else if(-1==comparison){const result_text=`No update needed, current version (${appversion}) is newer than latest publicly available version (${latest_version}).`;console.log(result_text),1==reportAll&&alert(result_text)}else{const result_text=`Update check failed, invalid semantic version comparison outcome: ${comparison}`;console.warn(result_text),alert(result_text)}}function setAutoUpdateChecking(){checkForUpdate(),setInterval(checkForUpdate,216e5),waitForElm("#versionlabel").then((elm=>{document.querySelector("#versionlabel")&&(document.querySelector("#versionlabel").onclick=function(){console.log("Checking version:"),checkForUpdate(!0)})}))}function checkIfUpdated(){const previous_version=localStorage.getObject("previous_app_version","0.1"),comparison=semanticVersionCompare(appversion,previous_version);return 1==comparison&&""!==comparison?(localStorage.setObject("previous_app_version",appversion),!0):0!=comparison&&void(-1==comparison?localStorage.setObject("previous_app_version",appversion):alert(`Invalid version comparison between ${appversion} and ${previous_version}, outcome: ${comparison}`))}async function getReleasesByTags(tags){return await Promise.all(tags.map((async tag=>fetchAsync(`https://api.github.com/repos/fjwillemsen/NativeOverleaf/releases/tags/${tag}`))))}async function showChangelogIfUpdated(){const previous_version=localStorage.getObject("previous_app_version",void 0);if(1==checkIfUpdated()){1!=lib_showdownjs_loaded&&await insertShowdownJS();const tags_after_previous=await getTagsAfter(previous_version),releases=await getReleasesByTags(tags_after_previous);console.log(releases);const markdown_converter=new showdown.Converter,all_releasenotes=releases.map((release=>{if(1!=lib_showdownjs_loaded||null==release||null==release.body||""==release.body||null==release.name||""==release.name||null==release.html_url||""==release.html_url||null==release.tag_name||""==release.tag_name)return void console.error(`Can not retrieve release notes, contents: ${release}`);let releasenotes_md=release.body;const releasenotes_images=releasenotes_md.match(/!\[.*\]\(.*github\.com.*\)/gim);releasenotes_images&&void 0!==releasenotes_images&&releasenotes_images.length>0&&releasenotes_images.forEach((github_image=>{releasenotes_md=releasenotes_md.replace(github_image,github_image.replace("/blob/","/raw/"))}));const releasenotes=markdown_converter.makeHtml(releasenotes_md);return`\n
\n

Release notes of ${release.name}:

\n ${releasenotes}\n View release online\n
\n
`})).join(""),updated_to_text=void 0!==previous_version?`from version ${previous_version} to`:"to version",versions_in_between=releases.length>1?`

Release notes of ${releases.length} versions:

`:"";injectDialog("updatechangelogdialog",`\n

🥳 Updated ${updated_to_text} ${tagToVersion(releases[0].tag_name)}

\n
\n ${versions_in_between}\n
\n ${all_releasenotes}\n
\n View all releases online`).showModal()}}let wordcount_timer_id,wordcounts;async function waitUntilPDFCompiled(){return await recursiveCheckAndWait(isPDFLinkAvailable,500,5,!0)}function extractWordCount(){const modal=document.getElementById("clone-project-modal");if(modal&&void 0!==modal){const modaltext=modal.outerText,wordcount=modaltext.substring(modaltext.lastIndexOf("\nTotal Words:\n")+14,modaltext.lastIndexOf("\nHeaders:")),parsedWordCount=parseInt(wordcount);if(0==isNaN(parsedWordCount))return parsedWordCount}return!1}async function getWordCount(){let wordcount_el=angular.element("[ng-controller=WordCountModalController]");if(wordcount_el&&void 0!==wordcount_el&&void 0!==wordcount_el.scope){let wordcount_scope=wordcount_el.scope();if(void 0!==wordcount_scope&&0!=await waitUntilPDFCompiled()){wordcount_scope.openWordCountModal();const wordcount=await recursiveCheckAndWait(extractWordCount,50,100);return wordcount_scope.handleHide(),0==wordcount?void console.warn("Unable to get wordcount within 5 seconds, skipping"):wordcount}}}function getWordCounts(){let wordcounts=localStorage.getObject("wordcounts")||{};this.project_id in wordcounts||(wordcounts[this.project_id]={});const currentdate=getLocalDate();return currentdate in wordcounts[this.project_id]||(wordcounts[this.project_id][currentdate]={earliest:void 0,latest:void 0,hasbeennotified:!1}),wordcounts}function resetWordCounts(){return wordcounts=void 0,localStorage.removeItem("wordcounts")}async function updateWordCount(){wordcounts=getWordCounts();const currentdate=getLocalDate(),wordcount=await getWordCount(),hasbeennotified=wordcounts[this.project_id][currentdate].hasbeennotified;if(void 0===wordcount)return;void 0===wordcounts[this.project_id][currentdate].earliest&&(wordcounts[this.project_id][currentdate].earliest=wordcount),wordcounts[this.project_id][currentdate].latest=wordcount;const achieved_wordcount=wordcount-wordcounts[this.project_id][currentdate].earliest;if(0==hasbeennotified&&up_wordcount_dailytarget>0&&achieved_wordcount>=up_wordcount_dailytarget&&(new Notification("Awesome, already met today's target!",{body:`You wrote ${achieved_wordcount} words, ${achieved_wordcount-up_wordcount_dailytarget} above target!`}),wordcounts[this.project_id][currentdate].hasbeennotified=!0),0==hasbeennotified&&up_wordcount_notificationhour>-1){(new Date).getHours()==up_wordcount_notificationhour&&(up_wordcount_dailytarget<=0?new Notification(`You wrote ${achieved_wordcount} out of ${up_wordcount_dailytarget} words today.`):achieved_wordcount0)return backup_source_html.getElementsByTagName("a")[0]}return!1}function getBackupLink(backup_type_index){const backup_element=getBackupElement(backup_type_index);return""!=backup_element?backup_element.href:""}function isPDFLinkAvailable(){return getBackupElement(1)}function makeBackup(){getBackupLink(up_backup_type)}let wordcountchart_show_net_wordcount=!0;async function insertChartJS(){return $.ajaxSetup({cache:!0}),$.when($.getScript("https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js")).done((()=>{lib_chartjs_loaded=!0})).fail((()=>{alert("Unable to dynamically load ChartJS, do you have an active internet connection?")}))}function injectWordCountChartElement(){const dialog=injectDialog("wordcountchartdialog",'\n

Word count overview per day

\n \n
\n \n
',"#chat-wrapper");return document.getElementById("show_net_wordcount").checked=wordcountchart_show_net_wordcount,document.getElementById("show_net_wordcount").addEventListener("change",(()=>{wordcountchart_show_net_wordcount=document.getElementById("show_net_wordcount").checked,updateWordCountChartData()})),dialog}function getWordCountChartConfig(){let labels=[],counts=[];const label=1==wordcountchart_show_net_wordcount?"Net number of words written":"Total number of words";let config={type:"bar",data:{labels:labels,datasets:[]},options:{}};if(null==wordcounts||Object.keys(wordcounts).length<=0)return alert("Wordcounts have not been tracked or have not properly loaded, check that wordcount tracking is enabled and recompile the PDF"),config;const wordcounts_project=wordcounts[this.project_id];for(const[date,wordcount]of Object.entries(wordcounts_project)){labels.push(date);const count=1==wordcountchart_show_net_wordcount?wordcount.latest-wordcount.earliest:wordcount.latest;counts.push(count)}return 1==wordcountchart_show_net_wordcount&&up_wordcount_dailytarget>0&&config.data.datasets.push({label:"Daily target",data:Array(labels.length).fill(up_wordcount_dailytarget),type:"line",backgroundColor:"red",borderColor:"red"}),config.data.datasets.push({label:label,data:counts,backgroundColor:"#408827"}),config}function updateWordCountChartData(){const config=getWordCountChartConfig();wordcountchart.data.labels=config.data.labels,wordcountchart.data.datasets=config.data.datasets,wordcountchart.update()}async function showWordCountChart(){await insertChartJS(),null==wordcountchartdialog&&(wordcountchartdialog=injectWordCountChartElement(),wordcountchart=new Chart(document.getElementById("wordcountchart"),getWordCountChartConfig())),wordcountchartdialog.showModal()}function hideWordCountChart(){wordcountchartdialog.close()}const startTime=performance.now();setupColormode(),setupNotifications(),setupPreferencesPane(),setCSS(),setAutoUpdateChecking(),setupWordCount(),showChangelogIfUpdated();const endTime=performance.now();console.log(`Native Overleaf injected setup took ${endTime-startTime} milliseconds`); \ No newline at end of file +let comments_watcher_unbind,changes_watcher_unbind,chat_observer,compilation_observer,pdf_change_observer,colorscheme,lib_chartjs_loaded=!1,lib_showdownjs_loaded=!1;const appversion="1.9.2";try{angular}catch(e){if("ReferenceError"==e.name)throw new Error("Angular is not present on this page, Native Overleaf will not be active here")}let notificationCounter=0,lastNotificationResetTimestamp=Date.now(),lastChangeNotificationTimestamp=Date.now();const originalDocumentTitle=document.title;let current_colorscheme_preference;function getLocalDate(){return(new Date).toLocaleDateString("en-CA")}function getTimeInSeconds(){return Math.round((new Date).getTime()/1e3)}async function insertShowdownJS(){return $.ajaxSetup({cache:!0}),$.when($.getScript("https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js")).done((()=>{lib_showdownjs_loaded=!0})).fail((()=>{alert("Unable to dynamically load ShowdownJS, do you have an active internet connection?")}))}function injectDialog(id,innerhtml,insertionselector="body"){const html=`\n ×\n ${innerhtml}\n `;document.querySelector(insertionselector).insertAdjacentHTML("afterend",html);const dialog=document.querySelector(`#${id}`);return dialog.addEventListener("click",(function(event){const rect=dialog.getBoundingClientRect();(event.clientYrect.bottom||event.clientXrect.right)&&dialog.close()})),dialog.querySelector("#closebutton").addEventListener("click",(function(event){dialog.close()})),dialog}function recursiveCheckAndWait(checkFunction,waitTime,numberOfTimesToCheck,multiplyWaitTime=!1,numberOfTimesChecked=0){const checkFunctionResult=checkFunction();return numberOfTimesChecked+=1,0!=checkFunctionResult?checkFunctionResult:!(numberOfTimesToCheck-numberOfTimesChecked<=0)&&new Promise((resolve=>{1==multiplyWaitTime&&(waitTime*=numberOfTimesChecked),setTimeout((()=>{resolve(recursiveCheckAndWait(checkFunction,waitTime,numberOfTimesToCheck,multiplyWaitTime,numberOfTimesChecked))}),waitTime)}))}function waitForElm(selector){return new Promise((resolve=>{if(document.querySelector(selector))return resolve(document.querySelector(selector));const observer=new MutationObserver((mutations=>{document.querySelector(selector)&&(observer.disconnect(),resolve(document.querySelector(selector)))}));observer.observe(document.body,{childList:!0,subtree:!0})}))}window.matchMedia&&(colorscheme=window.matchMedia("(prefers-color-scheme: dark)"),current_colorscheme_preference=colorscheme.matches?"dark":"light"),Storage.prototype.setObject=function(key,value){this.setItem(key,JSON.stringify(value))},Storage.prototype.getObject=function(key,defaultvalue){const value=this.getItem(key);return value&&null!=value?JSON.parse(value):defaultvalue};const deepDiffMapper={VALUE_CREATED:"created",VALUE_UPDATED:"updated",VALUE_DELETED:"deleted",VALUE_UNCHANGED:"---",map:function(obj1,obj2){if(this.isFunction(obj1)||this.isFunction(obj2))throw"Invalid argument. Function given, object expected.";if(this.isValue(obj1)||this.isValue(obj2)){let returnObj={type:this.compareValues(obj1,obj2),original:obj1,updated:obj2};return returnObj.type!=this.VALUE_UNCHANGED?returnObj:void 0}let diff={},foundKeys={};for(let key in obj1){if(this.isFunction(obj1[key]))continue;let value2;void 0!==obj2[key]&&(value2=obj2[key]);let mapValue=this.map(obj1[key],value2);foundKeys[key]=!0,mapValue&&(diff[key]=mapValue)}for(let key in obj2){if(this.isFunction(obj2[key])||void 0!==foundKeys[key])continue;let mapValue=this.map(void 0,obj2[key]);mapValue&&(diff[key]=mapValue)}if(Object.keys(diff).length>0)return diff},compareValues:function(value1,value2){return value1===value2||this.isDate(value1)&&this.isDate(value2)&&value1.getTime()===value2.getTime()?this.VALUE_UNCHANGED:void 0===value1?this.VALUE_CREATED:void 0===value2?this.VALUE_DELETED:this.VALUE_UPDATED},isFunction:function(x){return"[object Function]"===Object.prototype.toString.call(x)},isArray:function(x){return"[object Array]"===Object.prototype.toString.call(x)},isDate:function(x){return"[object Date]"===Object.prototype.toString.call(x)},isObject:function(x){return"[object Object]"===Object.prototype.toString.call(x)},isValue:function(x){return!this.isObject(x)&&!this.isArray(x)}},overallThemes_dark={dark:"Default"},overallThemes_light={light:"Light"},editorThemes_dark={dracula:"Dracula",monokai:"Monokai",cobalt:"Cobalt"},editorThemes_light={textmate:"TextMate",overleaf:"Overleaf",eclipse:"Eclipse"},pdfThemes_dark={dark:"Inverted"},pdfThemes_light={light:"Standard"};let up_notifications_comments=localStorage.getObject("notifications_comment",!0),up_notifications_comment_threads=localStorage.getObject("notifications_comment_response",!0),up_notifications_chats=localStorage.getObject("notifications_chat",!0),up_notifications_tracked_changes_created=localStorage.getObject("notifications_tracked_changes_created",!0),up_notifications_tracked_changes_updated=localStorage.getObject("notifications_tracked_changes_updated",!0),up_notifications_tracked_changes_resolved=localStorage.getObject("notifications_tracked_changes_resolved",!0),up_colormode_switching=localStorage.getObject("colormode_switching",!0),up_overalltheme_dark=localStorage.getObject("overalltheme_dark",Object.keys(overallThemes_dark)[0]),up_overalltheme_light=localStorage.getObject("overalltheme_light",Object.keys(overallThemes_light)[0]),up_editortheme_dark=localStorage.getObject("editortheme_dark",Object.keys(editorThemes_dark)[0]),up_editortheme_light=localStorage.getObject("editortheme_light",Object.keys(editorThemes_light)[0]),up_pdftheme_dark=localStorage.getObject("pdftheme_dark",Object.keys(pdfThemes_dark)[0]),up_pdftheme_light=localStorage.getObject("pdftheme_light",Object.keys(pdfThemes_light)[0]),up_wordcount_tracking=localStorage.getObject("wordcount_tracking",!0),up_wordcount_dailytarget=localStorage.getObject("wordcount_dailytarget",200),up_wordcount_notificationhour=localStorage.getObject("wordcount_notificationhour",18),up_editor_font_family=localStorage.getObject("editor_font_family",""),up_wordcount_dailytarget_min=1,up_wordcount_dailytarget_max=2147483647,up_wordcount_notificationhour_min=-1,up_wordcount_notificationhour_max=23,settings_form;function getFormSelectHTML(category_dicts,category_names){let str="";for(category_index in category_dicts){let endstr="";category_names.length-1>=category_index&&(str+=``,endstr+=""),category_dict=category_dicts[category_index];for(let key in category_dict)str+=`\n`;str+=endstr}return str}function setupPreferencesPane(){const settings_html=`\n

Native Overleaf

\n
\n

Version ${appversion}

\n \n \n
\n
Notifications
\n \n
\n \n
\n \n
\n
\n Suggested Changes
\n Get notifications on tracked changes\n
\n \n
\n \n
\n \n
\n
Customizations
\n \n \n
\n
Dark / Light Mode
\n \n
\n
\n Dark Mode\n
\n \n \n
\n \n \n
\n \n \n
\n
\n Light Mode\n
\n \n \n
\n \n \n
\n \n \n
\n
\n The PDF option "inverted" forces the PDF viewer to inverted colors. This does not change the PDF file itself.\n
\n
\n
Wordcount Tracking
\n

When the document is recompiled, this keeps track of the number of words, allowing you to see your progress.

\n \n
\n \n \n
\n \n \n
\n
\n
Show wordcount graph
\n
\n
\n
Reset wordcount
\n
\n `;$(".editor-menu-icon").first().parent().click((function(){waitForElm("#left-menu").then((elm=>{document.querySelector("#left-menu")?waitForElm(".settings").then((elm=>{console.log(elm.querySelector(".settings")),document.querySelector(".settings")?(document.querySelector("#left-menu").getElementsByTagName("form")[0].insertAdjacentHTML("afterend",settings_html),settings_form=document.querySelector("#native-overleaf-settings"),settings_form.querySelector("#notifications_chat").checked=up_notifications_chats,settings_form.querySelector("#notifications_comment").checked=up_notifications_comments,settings_form.querySelector("#notifications_comment_response").checked=up_notifications_comment_threads,settings_form.querySelector("#notifications_tracked_changes_created").checked=up_notifications_tracked_changes_created,settings_form.querySelector("#notifications_tracked_changes_updated").checked=up_notifications_tracked_changes_updated,settings_form.querySelector("#notifications_tracked_changes_resolved").checked=up_notifications_tracked_changes_resolved,settings_form.querySelector("#colormode_switching").checked=up_colormode_switching,settings_form.querySelector("#overalltheme_dark").value=up_overalltheme_dark,settings_form.querySelector("#overalltheme_light").value=up_overalltheme_light,settings_form.querySelector("#editortheme_dark").value=up_editortheme_dark,settings_form.querySelector("#editortheme_light").value=up_editortheme_light,settings_form.querySelector("#pdftheme_dark").value=up_pdftheme_dark,settings_form.querySelector("#pdftheme_light").value=up_pdftheme_light,settings_form.querySelector("#wordcount_tracking").checked=up_wordcount_tracking,settings_form.querySelector("#wordcount_dailytarget").value=up_wordcount_dailytarget,settings_form.querySelector("#wordcount_notificationhour").value=up_wordcount_notificationhour,settings_form.querySelector("#editor_font_family").value=up_editor_font_family,settings_form.querySelector("#overalltheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#overalltheme_light").disabled=!up_colormode_switching,settings_form.querySelector("#editortheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#editortheme_light").disabled=!up_colormode_switching,settings_form.querySelector("#pdftheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#pdftheme_light").disabled=!up_colormode_switching,settings_form.querySelector("#wordcount_dailytarget").disabled=!up_wordcount_tracking,settings_form.querySelector("#wordcount_notificationhour").disabled=!up_wordcount_tracking,settings_form.addEventListener("change",(function(){for(let id_key in settings_handler)settings_handler[id_key](id_key,settings_form.querySelector(`#${id_key}`))})),document.getElementById("button_show_wordcount_graph").addEventListener("click",(()=>{showWordCountChart()})),document.getElementById("button_reset_wordcount").addEventListener("click",(()=>{let text_number_of_days="";void 0!==wordcounts&&void 0!==wordcounts[this.project_id]&&(text_number_of_days=`You currently have ${Object.keys(wordcounts[this.project_id]).length} tracked days.`);confirm(`Are you sure you want to remove all tracked wordcount history?\n ${text_number_of_days}`)&&(resetWordCounts(),setupWordCount())}))):console.log("No element '.settings'")})):console.log("No element '#left-menu'")}))}))}const settings_handler={notifications_chat:set_notifications_chat,notifications_comment:set_notifications_comment,notifications_comment_response:set_notifications_comment_response,notifications_tracked_changes_created:set_notifications_tracked_changes_created,notifications_tracked_changes_updated:set_notifications_tracked_changes_updated,notifications_tracked_changes_resolved:set_notifications_tracked_changes_resolved,colormode_switching:set_colormode_switching,overalltheme_dark:set_overalltheme_dark,overalltheme_light:set_overalltheme_light,editortheme_dark:set_editortheme_dark,editortheme_light:set_editortheme_light,pdftheme_dark:set_pdftheme_dark,pdftheme_light:set_pdftheme_light,wordcount_tracking:set_wordcount_tracking,wordcount_dailytarget:set_wordcount_dailytarget,wordcount_notificationhour:set_wordcount_notificationhour,editor_font_family:set_editor_font_family};function set_notifications_tracked_changes_created(key,value){value.checked!=up_notifications_tracked_changes_created&&(up_notifications_tracked_changes_created=value.checked,localStorage.setObject(key,value.checked),1==up_notifications_tracked_changes_created&&1==notificationsRequiresSetup()?setupNotifications():1==notificationsRequiresDestruction()&&destructWordCount())}function set_notifications_tracked_changes_updated(key,value){value.checked!=up_notifications_tracked_changes_updated&&(up_notifications_tracked_changes_updated=value.checked,localStorage.setObject(key,value.checked),1==up_notifications_tracked_changes_updated&&1==notificationsRequiresSetup()?setupNotifications():1==notificationsRequiresDestruction()&&destructWordCount())}function set_notifications_tracked_changes_resolved(key,value){value.checked!=up_notifications_tracked_changes_resolved&&(up_notifications_tracked_changes_resolved=value.checked,localStorage.setObject(key,value.checked),1==up_notifications_tracked_changes_resolved&&1==notificationsRequiresSetup()?setupNotifications():1==notificationsRequiresDestruction()&&destructWordCount())}function set_wordcount_tracking(key,value){value.checked!=up_wordcount_tracking&&(up_wordcount_tracking=value.checked,localStorage.setObject(key,value.checked),settings_form.querySelector("#wordcount_dailytarget").disabled=!up_wordcount_tracking,settings_form.querySelector("#wordcount_notificationhour").disabled=!up_wordcount_tracking,1==up_wordcount_tracking?setupWordCount():destructWordCount())}function set_wordcount_dailytarget(key,value){value.valueup_wordcount_dailytarget_max?(alert(`You set ${value.value}, but wordcount daily target must be between ${up_wordcount_dailytarget_min} and ${up_wordcount_dailytarget_max}`),settings_form.querySelector(`#${key}`).value=up_wordcount_dailytarget):value.value!=up_wordcount_dailytarget&&(up_wordcount_dailytarget=value.value,localStorage.setObject(key,value.value),setHasBeenNotified(!1))}function set_wordcount_notificationhour(key,value){(value.valueup_wordcount_notificationhour_max)&&(alert(`You set ${value.value}, but wordcount notification hour must be between ${up_wordcount_notificationhour_min} and ${up_wordcount_notificationhour_max}`),settings_form.querySelector(`#${key}`).value=up_wordcount_notificationhour),value.value!=up_wordcount_notificationhour&&(up_wordcount_notificationhour=value.value,localStorage.setObject(key,value.value),setHasBeenNotified(!1))}function set_notifications_chat(key,value){value!=up_notifications_chats&&(up_notifications_chats=value.checked,localStorage.setObject(key,value.checked),destructNotifications(),setupNotifications())}function set_notifications_comment(key,value){value.checked!=up_notifications_comments&&(up_notifications_comments=value.checked,localStorage.setObject(key,value.checked),destructNotifications(),setupNotifications())}function set_notifications_comment_response(key,value){value.checked!=up_notifications_comment_threads&&(up_notifications_comment_threads=value.checked,localStorage.setObject(key,value.checked),destructNotifications(),setupNotifications())}function set_editor_font_family(key,value){value.value!=up_editor_font_family&&(up_editor_font_family=value.value,localStorage.setObject(key,value.value),setCSS())}function set_colormode_switching(key,value){value.checked!=up_colormode_switching&&(up_colormode_switching=value.checked,localStorage.setObject(key,value.checked),settings_form.querySelector("#overalltheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#overalltheme_light").disabled=!up_colormode_switching,settings_form.querySelector("#editortheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#editortheme_light").disabled=!up_colormode_switching,settings_form.querySelector("#pdftheme_dark").disabled=!up_colormode_switching,settings_form.querySelector("#pdftheme_light").disabled=!up_colormode_switching,1==up_colormode_switching?setupColormode():destructColormode())}function themesetter(user_preference_variable_name,key,value){const user_preference_variable=eval(user_preference_variable_name);localStorage.setObject(key,value.value),value.value!=user_preference_variable&&(eval(`${user_preference_variable_name} = "${value.value}"`),switchColorMode())}function set_overalltheme_dark(key,value){themesetter("up_overalltheme_dark",key,value)}function set_overalltheme_light(key,value){themesetter("up_overalltheme_light",key,value)}function set_editortheme_dark(key,value){themesetter("up_editortheme_dark",key,value)}function set_editortheme_light(key,value){themesetter("up_editortheme_light",key,value)}function set_pdftheme_dark(key,value){themesetter("up_pdftheme_dark",key,value)}function set_pdftheme_light(key,value){themesetter("up_pdftheme_light",key,value)}const overallThemeToOverleaf={dark:"",light:"light-"};function switchColorModePDF(){current_pdfcolor="dark"==current_colorscheme_preference?up_pdftheme_dark:up_pdftheme_light,"dark"==current_pdfcolor?$(".pdf-viewer .pdfjs-viewer .page").addClass("conditional-invert-colors"):"light"==current_pdfcolor?$(".pdf-viewer .pdfjs-viewer .page").removeClass("conditional-invert-colors"):console.error(`current pdfcolor preference ${current_pdfcolor} is not a valid value`)}function switchColorMode(){console.log("switchColormode");let scope=angular.element("[ng-controller=IdeController]").scope();console.log(scope),scope&&scope.settings?scope.$applyAsync((function(){"dark"==current_colorscheme_preference?(scope.settings.overallTheme=overallThemeToOverleaf[up_overalltheme_dark],scope.darkTheme=!up_overalltheme_light,scope.settings.editorTheme=up_editortheme_dark):"light"==current_colorscheme_preference?(scope.settings.overallTheme=overallThemeToOverleaf[up_overalltheme_light],scope.darkTheme=!up_overalltheme_light,scope.settings.editorTheme=up_editortheme_light):console.error(`current colorscheme preference ${current_colorscheme_preference} is not a valid value`),console.log("Applied scope change"),console.log(angular.element("[ng-controller=IdeController]").scope()),switchColorModePDF()})):(console.log("No scope settings:"),console.log(scope))}function autoChangeColorMode(event){current_colorscheme_preference=event.matches?"dark":"light",switchColorMode()}async function setupColormode(){if(void 0!==colorscheme&&1==up_colormode_switching&&(switchColorMode(),colorscheme.addEventListener("change",autoChangeColorMode,!0)),0!=await waitUntilPDFCompiled()){switchColorModePDF(),void 0===pdf_change_observer&&(pdf_change_observer=new MutationObserver((function(mutations){console.log("PDF changed, applying color mode"),switchColorModePDF()})));let pdf_viewer=document.getElementsByClassName("pdfViewer")[0];void 0!==pdf_viewer?pdf_change_observer.observe(pdf_viewer,{attributes:!0,childList:!0,subTree:!0}):console.warn("Element .pdfViewer was undefined, have you set the PDF viewer to something other than 'Overleaf'?")}}function destructColormode(){void 0!==colorscheme&&0==up_colormode_switching&&colorscheme.removeEventListener("change",autoChangeColorMode,!0),void 0!==pdf_change_observer&&(pdf_change_observer.disconnect(),$(".pdf-viewer .pdfjs-viewer .page").removeClass("conditional-invert-colors"))}function sendNotification(text){new Notification(`${text}`),updateCounter(1)}function cleanAndTruncateText(text,max_characters=15){return void 0===text||text.length<=0||(text=text.replace(/(\r\n|\n|\r)/gm,"")).length>max_characters&&(text=text.substring(0,max_characters)+"..."),text}function notificationsCooledDown(seconds=5,timestamp=lastNotificationResetTimestamp){return Date.now()-timestamp>1e3*seconds}async function setupNotifications(){if(1==(1==up_notifications_chats||1==up_notifications_comments||1==up_notifications_comment_threads||1==up_notifications_tracked_changes_created||1==up_notifications_tracked_changes_updated||1==up_notifications_tracked_changes_resolved)){if("Notification"in window?"granted"===Notification.permission||"denied"!==Notification.permission&&Notification.requestPermission((function(permission){"granted"===permission&&sendNotification("Notifications are now enabled")})):alert("This browser does not support notifications"),addEventListener("focus",resetCounter),1==up_notifications_tracked_changes_created||1==up_notifications_tracked_changes_updated||1==up_notifications_tracked_changes_resolved){let changes_scope=angular.element("[ng-controller=ReviewPanelController]").scope();if(changes_scope&&void 0!==changes_scope){if(void 0!==changes_watcher_unbind)throw"changes_watcher_unbind should be undefined at this point";changes_watcher_unbind=changes_scope.$watch("reviewPanel.entries",(function(newVal,oldVal){oldVal=oldVal[Object.keys(oldVal)[0]],newVal=newVal[Object.keys(newVal)[0]];const diffs=deepDiffMapper.map(oldVal,newVal),users=angular.element("[ng-controller=ReviewPanelController]").scope().reviewPanel.formattedProjectMembers;for(const diff_key in diffs){let payload=diffs[diff_key];if(null!=payload&&(payload.content&&void 0!==payload.content&&(payload=payload.content),payload.type&&void 0!==payload.type))if("created"==payload.type){let message=payload.updated;if(void 0!==message&&void 0!==message.content&&void 0!==message.metadata){message.content=cleanAndTruncateText(message.content);const user=users[message.metadata.user_id];1==up_notifications_tracked_changes_created&&1!=user.isSelf&&new Date(message.metadata.ts)>new Date(lastNotificationResetTimestamp)&¬ificationsCooledDown(1,lastChangeNotificationTimestamp)&&("aggregate-change"==message.type?sendNotification(`${user.name} suggests changing "${cleanAndTruncateText(message.metadata.replaced_content)}" to "${message.content}"`):"insert"==message.type?sendNotification(`${user.name} suggests adding "${message.content}"`):"delete"==message.type&&sendNotification(`${user.name} suggests removing "${message.content}"`),lastChangeNotificationTimestamp=Date.now())}}else if("updated"==payload.type)1==up_notifications_tracked_changes_updated&&void 0!==payload.original&&"string"==typeof payload.original&&void 0!==payload.updated&&"string"==typeof payload.updated&&0==document.hasFocus()&&(notificationsCooledDown(60,lastChangeNotificationTimestamp)&&sendNotification(`Suggested change "${cleanAndTruncateText(payload.original)}" was updated to "${cleanAndTruncateText(payload.updated)}"`),lastChangeNotificationTimestamp=Date.now());else if("deleted"==payload.type){if(1==up_notifications_tracked_changes_resolved&&void 0!==payload.original&¬ificationsCooledDown(1)&&0==document.hasFocus()){let message=payload.original;message.content=cleanAndTruncateText(message.content),"aggregate-change"==payload.original.type?sendNotification(`Resolved suggestion to change "${cleanAndTruncateText(message.metadata.replaced_content)}" to "${message.content}"`):"insert"==message.type?sendNotification(`Resolved suggestion to add "${message.content}"`):"delete"==message.type&&sendNotification(`Resolved suggestion to delete "${message.content}"`),lastChangeNotificationTimestamp=Date.now()}}else console.warn("Unrecognized payload type",payload)}}),!0)}}if(1==up_notifications_comments){let comments_scope=angular.element("[ng-controller=ReviewPanelController]").scope();if(comments_scope&&void 0!==comments_scope){if(void 0!==comments_watcher_unbind)throw"comments_watcher_unbind should be undefined at this point";comments_watcher_unbind=comments_scope.$watch("reviewPanel.commentThreads",(function(newVal,oldVal){const diffs=deepDiffMapper.map(oldVal,newVal);for(const diff_key in diffs){let payload=diffs[diff_key];if(payload.resolved&&payload.resolved_at&&payload.resolved_by_user){const user=payload.resolved_by_user.updated;new Date(payload.resolved_at.updated)>lastNotificationResetTimestamp&&!user.isSelf&&sendNotification(`${user.name} resolved a comment`)}let actionText="responded to a comment";payload.updated&&(payload=payload.updated,actionText="commented");const messages=payload.messages;for(const message_index in messages){let message=messages[message_index];message.updated&&(message=message.updated,!up_notifications_comment_threads)||message.timestamp>lastNotificationResetTimestamp&&message.user&&message.content&&!message.user.isSelf&&sendNotification(`${message.user.name} ${actionText}: ${message.content}`)}}}),!0)}}if(1==up_notifications_chats){let chat_scope=angular.element('[class="infinite-scroll messages"]').children().children();chat_scope&&chat_scope.length&&chat_scope[1]&&(void 0===chat_observer&&(chat_observer=new MutationObserver((function(mutations){if(mutations.length&&(mutations=mutations[mutations.length-1]),notificationsCooledDown(2))for(const message_index in mutations.addedNodes)message=mutations.addedNodes[message_index],message.getElementsByClassName&&(wrapper=message.getElementsByClassName("message-wrapper")[0],wrapper.getElementsByClassName("name").length&&(sendername=wrapper.getElementsByClassName("name")[0].getElementsByTagName("span")[0].innerHTML,contents=wrapper.getElementsByClassName("message")[0].getElementsByClassName("message-content"),last_texts=contents[contents.length-1].getElementsByTagName("p"),last_text=last_texts[last_texts.length-1].innerHTML,sendNotification(`${sendername} in chat: ${last_text}`)))}))),chat_observer.observe(chat_scope[1],{childList:!0,subtree:!0}))}}}function destructNotifications(){void 0!==comments_watcher_unbind&&(comments_watcher_unbind(),comments_watcher_unbind=void 0),void 0!==changes_watcher_unbind&&(changes_watcher_unbind(),changes_watcher_unbind=void 0),void 0!==chat_observer&&chat_observer.disconnect(),removeEventListener("focus",resetCounter)}function countEnabledNotificationPreferences(){return!!up_notifications_chats+!!up_notifications_comments+!!up_notifications_comment_threads+!!up_notifications_tracked_changes_created+!!up_notifications_tracked_changes_updated+!!up_notifications_tracked_changes_resolved}function notificationsRequiresSetup(){return 1==countEnabledNotificationPreferences()}function notificationsRequiresDestruction(){return 0==countEnabledNotificationPreferences()}function updateCounter(countToAdd){if(notificationCounter+=countToAdd,notificationCounter<=0)return resetCounter();const replaceOldCounter=/^(\(\d*\))\W/;replaceOldCounter.test(document.title)?document.title=document.title.replace(replaceOldCounter,`(${notificationCounter}) `):document.title=`(${notificationCounter}) ${originalDocumentTitle}`}function resetCounter(event){notificationCounter=0,document.title=originalDocumentTitle,lastNotificationResetTimestamp=Date.now()}function setCSS(){var css_text='\n body {\n background-color: #fff;\n color: black;\n }\n\n #left-menu {\n color: black;\n }\n\n .loading-screen {\n background-color: #fff;\n }\n\n .settings-toggle {\n cursor: pointer;\n display: inline-block;\n }\n .settings-toggle-switch {\n display: inline-block;\n background: #2e3644;\n border-radius: 16px;\n width: 58px;\n height: 32px;\n position: relative;\n vertical-align: middle;\n transition: background 0.25s;\n }\n .settings-toggle-switch:before, .settings-toggle-switch:after {\n content: "";\n }\n .settings-toggle-switch:before {\n display: block;\n background: linear-gradient(to bottom, #fff 0%, #eee 100%);\n border-radius: 50%;\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);\n width: 24px;\n height: 24px;\n position: absolute;\n top: 4px;\n left: 4px;\n transition: left 0.25s;\n }\n .settings-toggle:hover .settings-toggle-switch:before {\n background: linear-gradient(to bottom, #fff 0%, #fff 100%);\n box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.5);\n }\n .settings-toggle-checkbox:checked + .settings-toggle-switch {\n background: #408827;\n }\n .settings-toggle-checkbox:checked + .settings-toggle-switch:before {\n left: 30px;\n }\n .settings-toggle-checkbox {\n position: absolute;\n visibility: hidden;\n }\n .settings-toggle-label {\n margin-left: 5px;\n position: relative;\n top: 2px;\n }\n dialog {\n width: 80vw;\n background: #EEEFEE;\n color: black;\n border-color: #E9E9E9;\n margin: auto;\n position: fixed;\n box-shadow: 5px;\n border-radius: 10px;\n }\n dialog img {\n max-width: 80%; \n display: block;\n margin-left: auto;\n margin-right: auto;\n }\n dialog::backdrop {\n background: black;\n opacity: 0.7;\n backdrop-filter: blur(25px);\n }\n\n #review-panel {\n background-color: #dadfed;\n color: #6b7797;\n border-left: 0 solid #d9d9d9;\n }\n\n .review-panel-toolbar {\n background-color: #fafafa;\n }\n\n .rp-entry {\n background-color: #fff;\n color: #6b7797;\n }\n\n .rp-comment-input {\n background-color: #fff;\n }\n\n .rp-nav {\n background-color: #fafafa;\n }\n\n .file-view {\n background-color: #f0f0f0;\n }\n\n .history-entry-details {\n background-color: #fff;\n color: #5d6879;\n }\n\n .history-entry-change-doc {\n color: #3f3f3f;\n }\n\n .history-labels-list, .history-labels-list-compare {\n background-color: #fff;\n }\n\n .conditional-invert-colors {\n filter: invert(100%) hue-rotate(180deg);\n }\n \n @media (prefers-color-scheme: dark) {\n body {\n background-color: #282a35;\n color: white;\n }\n\n .loading-screen {\n background-color: #282a35;\n }\n\n .loading-panel {\n background-color: #282a35;\n color: white;\n }\n\n dialog {\n background: #282A35;\n color: white;\n border-color: #485263;\n }\n dialog #closebutton {\n color: white;\n }\n #wordcountchart {\n filter: invert(1) hue-rotate(180deg);\n }\n\n .pdf-viewer {\n background-color: #485263;\n }\n\n #review-panel {\n background-color: #485263 !important;\n color: white !important;\n border-left: 0 solid black !important;\n }\n\n .review-panel-toolbar {\n background-color: #282a35 !important;\n }\n\n .rp-entry {\n background-color: #d1cfbc !important;\n color: darkslategray !important;\n }\n\n .rp-comment-input {\n background-color: floralwhite !important;\n }\n\n .rp-nav {\n background-color: floralwhite !important;\n }\n\n .file-view {\n background-color: #282a35 !important;\n }\n\n .history-entry-details {\n background-color: #485263 !important;\n color: floralwhite !important;\n }\n\n .history-entry-change-doc {\n color: floralwhite !important;\n }\n\n .history-labels-list, .history-labels-list-compare {\n background-color: #485263 !important;\n }\n\n .project-list-main {\n background-color: #485263;\n }\n\n .project-list-card {\n background-color: #282a35;\n color: white;\n }\n\n .project-list-table-name-link {\n color: lightskyblue;\n }\n\n .project-list-table-row:hover {\n background-color: darkslategray;\n }\n\n .current-plan a.current-plan-label {\n color: floralwhite;\n }\n\n footer.site-footer {\n background-color: black;\n }\n }\n ';let editor_font_family=up_editor_font_family;up_editor_font_family.length<=0&&(editor_font_family="inherit"),css_text+=`\n .cm-editor {\n --source-font-family: ${editor_font_family} !important;\n }`;let styleSheet=document.createElement("style");styleSheet.innerText=css_text,document.head.appendChild(styleSheet)}async function fetchAsync(url){let response=await fetch(url);return await response.json()}function semanticVersionCompare(a,b){return a.startsWith(b+"-")?-1:b.startsWith(a+"-")?1:a.localeCompare(b,void 0,{numeric:!0,sensitivity:"case",caseFirst:"upper"})}async function getTags(){const tags=await fetchAsync("https://api.github.com/repos/fjwillemsen/NativeOverleaf/tags");return null!=tags&&tags.length&&void 0!==tags.length||console.error("Can not retrieve latest version for update checking"),tags}function tagToVersion(tag){return tag.replace("v","")}function versionToTag(version){return`v${version}`}async function getTagsAfter(previous_version){let tags=await getTags();if(tags&&null!=tags&&tags.length)return tags=tags.filter((tag=>1==semanticVersionCompare(tagToVersion(tag.name),previous_version))),tags.map((tag=>tag.name));console.error("Could not get tags: ",tags)}async function checkForUpdate(reportAll=!1){const latest_version=tagToVersion((await getTags())[0].name),previous_version_checked=localStorage.getObject("previous_version_checked");if(0==reportAll&&void 0!==previous_version_checked&&0==semanticVersionCompare(latest_version,previous_version_checked))return void console.log("User already notified, skipping update notification");localStorage.setObject("previous_version_checked",latest_version);const comparison=semanticVersionCompare(latest_version,appversion);if(0==comparison&&""!==comparison)console.log("Update check completed, no update available."),1==reportAll&&alert("You're up to date with the latest version!");else if(1==comparison){confirm(`Update available! \n Current: ${appversion}, latest: ${latest_version}.\n Go to downloads page?`)&&window.open("https://github.com/fjwillemsen/NativeOverleaf/releases/latest/")}else if(-1==comparison){const result_text=`No update needed, current version (${appversion}) is newer than latest publicly available version (${latest_version}).`;console.log(result_text),1==reportAll&&alert(result_text)}else{const result_text=`Update check failed, invalid semantic version comparison outcome: ${comparison}`;console.warn(result_text),alert(result_text)}}function setAutoUpdateChecking(){checkForUpdate(),setInterval(checkForUpdate,216e5),waitForElm("#versionlabel").then((elm=>{document.querySelector("#versionlabel")&&(document.querySelector("#versionlabel").onclick=function(){console.log("Checking version:"),checkForUpdate(!0)})}))}function checkIfUpdated(){const previous_version=localStorage.getObject("previous_app_version","0.1"),comparison=semanticVersionCompare(appversion,previous_version);return 1==comparison&&""!==comparison?(localStorage.setObject("previous_app_version",appversion),!0):0!=comparison&&void(-1==comparison?localStorage.setObject("previous_app_version",appversion):alert(`Invalid version comparison between ${appversion} and ${previous_version}, outcome: ${comparison}`))}async function getReleasesByTags(tags){return await Promise.all(tags.map((async tag=>fetchAsync(`https://api.github.com/repos/fjwillemsen/NativeOverleaf/releases/tags/${tag}`))))}async function showChangelogIfUpdated(){const previous_version=localStorage.getObject("previous_app_version",void 0);if(1==checkIfUpdated()){1!=lib_showdownjs_loaded&&await insertShowdownJS();const tags_after_previous=await getTagsAfter(previous_version),releases=await getReleasesByTags(tags_after_previous);console.log(releases);const markdown_converter=new showdown.Converter,all_releasenotes=releases.map((release=>{if(1!=lib_showdownjs_loaded||null==release||null==release.body||""==release.body||null==release.name||""==release.name||null==release.html_url||""==release.html_url||null==release.tag_name||""==release.tag_name)return void console.error(`Can not retrieve release notes, contents: ${release}`);let releasenotes_md=release.body;const releasenotes_images=releasenotes_md.match(/!\[.*\]\(.*github\.com.*\)/gim);releasenotes_images&&void 0!==releasenotes_images&&releasenotes_images.length>0&&releasenotes_images.forEach((github_image=>{releasenotes_md=releasenotes_md.replace(github_image,github_image.replace("/blob/","/raw/"))}));const releasenotes=markdown_converter.makeHtml(releasenotes_md);return`\n
\n

Release notes of ${release.name}:

\n ${releasenotes}\n View release online\n
\n
`})).join(""),updated_to_text=void 0!==previous_version?`from version ${previous_version} to`:"to version",versions_in_between=releases.length>1?`

Release notes of ${releases.length} versions:

`:"";injectDialog("updatechangelogdialog",`\n

🥳 Updated ${updated_to_text} ${tagToVersion(releases[0].tag_name)}

\n
\n ${versions_in_between}\n
\n ${all_releasenotes}\n
\n View all releases online`).showModal()}}let wordcount_timer_id,wordcounts;async function waitUntilPDFCompiled(){return await recursiveCheckAndWait(isPDFLinkAvailable,500,5,!0)}function extractWordCount(){const modal=document.getElementById("clone-project-modal");if(modal&&void 0!==modal){const modaltext=modal.outerText,wordcount=modaltext.substring(modaltext.lastIndexOf("\nTotal Words:\n")+14,modaltext.lastIndexOf("\nHeaders:")),parsedWordCount=parseInt(wordcount);if(0==isNaN(parsedWordCount))return parsedWordCount}return!1}async function getWordCount(){let wordcount_el=angular.element("[ng-controller=WordCountModalController]");if(wordcount_el&&void 0!==wordcount_el&&void 0!==wordcount_el.scope){let wordcount_scope=wordcount_el.scope();if(void 0!==wordcount_scope&&0!=await waitUntilPDFCompiled()){wordcount_scope.openWordCountModal();const wordcount=await recursiveCheckAndWait(extractWordCount,50,100);return wordcount_scope.handleHide(),0==wordcount?void console.warn("Unable to get wordcount within 5 seconds, skipping"):wordcount}}}function getWordCounts(){let wordcounts=localStorage.getObject("wordcounts")||{};this.project_id in wordcounts||(wordcounts[this.project_id]={});const currentdate=getLocalDate();return currentdate in wordcounts[this.project_id]||(wordcounts[this.project_id][currentdate]={earliest:void 0,latest:void 0,hasbeennotified:!1}),wordcounts}function resetWordCounts(){return wordcounts=void 0,localStorage.removeItem("wordcounts")}async function updateWordCount(){wordcounts=getWordCounts();const currentdate=getLocalDate(),wordcount=await getWordCount(),hasbeennotified=wordcounts[this.project_id][currentdate].hasbeennotified;if(void 0===wordcount)return;void 0===wordcounts[this.project_id][currentdate].earliest&&(wordcounts[this.project_id][currentdate].earliest=wordcount),wordcounts[this.project_id][currentdate].latest=wordcount;const achieved_wordcount=wordcount-wordcounts[this.project_id][currentdate].earliest;if(0==hasbeennotified&&up_wordcount_dailytarget>0&&achieved_wordcount>=up_wordcount_dailytarget&&(new Notification("Awesome, already met today's target!",{body:`You wrote ${achieved_wordcount} words, ${achieved_wordcount-up_wordcount_dailytarget} above target!`}),wordcounts[this.project_id][currentdate].hasbeennotified=!0),0==hasbeennotified&&up_wordcount_notificationhour>-1){(new Date).getHours()==up_wordcount_notificationhour&&(up_wordcount_dailytarget<=0?new Notification(`You wrote ${achieved_wordcount} out of ${up_wordcount_dailytarget} words today.`):achieved_wordcount0)return backup_source_html.getElementsByTagName("a")[0]}return!1}function getBackupLink(backup_type_index){const backup_element=getBackupElement(backup_type_index);return""!=backup_element?backup_element.href:""}function isPDFLinkAvailable(){return getBackupElement(1)}function makeBackup(){getBackupLink(up_backup_type)}let wordcountchart_show_net_wordcount=!0;async function insertChartJS(){return $.ajaxSetup({cache:!0}),$.when($.getScript("https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js")).done((()=>{lib_chartjs_loaded=!0})).fail((()=>{alert("Unable to dynamically load ChartJS, do you have an active internet connection?")}))}function injectWordCountChartElement(){const dialog=injectDialog("wordcountchartdialog",'\n

Word count overview per day

\n \n
\n \n
',"#chat-wrapper");return document.getElementById("show_net_wordcount").checked=wordcountchart_show_net_wordcount,document.getElementById("show_net_wordcount").addEventListener("change",(()=>{wordcountchart_show_net_wordcount=document.getElementById("show_net_wordcount").checked,updateWordCountChartData()})),dialog}function getWordCountChartConfig(){let labels=[],counts=[];const label=1==wordcountchart_show_net_wordcount?"Net number of words written":"Total number of words";let config={type:"bar",data:{labels:labels,datasets:[]},options:{}};if(null==wordcounts||Object.keys(wordcounts).length<=0)return alert("Wordcounts have not been tracked or have not properly loaded, check that wordcount tracking is enabled and recompile the PDF"),config;const wordcounts_project=wordcounts[this.project_id];for(const[date,wordcount]of Object.entries(wordcounts_project)){labels.push(date);const count=1==wordcountchart_show_net_wordcount?wordcount.latest-wordcount.earliest:wordcount.latest;counts.push(count)}return 1==wordcountchart_show_net_wordcount&&up_wordcount_dailytarget>0&&config.data.datasets.push({label:"Daily target",data:Array(labels.length).fill(up_wordcount_dailytarget),type:"line",backgroundColor:"red",borderColor:"red"}),config.data.datasets.push({label:label,data:counts,backgroundColor:"#408827"}),config}function updateWordCountChartData(){const config=getWordCountChartConfig();wordcountchart.data.labels=config.data.labels,wordcountchart.data.datasets=config.data.datasets,wordcountchart.update()}async function showWordCountChart(){await insertChartJS(),null==wordcountchartdialog&&(wordcountchartdialog=injectWordCountChartElement(),wordcountchart=new Chart(document.getElementById("wordcountchart"),getWordCountChartConfig())),wordcountchartdialog.showModal()}function hideWordCountChart(){wordcountchartdialog.close()}const startTime=performance.now();setupColormode(),setupNotifications(),setupPreferencesPane(),setCSS(),setAutoUpdateChecking(),setupWordCount(),showChangelogIfUpdated();const endTime=performance.now();console.log(`Native Overleaf injected setup took ${endTime-startTime} milliseconds`); \ No newline at end of file diff --git a/compile-all.sh b/compile-all.sh index fc7619d..e46af9d 100755 --- a/compile-all.sh +++ b/compile-all.sh @@ -99,8 +99,10 @@ options="--darwin-dark-mode-support --counter --bounce --fast-quit" compile $platform "arm64" $icon $options compile $platform "x64" $icon $options echo "Codesigning Mac apps" -codesign --deep --force -s "NativeOverleaf" Binaries/Overleaf-darwin-arm64/Overleaf.app -codesign --deep --force -s "NativeOverleaf" Binaries/Overleaf-darwin-x64/Overleaf.app +xattr -c Binaries/Overleaf-darwin-arm64/Overleaf.app +xattr -c Binaries/Overleaf-darwin-x64/Overleaf.app +codesign -s - -f --deep Binaries/Overleaf-darwin-arm64/Overleaf.app +codesign -s - -f --deep Binaries/Overleaf-darwin-x64/Overleaf.app echo "" # Linux diff --git a/package-lock.json b/package-lock.json index 48fe562..9bb157b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "native-overleaf", - "version": "1.9.1", + "version": "1.9.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "native-overleaf", - "version": "1.9.1", + "version": "1.9.2", "license": "GPL-3.0", "dependencies": { "nativefier": "^52.0.0" @@ -12357,4 +12357,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 831ae71..5d2cadd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "native-overleaf", - "version": "1.9.1", + "version": "1.9.2", "description": "Next-level academia by extending Overleaf in a native package.", "main": "bundled_script.js", "scripts": {