| 
									
										
										
										
											2020-01-11 21:19:56 +01:00
										 |  |  | import GlobalButtonsWidget from "../widgets/global_buttons.js"; | 
					
						
							|  |  |  | import SearchBoxWidget from "../widgets/search_box.js"; | 
					
						
							|  |  |  | import SearchResultsWidget from "../widgets/search_results.js"; | 
					
						
							|  |  |  | import NoteTreeWidget from "../widgets/note_tree.js"; | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  | import treeService from "./tree.js"; | 
					
						
							|  |  |  | import noteDetailService from "./note_detail.js"; | 
					
						
							|  |  |  | import TabContext from "./tab_context.js"; | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  | import server from "./server.js"; | 
					
						
							|  |  |  | import keyboardActionService from "./keyboard_actions.js"; | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  | import TabRowWidget from "./tab_row.js"; | 
					
						
							| 
									
										
										
										
											2020-01-12 23:03:55 +01:00
										 |  |  | import NoteTitleWidget from "../widgets/note_title.js"; | 
					
						
							| 
									
										
										
										
											2020-01-11 21:19:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 09:57:28 +01:00
										 |  |  | class AppContext { | 
					
						
							| 
									
										
										
										
											2020-01-11 21:19:56 +01:00
										 |  |  |     constructor() { | 
					
						
							|  |  |  |         this.widgets = []; | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  |         /** @type {TabContext[]} */ | 
					
						
							|  |  |  |         this.tabContexts = []; | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  |         this.tabsChangedTaskId = null; | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         /** @type {TabRowWidget} */ | 
					
						
							|  |  |  |         this.tabRow = null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     showWidgets() { | 
					
						
							|  |  |  |         const $leftPane = $("#left-pane"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.tabRow = new TabRowWidget(this); | 
					
						
							| 
									
										
										
										
											2020-01-12 20:15:05 +01:00
										 |  |  |         const contents = this.tabRow.render(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $("#global-menu-wrapper").after(contents); | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 23:03:55 +01:00
										 |  |  |         this.noteTitleWidget = new NoteTitleWidget(this); | 
					
						
							|  |  |  |         $("#center-pane").prepend(this.noteTitleWidget.render()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         this.noteTreeWidget = new NoteTreeWidget(this); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.widgets = [ | 
					
						
							|  |  |  |             new GlobalButtonsWidget(this), | 
					
						
							|  |  |  |             new SearchBoxWidget(this), | 
					
						
							|  |  |  |             new SearchResultsWidget(this), | 
					
						
							|  |  |  |             this.noteTreeWidget | 
					
						
							|  |  |  |         ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (const widget of this.widgets) { | 
					
						
							|  |  |  |             const $widget = widget.render(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $leftPane.append($widget); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-01-12 23:03:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         this.widgets.push(this.noteTitleWidget); | 
					
						
							| 
									
										
										
										
											2020-01-11 21:19:56 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trigger(name, data) { | 
					
						
							| 
									
										
										
										
											2020-01-12 23:03:55 +01:00
										 |  |  |         this.eventReceived(name, data); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-11 21:19:56 +01:00
										 |  |  |         for (const widget of this.widgets) { | 
					
						
							|  |  |  |             widget.eventReceived(name, data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 23:03:55 +01:00
										 |  |  |     eventReceived(name, data) { | 
					
						
							|  |  |  |         const fun = this[name + 'Listener']; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (typeof fun === 'function') { | 
					
						
							|  |  |  |             fun.call(this, data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  |     /** @return {TabContext[]} */ | 
					
						
							|  |  |  |     getTabContexts() { | 
					
						
							|  |  |  |         return this.tabContexts; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** @returns {TabContext} */ | 
					
						
							|  |  |  |     getActiveTabContext() { | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         const activeTabEl = this.tabRow.activeTabEl; | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (!activeTabEl) { | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const tabId = activeTabEl.getAttribute('data-tab-id'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return this.tabContexts.find(tc => tc.tabId === tabId); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** @returns {string|null} */ | 
					
						
							|  |  |  |     getActiveTabNotePath() { | 
					
						
							|  |  |  |         const activeContext = this.getActiveTabContext(); | 
					
						
							|  |  |  |         return activeContext ? activeContext.notePath : null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** @return {NoteFull} */ | 
					
						
							|  |  |  |     getActiveTabNote() { | 
					
						
							|  |  |  |         const activeContext = this.getActiveTabContext(); | 
					
						
							|  |  |  |         return activeContext ? activeContext.note : null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** @return {string|null} */ | 
					
						
							|  |  |  |     getActiveTabNoteId() { | 
					
						
							|  |  |  |         const activeNote = this.getActiveTabNote(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return activeNote ? activeNote.noteId : null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** @return {string|null} */ | 
					
						
							|  |  |  |     getActiveTabNoteType() { | 
					
						
							|  |  |  |         const activeNote = this.getActiveTabNote(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return activeNote ? activeNote.type : null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async switchToTab(tabId, notePath) { | 
					
						
							|  |  |  |         const tabContext = this.tabContexts.find(tc => tc.tabId === tabId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!tabContext) { | 
					
						
							|  |  |  |             await noteDetailService.loadNoteDetail(notePath, { | 
					
						
							|  |  |  |                 newTab: true, | 
					
						
							|  |  |  |                 activate: true | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             await tabContext.activate(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (notePath && tabContext.notePath !== notePath) { | 
					
						
							|  |  |  |                 await treeService.activateNote(notePath); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async showTab(tabId) { | 
					
						
							|  |  |  |         for (const ctx of this.tabContexts) { | 
					
						
							|  |  |  |             if (ctx.tabId === tabId) { | 
					
						
							|  |  |  |                 await ctx.show(); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 ctx.hide(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const oldActiveNode = this.getMainNoteTree().getActiveNode(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (oldActiveNode) { | 
					
						
							|  |  |  |             oldActiveNode.setActive(false); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const newActiveTabContext = this.getActiveTabContext(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (newActiveTabContext && newActiveTabContext.notePath) { | 
					
						
							|  |  |  |             const newActiveNode = await this.getMainNoteTree().getNodeFromPath(newActiveTabContext.notePath); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (newActiveNode) { | 
					
						
							|  |  |  |                 if (!newActiveNode.isVisible()) { | 
					
						
							|  |  |  |                     await this.getMainNoteTree().expandToNote(newActiveTabContext.notePath); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 newActiveNode.setActive(true, {noEvents: true}); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 11:15:23 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * @return {NoteTreeWidget} | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     getMainNoteTree() { | 
					
						
							|  |  |  |         return this.noteTreeWidget; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     getTab(newTab, state) { | 
					
						
							|  |  |  |         if (!this.getActiveTabContext() || newTab) { | 
					
						
							|  |  |  |             // if it's a new tab explicitly by user then it's in background
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |             const ctx = new TabContext(this.tabRow, state); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  |             this.tabContexts.push(ctx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return ctx; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return this.getActiveTabContext(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async reloadAllTabs() { | 
					
						
							|  |  |  |         for (const tabContext of this.tabContexts) { | 
					
						
							|  |  |  |             await this.reloadTab(tabContext); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async refreshTabs(sourceTabId, noteId) { | 
					
						
							|  |  |  |         for (const tc of this.tabContexts) { | 
					
						
							|  |  |  |             if (tc.noteId === noteId && tc.tabId !== sourceTabId) { | 
					
						
							|  |  |  |                 await this.reloadTab(tc); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async reloadTab(tc) { | 
					
						
							|  |  |  |         if (tc.note) { | 
					
						
							|  |  |  |             noteDetailService.reloadNote(tc); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async openEmptyTab() { | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         const ctx = new TabContext(this.tabRow); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  |         this.tabContexts.push(ctx); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         await this.tabRow.activateTab(ctx.$tab[0]); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     async filterTabs(noteId) { | 
					
						
							|  |  |  |         for (const tc of this.tabContexts) { | 
					
						
							|  |  |  |             if (tc.notePath && !tc.notePath.split("/").includes(noteId)) { | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |                 await this.tabRow.removeTab(tc.$tab[0]); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (this.tabContexts.length === 0) { | 
					
						
							|  |  |  |             this.openEmptyTab() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         await this.saveOpenTabs(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async saveOpenTabs() { | 
					
						
							|  |  |  |         const openTabs = []; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         for (const tabEl of this.tabRow.tabEls) { | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  |             const tabId = tabEl.getAttribute('data-tab-id'); | 
					
						
							|  |  |  |             const tabContext = appContext.getTabContexts().find(tc => tc.tabId === tabId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (tabContext) { | 
					
						
							|  |  |  |                 const tabState = tabContext.getTabState(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (tabState) { | 
					
						
							|  |  |  |                     openTabs.push(tabState); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         await server.put('options', { | 
					
						
							|  |  |  |             openTabs: JSON.stringify(openTabs) | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     clearOpenTabsTask() { | 
					
						
							|  |  |  |         if (this.tabsChangedTaskId) { | 
					
						
							|  |  |  |             clearTimeout(this.tabsChangedTaskId); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     openTabsChanged() { | 
					
						
							|  |  |  |         // we don't want to send too many requests with tab changes so we always schedule task to do this in 1 seconds,
 | 
					
						
							|  |  |  |         // but if there's any change in between, we cancel the old one and schedule new one
 | 
					
						
							|  |  |  |         // so effectively we kind of wait until user stopped e.g. quickly switching tabs
 | 
					
						
							|  |  |  |         this.clearOpenTabsTask(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.tabsChangedTaskId = setTimeout(() => this.saveOpenTabs(), 1000); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-01-12 09:57:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 20:15:05 +01:00
										 |  |  |     async activateTab(tabContext) { | 
					
						
							|  |  |  |         return this.tabRow.activateTab(tabContext.$tab[0]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |     newTabListener() { | 
					
						
							|  |  |  |         this.openEmptyTab(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-01-12 09:57:28 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |     async activeTabChangedListener({tabEl}) { | 
					
						
							|  |  |  |         const tabId = tabEl.getAttribute('data-tab-id'); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         await this.showTab(tabId); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |     async tabRemoveListener({tabEl}) { | 
					
						
							|  |  |  |         const tabId = tabEl.getAttribute('data-tab-id'); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         this.tabContexts.filter(nc => nc.tabId === tabId) | 
					
						
							|  |  |  |             .forEach(tc => tc.remove()); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         this.tabContexts = this.tabContexts.filter(nc => nc.tabId !== tabId); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         if (this.tabContexts.length === 0) { | 
					
						
							|  |  |  |             this.openEmptyTab(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         this.openTabsChanged(); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |     tabReorderListener() { | 
					
						
							|  |  |  |         this.openTabsChanged(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const appContext = new AppContext(); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | keyboardActionService.setGlobalActionHandler('OpenNewTab', () => { | 
					
						
							|  |  |  |     appContext.openEmptyTab(); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | keyboardActionService.setGlobalActionHandler('CloseActiveTab', () => { | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |     if (this.tabRow.activeTabEl) { | 
					
						
							|  |  |  |         this.tabRow.removeTab(this.tabRow.activeTabEl); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | keyboardActionService.setGlobalActionHandler('ActivateNextTab', () => { | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |     const nextTab = this.tabRow.nextTabEl; | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (nextTab) { | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         this.tabRow.activateTab(nextTab); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | keyboardActionService.setGlobalActionHandler('ActivatePreviousTab', () => { | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |     const prevTab = this.tabRow.previousTabEl; | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (prevTab) { | 
					
						
							| 
									
										
										
										
											2020-01-12 19:05:09 +01:00
										 |  |  |         this.tabRow.activateTab(prevTab); | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 09:57:28 +01:00
										 |  |  | export default appContext; |