diff --git a/bundled_script.js b/bundled_script.js index 76c75a4..3916f2f 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.8.0";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 ChartJS, 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)}))}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"};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_colormode_switching_pdf=localStorage.getObject("colormode_switching_pdf"),up_overalltheme_dark=localStorage.getObject("overalltheme_dark",overallThemes_dark[0]),up_overalltheme_light=localStorage.getObject("overalltheme_light",overallThemes_light[0]),up_editortheme_dark=localStorage.getObject("editortheme_dark",editorThemes_dark[0]),up_editortheme_light=localStorage.getObject("editortheme_light",editorThemes_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 This option forces the PDF viewer to inverted colors. This does not change the PDF file itself.\n
\n
\n Dark Mode\n
\n \n \n
\n \n \n
\n
\n Light Mode\n
\n \n \n
\n \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 `;document.querySelector("#left-menu")&&(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("#colormode_switching_pdf").checked=up_colormode_switching_pdf,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("#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("#colormode_switching_pdf").disabled=!up_colormode_switching,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("#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())})))}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,colormode_switching_pdf:set_colormode_switching_pdf,overalltheme_dark:set_overalltheme_dark,overalltheme_light:set_overalltheme_light,editortheme_dark:set_editortheme_dark,editortheme_light:set_editortheme_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("#colormode_switching_pdf").disabled=!up_colormode_switching,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,1==up_colormode_switching?setupColormode():destructColormode())}function set_colormode_switching_pdf(key,value){value.checked!=up_colormode_switching_pdf&&(up_colormode_switching_pdf=value.checked,localStorage.setObject(key,value.checked),1==up_colormode_switching_pdf?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)}const overallThemeToOverleaf={dark:"",light:"light-"};function switchColorModePDF(){1==up_colormode_switching_pdf&&("dark"==current_colorscheme_preference?$(".pdf-viewer .pdfjs-viewer .page").addClass("conditional-invert-colors"):"light"==current_colorscheme_preference&&$(".pdf-viewer .pdfjs-viewer .page").removeClass("conditional-invert-colors"))}function switchColorMode(){let scope=angular.element("[ng-controller=SettingsController]").scope();scope&&("dark"==current_colorscheme_preference?(scope.settings.overallTheme=overallThemeToOverleaf[up_overalltheme_dark],scope.settings.editorTheme=up_editortheme_dark):"light"==current_colorscheme_preference?(scope.settings.overallTheme=overallThemeToOverleaf[up_overalltheme_light],scope.settings.editorTheme=up_editortheme_light):console.err(`current colorscheme preference ${current_colorscheme_preference} is not a valid value`),scope.$apply(),switchColorModePDF())}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)),1==up_colormode_switching_pdf&&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),0==up_colormode_switching_pdf&&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 @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 .conditional-invert-colors {\n filter: invert(100%) hue-rotate(180deg);\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){console.log("checkForUpdate");const latest_version=tagToVersion((await getTags())[0].name);console.log(latest_version);const 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),console.log(reportAll);const comparison=semanticVersionCompare(latest_version,appversion);if(console.log(comparison),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),document.querySelector("#versionlabel")&&(document.querySelector("#versionlabel").onclick=function(){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 extensionId="opgfklgemimfakkhhenkjpebopabfjjd",writefullUrl="https://storage.googleapis.com/writefull-cdn/writefull.js";let isRunning=!1;document.addEventListener("readystatechange",(event=>{if(isRunning=!0,console.log("Writefull"),"interactive"===document.readyState||"complete"===document.readyState&&!window._writefull&&!isRunning){console.log("Writefull readystatechange");const scriptCM6=document.createElement("script");scriptCM6.type="text/javascript",scriptCM6.innerHTML="\n window.addEventListener('UNSTABLE_editor:extensions', (event) => {\n if (event.detail) {\n const { CodeMirror, extensions } = event.detail;\n\n window._writefull = {\n ...CodeMirror,\n extensions\n }\n }\n\n if (window.w$) {\n w$.reloadEditor();\n }\n });\n window.addEventListener('editor:extensions', (event) => {\n if (event.detail) {\n const { CodeMirror, extensions } = event.detail;\n\n window._writefull = {\n ...CodeMirror,\n extensions\n }\n }\n\n if (window.w$) {\n w$.reloadEditor();\n }\n });\n ",document.body.append(scriptCM6),setTimeout((()=>{scriptCM6.remove()}),0),isRunning=!1}}));const load=function(){setTimeout((function(){const script=document.createElement("script");script.src=`${writefullUrl}?ts=${+new Date}`,console.log(script.src),script.type="text/javascript",document.body.append(script),console.log("Writefull appended!"),console.log(document.body),setTimeout((()=>{script.remove()}),0)}),1e3)};window.addEventListener("load",load);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.8.0";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 ChartJS, 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)}))}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"};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_colormode_switching_pdf=localStorage.getObject("colormode_switching_pdf"),up_overalltheme_dark=localStorage.getObject("overalltheme_dark",overallThemes_dark[0]),up_overalltheme_light=localStorage.getObject("overalltheme_light",overallThemes_light[0]),up_editortheme_dark=localStorage.getObject("editortheme_dark",editorThemes_dark[0]),up_editortheme_light=localStorage.getObject("editortheme_light",editorThemes_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 This option forces the PDF viewer to inverted colors. This does not change the PDF file itself.\n
\n
\n Dark Mode\n
\n \n \n
\n \n \n
\n
\n Light Mode\n
\n \n \n
\n \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 `;document.querySelector("#left-menu")&&(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("#colormode_switching_pdf").checked=up_colormode_switching_pdf,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("#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("#colormode_switching_pdf").disabled=!up_colormode_switching,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("#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())})))}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,colormode_switching_pdf:set_colormode_switching_pdf,overalltheme_dark:set_overalltheme_dark,overalltheme_light:set_overalltheme_light,editortheme_dark:set_editortheme_dark,editortheme_light:set_editortheme_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("#colormode_switching_pdf").disabled=!up_colormode_switching,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,1==up_colormode_switching?setupColormode():destructColormode())}function set_colormode_switching_pdf(key,value){value.checked!=up_colormode_switching_pdf&&(up_colormode_switching_pdf=value.checked,localStorage.setObject(key,value.checked),1==up_colormode_switching_pdf?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)}const overallThemeToOverleaf={dark:"",light:"light-"};function switchColorModePDF(){1==up_colormode_switching_pdf&&("dark"==current_colorscheme_preference?$(".pdf-viewer .pdfjs-viewer .page").addClass("conditional-invert-colors"):"light"==current_colorscheme_preference&&$(".pdf-viewer .pdfjs-viewer .page").removeClass("conditional-invert-colors"))}function switchColorMode(){let scope=angular.element("[ng-controller=SettingsController]").scope();scope&&("dark"==current_colorscheme_preference?(scope.settings.overallTheme=overallThemeToOverleaf[up_overalltheme_dark],scope.settings.editorTheme=up_editortheme_dark):"light"==current_colorscheme_preference?(scope.settings.overallTheme=overallThemeToOverleaf[up_overalltheme_light],scope.settings.editorTheme=up_editortheme_light):console.err(`current colorscheme preference ${current_colorscheme_preference} is not a valid value`),scope.$apply(),switchColorModePDF())}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)),1==up_colormode_switching_pdf&&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),0==up_colormode_switching_pdf&&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 @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 .conditional-invert-colors {\n filter: invert(100%) hue-rotate(180deg);\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),document.querySelector("#versionlabel")&&(document.querySelector("#versionlabel").onclick=function(){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 extensionId="opgfklgemimfakkhhenkjpebopabfjjd",writefullUrl="https://storage.googleapis.com/writefull-cdn/writefull.js";let isRunning=!1;document.addEventListener("readystatechange",(event=>{if(isRunning=!0,console.log("Writefull"),"interactive"===document.readyState||"complete"===document.readyState&&!window._writefull&&!isRunning){console.log("Writefull readystatechange");const scriptCM6=document.createElement("script");scriptCM6.type="text/javascript",scriptCM6.innerHTML="\n window.addEventListener('UNSTABLE_editor:extensions', (event) => {\n if (event.detail) {\n const { CodeMirror, extensions } = event.detail;\n\n window._writefull = {\n ...CodeMirror,\n extensions\n }\n }\n\n if (window.w$) {\n w$.reloadEditor();\n }\n });\n window.addEventListener('editor:extensions', (event) => {\n if (event.detail) {\n const { CodeMirror, extensions } = event.detail;\n\n window._writefull = {\n ...CodeMirror,\n extensions\n }\n }\n\n if (window.w$) {\n w$.reloadEditor();\n }\n });\n ",document.body.append(scriptCM6),setTimeout((()=>{scriptCM6.remove()}),0),isRunning=!1}}));const load=function(){setTimeout((function(){const script=document.createElement("script");script.src=`${writefullUrl}?ts=${+new Date}`,console.log(script.src),script.type="text/javascript",document.body.append(script),console.log("Writefull appended!"),console.log(document.body),setTimeout((()=>{script.remove()}),0)}),1e3)};window.addEventListener("load",load);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