| 
									
										
										
										
											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 tabRow from "./tab_row.js"; | 
					
						
							|  |  |  | 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"; | 
					
						
							|  |  |  | import contextMenuService from "./context_menu.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-11 21:19:56 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trigger(name, data) { | 
					
						
							|  |  |  |         for (const widget of this.widgets) { | 
					
						
							|  |  |  |             widget.eventReceived(name, data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  |     /** @return {TabContext[]} */ | 
					
						
							|  |  |  |     getTabContexts() { | 
					
						
							|  |  |  |         return this.tabContexts; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** @returns {TabContext} */ | 
					
						
							|  |  |  |     getActiveTabContext() { | 
					
						
							|  |  |  |         const activeTabEl = tabRow.activeTabEl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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-11 21:19:56 +01:00
										 |  |  |     showWidgets() { | 
					
						
							|  |  |  |         const $leftPane = $("#left-pane"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 11:15:23 +01:00
										 |  |  |         this.noteTreeWidget = new NoteTreeWidget(this); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-11 21:19:56 +01:00
										 |  |  |         this.widgets = [ | 
					
						
							|  |  |  |             new GlobalButtonsWidget(this), | 
					
						
							|  |  |  |             new SearchBoxWidget(this), | 
					
						
							|  |  |  |             new SearchResultsWidget(this), | 
					
						
							| 
									
										
										
										
											2020-01-12 11:15:23 +01:00
										 |  |  |             this.noteTreeWidget | 
					
						
							| 
									
										
										
										
											2020-01-11 21:19:56 +01:00
										 |  |  |         ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (const widget of this.widgets) { | 
					
						
							|  |  |  |             const $widget = widget.render(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $leftPane.append($widget); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							|  |  |  |             const ctx = new TabContext(tabRow, state); | 
					
						
							|  |  |  |             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() { | 
					
						
							|  |  |  |         const ctx = new TabContext(tabRow); | 
					
						
							|  |  |  |         this.tabContexts.push(ctx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         await tabRow.activateTab(ctx.$tab[0]); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											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)) { | 
					
						
							|  |  |  |                 await tabRow.removeTab(tc.$tab[0]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (this.tabContexts.length === 0) { | 
					
						
							|  |  |  |             this.openEmptyTab() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         await this.saveOpenTabs(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async saveOpenTabs() { | 
					
						
							|  |  |  |         const openTabs = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (const tabEl of tabRow.tabEls) { | 
					
						
							|  |  |  |             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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const appContext = new AppContext(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 12:30:30 +01:00
										 |  |  | tabRow.addListener('newTab', () => appContext.openEmptyTab()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | tabRow.addListener('activeTabChange', async ({ detail }) => { | 
					
						
							|  |  |  |     const tabId = detail.tabEl.getAttribute('data-tab-id'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     await appContext.showTab(tabId); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | tabRow.addListener('tabRemove', async ({ detail }) => { | 
					
						
							|  |  |  |     const tabId = detail.tabEl.getAttribute('data-tab-id'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     appContext.tabContexts.filter(nc => nc.tabId === tabId) | 
					
						
							|  |  |  |         .forEach(tc => tc.remove()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     appContext.tabContexts = appContext.tabContexts.filter(nc => nc.tabId !== tabId); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (appContext.tabContexts.length === 0) { | 
					
						
							|  |  |  |         appContext.openEmptyTab(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 12:48:17 +01:00
										 |  |  | tabRow.addListener('activeTabChange', () => appContext.openTabsChanged()); | 
					
						
							|  |  |  | tabRow.addListener('tabRemove', () => appContext.openTabsChanged()); | 
					
						
							|  |  |  | tabRow.addListener('tabReorder', () => appContext.openTabsChanged()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | keyboardActionService.setGlobalActionHandler('OpenNewTab', () => { | 
					
						
							|  |  |  |     appContext.openEmptyTab(); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | keyboardActionService.setGlobalActionHandler('CloseActiveTab', () => { | 
					
						
							|  |  |  |     if (tabRow.activeTabEl) { | 
					
						
							|  |  |  |         tabRow.removeTab(tabRow.activeTabEl); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | keyboardActionService.setGlobalActionHandler('ActivateNextTab', () => { | 
					
						
							|  |  |  |     const nextTab = tabRow.nextTabEl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (nextTab) { | 
					
						
							|  |  |  |         tabRow.activateTab(nextTab); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | keyboardActionService.setGlobalActionHandler('ActivatePreviousTab', () => { | 
					
						
							|  |  |  |     const prevTab = tabRow.previousTabEl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (prevTab) { | 
					
						
							|  |  |  |         tabRow.activateTab(prevTab); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | $(tabRow.el).on('contextmenu', '.note-tab', e => { | 
					
						
							|  |  |  |     e.preventDefault(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const tab = $(e.target).closest(".note-tab"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     contextMenuService.initContextMenu(e, { | 
					
						
							|  |  |  |         getContextMenuItems: () => { | 
					
						
							|  |  |  |             return [ | 
					
						
							|  |  |  |                 {title: "Close all tabs", cmd: "removeAllTabs", uiIcon: "empty"}, | 
					
						
							|  |  |  |                 {title: "Close all tabs except for this", cmd: "removeAllTabsExceptForThis", uiIcon: "empty"} | 
					
						
							|  |  |  |             ]; | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         selectContextMenuItem: (e, cmd) => { | 
					
						
							|  |  |  |             if (cmd === 'removeAllTabs') { | 
					
						
							|  |  |  |                 tabRow.removeAllTabs(); | 
					
						
							|  |  |  |             } else if (cmd === 'removeAllTabsExceptForThis') { | 
					
						
							|  |  |  |                 tabRow.removeAllTabsExceptForThis(tab[0]); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 09:57:28 +01:00
										 |  |  | export default appContext; |