mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	middle click can now open links in new tab (and close tab)
This commit is contained in:
		
							
								
								
									
										2045
									
								
								libraries/jsplumb.js
									
									
									
									
									
								
							
							
						
						
									
										2045
									
								
								libraries/jsplumb.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -33,11 +33,11 @@ function createPanZoom(domElement, options) { | ||||
|  | ||||
|   if (!panController) { | ||||
|     if (domElement instanceof SVGElement) { | ||||
|       panController = makeSvgController(domElement) | ||||
|       panController = makeSvgController(domElement, options) | ||||
|     } | ||||
|  | ||||
|     if (domElement instanceof HTMLElement) { | ||||
|       panController = makeDomController(domElement) | ||||
|       panController = makeDomController(domElement, options) | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -57,7 +57,8 @@ function createPanZoom(domElement, options) { | ||||
|   } | ||||
|  | ||||
|   var filterKey = typeof options.filterKey === 'function' ? options.filterKey : noop; | ||||
|   var realPinch = typeof options.realPinch === 'boolean' ? options.realPinch : false | ||||
|   // TODO: likely need to unite pinchSpeed with zoomSpeed | ||||
|   var pinchSpeed = typeof options.pinchSpeed === 'number' ? options.pinchSpeed : 1; | ||||
|   var bounds = options.bounds | ||||
|   var maxZoom = typeof options.maxZoom === 'number' ? options.maxZoom : Number.POSITIVE_INFINITY | ||||
|   var minZoom = typeof options.minZoom === 'number' ? options.minZoom : 0 | ||||
| @@ -101,7 +102,7 @@ function createPanZoom(domElement, options) { | ||||
|   var moveByAnimation | ||||
|   var zoomToAnimation | ||||
|  | ||||
|   var multitouch | ||||
|   var multiTouch | ||||
|   var paused = false | ||||
|  | ||||
|   listenForEvents() | ||||
| @@ -144,7 +145,8 @@ function createPanZoom(domElement, options) { | ||||
|  | ||||
|   function showRectangle(rect) { | ||||
|     // TODO: this duplicates autocenter. I think autocenter should go. | ||||
|     var size = transformToScreen(owner.clientWidth, owner.clientHeight) | ||||
|     var clientRect = owner.getBoundingClientRect() | ||||
|     var size = transformToScreen(clientRect.width, clientRect.height) | ||||
|  | ||||
|     var rectWidth = rect.right - rect.left | ||||
|     var rectHeight = rect.bottom - rect.top | ||||
| @@ -503,7 +505,7 @@ function createPanZoom(domElement, options) { | ||||
|     } else if (e.touches.length === 2) { | ||||
|       // handleTouchMove() will care about pinch zoom. | ||||
|       pinchZoomLength = getPinchZoomLength(e.touches[0], e.touches[1]) | ||||
|       multitouch  = true | ||||
|       multiTouch  = true | ||||
|       startTouchListenerIfNeeded() | ||||
|     } | ||||
|   } | ||||
| @@ -568,25 +570,14 @@ function createPanZoom(domElement, options) { | ||||
|       internalMoveBy(point.x, point.y) | ||||
|     } else if (e.touches.length === 2) { | ||||
|       // it's a zoom, let's find direction | ||||
|       multitouch = true | ||||
|       multiTouch = true | ||||
|       var t1 = e.touches[0] | ||||
|       var t2 = e.touches[1] | ||||
|       var currentPinchLength = getPinchZoomLength(t1, t2) | ||||
|  | ||||
|       var scaleMultiplier = 1 | ||||
|  | ||||
|       if (realPinch) { | ||||
|         scaleMultiplier = currentPinchLength / pinchZoomLength | ||||
|       } else { | ||||
|         var delta = 0 | ||||
|         if (currentPinchLength < pinchZoomLength) { | ||||
|           delta = 1 | ||||
|         } else if (currentPinchLength > pinchZoomLength) { | ||||
|           delta = -1 | ||||
|         } | ||||
|  | ||||
|         scaleMultiplier = getScaleMultiplier(delta) | ||||
|       } | ||||
|       // since the zoom speed is always based on distance from 1, we need to apply | ||||
|       // pinch speed only on that distance from 1: | ||||
|       var scaleMultiplier = 1 + (currentPinchLength / pinchZoomLength - 1) * pinchSpeed | ||||
|  | ||||
|       mouseX = (t1.clientX + t2.clientX)/2 | ||||
|       mouseY = (t1.clientY + t2.clientY)/2 | ||||
| @@ -619,8 +610,9 @@ function createPanZoom(domElement, options) { | ||||
|   } | ||||
|  | ||||
|   function getPinchZoomLength(finger1, finger2) { | ||||
|     return Math.sqrt((finger1.clientX - finger2.clientX) * (finger1.clientX - finger2.clientX) + | ||||
|       (finger1.clientY - finger2.clientY) * (finger1.clientY - finger2.clientY)) | ||||
|     var dx = finger1.clientX - finger2.clientX | ||||
|     var dy = finger1.clientY - finger2.clientY | ||||
|     return Math.sqrt(dx * dx + dy * dy) | ||||
|   } | ||||
|  | ||||
|   function onDoubleClick(e) { | ||||
| @@ -630,12 +622,6 @@ function createPanZoom(domElement, options) { | ||||
|   } | ||||
|  | ||||
|   function onMouseDown(e) { | ||||
|       if (options.onMouseDown && !options.onMouseDown(e)) { | ||||
|           // if they return `false` from onTouch, we don't want to stop | ||||
|           // events propagation. Fixes https://github.com/anvaka/panzoom/issues/46 | ||||
|           return | ||||
|       } | ||||
|  | ||||
|     if (touchInProgress) { | ||||
|       // modern browsers will fire mousedown for touch events too | ||||
|       // we do not want this: touch is handled separately. | ||||
| @@ -698,7 +684,7 @@ function createPanZoom(domElement, options) { | ||||
|     document.removeEventListener('touchend', handleTouchEnd) | ||||
|     document.removeEventListener('touchcancel', handleTouchEnd) | ||||
|     panstartFired = false | ||||
|     multitouch = false | ||||
|     multiTouch = false | ||||
|   } | ||||
|  | ||||
|   function onMouseWheel(e) { | ||||
| @@ -775,8 +761,8 @@ function createPanZoom(domElement, options) { | ||||
|  | ||||
|   function triggerPanEnd() { | ||||
|     if (panstartFired) { | ||||
|       // we should never run smooth scrolling if it was multitouch (pinch zoom animation): | ||||
|       if (!multitouch) smoothScroll.stop() | ||||
|       // we should never run smooth scrolling if it was multiTouch (pinch zoom animation): | ||||
|       if (!multiTouch) smoothScroll.stop() | ||||
|       triggerEvent('panend') | ||||
|     } | ||||
|   } | ||||
| @@ -828,11 +814,13 @@ function autoRun() { | ||||
|   if (!scripts) return; | ||||
|   var panzoomScript; | ||||
|  | ||||
|   Array.from(scripts).forEach(function(x) { | ||||
|   for (var i = 0; i < scripts.length; ++i) { | ||||
|     var x = scripts[i]; | ||||
|     if (x.src && x.src.match(/\bpanzoom(\.min)?\.js/)) { | ||||
|       panzoomScript = x | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   }) | ||||
|  | ||||
|   if (!panzoomScript) return; | ||||
|  | ||||
| @@ -894,7 +882,7 @@ autoRun(); | ||||
| },{"./lib/domController.js":2,"./lib/kinetic.js":3,"./lib/svgController.js":4,"./lib/textSelectionInterceptor.js":5,"./lib/transform.js":6,"amator":7,"ngraph.events":9,"wheel":10}],2:[function(require,module,exports){ | ||||
| module.exports = makeDomController | ||||
|  | ||||
| function makeDomController(domElement) { | ||||
| function makeDomController(domElement, options) { | ||||
|   var elementValid = (domElement instanceof HTMLElement) | ||||
|   if (!elementValid) { | ||||
|     throw new Error('svg element is required for svg.panzoom to work') | ||||
| @@ -908,7 +896,10 @@ function makeDomController(domElement) { | ||||
|   } | ||||
|  | ||||
|   domElement.scrollTop = 0; | ||||
|   owner.setAttribute('tabindex', 1); // TODO: not sure if this is really polite | ||||
|    | ||||
|   if (!options.disableKeyboardInteraction) { | ||||
|     owner.setAttribute('tabindex', 0); | ||||
|   } | ||||
|  | ||||
|   var api = { | ||||
|     getBBox: getBBox, | ||||
| @@ -1067,7 +1058,7 @@ function kinetic(getPoint, scroll, settings) { | ||||
| },{}],4:[function(require,module,exports){ | ||||
| module.exports = makeSvgController | ||||
|  | ||||
| function makeSvgController(svgElement) { | ||||
| function makeSvgController(svgElement, options) { | ||||
|   var elementValid = (svgElement instanceof SVGElement) | ||||
|   if (!elementValid) { | ||||
|     throw new Error('svg element is required for svg.panzoom to work') | ||||
| @@ -1081,7 +1072,9 @@ function makeSvgController(svgElement) { | ||||
|       'As of March 2016 only FireFox supported transform on the root element') | ||||
|   } | ||||
|  | ||||
|   owner.setAttribute('tabindex', 1); // TODO: not sure if this is really polite | ||||
|   if (!options.disableKeyboardInteraction) { | ||||
|     owner.setAttribute('tabindex', 0); | ||||
|   } | ||||
|  | ||||
|   var api = { | ||||
|     getBBox: getBBox, | ||||
| @@ -1578,7 +1571,7 @@ function removeWheelListener( elem, callback, useCapture ) { | ||||
|   // unsubscription in some browsers. But in practice, I don't think we should | ||||
|   // worry too much about it (those browsers are on the way out) | ||||
| function _addWheelListener( elem, eventName, callback, useCapture ) { | ||||
|   elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) { | ||||
|   elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function(originalEvent ) { | ||||
|     !originalEvent && ( originalEvent = window.event ); | ||||
|  | ||||
|     // create a normalized event object | ||||
| @@ -1620,7 +1613,10 @@ function _addWheelListener( elem, eventName, callback, useCapture ) { | ||||
|     // it's time to fire the callback | ||||
|     return callback( event ); | ||||
|  | ||||
|   }, useCapture || false ); | ||||
|   }, { | ||||
|     capture: useCapture || false , | ||||
|     passive: false | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function _removeWheelListener( elem, eventName, callback, useCapture ) { | ||||
|   | ||||
| @@ -43,18 +43,19 @@ function getNotePathFromLink($link) { | ||||
| } | ||||
|  | ||||
| function goToLink(e) { | ||||
|     e.preventDefault(); | ||||
|  | ||||
|     const $link = $(e.target); | ||||
|  | ||||
|     const notePath = getNotePathFromLink($link); | ||||
|  | ||||
|     if (notePath) { | ||||
|         if (e.ctrlKey) { | ||||
|             noteDetailService.loadNoteDetail(notePath.split("/").pop(), { newTab: true }); | ||||
|         if ((e.which === 1 && e.ctrlKey) || e.which === 2) { | ||||
|             noteDetailService.openInTab(notePath); | ||||
|         } | ||||
|         else if (e.which === 1) { | ||||
|             treeService.activateNote(notePath); | ||||
|         } | ||||
|         else { | ||||
|             treeService.activateNote(notePath); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     else { | ||||
| @@ -64,6 +65,11 @@ function goToLink(e) { | ||||
|             window.open(address, '_blank'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     e.preventDefault(); | ||||
|     e.stopPropagation(); | ||||
|  | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| function addLinkToEditor(linkTitle, linkHref) { | ||||
| @@ -129,22 +135,24 @@ $(document).on('contextmenu', ".note-detail-render a", tabContextMenu); | ||||
|  | ||||
| // when click on link popup, in case of internal link, just go the the referenced note instead of default behavior | ||||
| // of opening the link in new window/tab | ||||
| $(document).on('click', "a[data-action='note']", goToLink); | ||||
| $(document).on('click', 'div.popover-content a, div.ui-tooltip-content a', goToLink); | ||||
| $(document).on('mousedown', "a[data-action='note']", goToLink); | ||||
| $(document).on('mousedown', 'div.popover-content a, div.ui-tooltip-content a', goToLink); | ||||
| $(document).on('dblclick', '.note-detail-text a', goToLink); | ||||
| $(document).on('click', '.note-detail-text a', function (e) { | ||||
| $(document).on('mousedown', '.note-detail-text a', function (e) { | ||||
|     const notePath = getNotePathFromLink($(e.target)); | ||||
|     if (notePath && e.ctrlKey) { | ||||
|     if (notePath && ((e.which === 1 && e.ctrlKey) || e.which === 2)) { | ||||
|         // if it's a ctrl-click, then we open on new tab, otherwise normal flow (CKEditor opens link-editing dialog) | ||||
|         e.preventDefault(); | ||||
|  | ||||
|         noteDetailService.loadNoteDetail(notePath.split("/").pop(), { newTab: true }); | ||||
|         noteDetailService.loadNoteDetail(notePath, { newTab: true }); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| $(document).on('click', '.note-detail-render a', goToLink); | ||||
| $(document).on('click', '.note-detail-text.ck-read-only a', goToLink); | ||||
| $(document).on('click', 'span.ck-button__label', e => { | ||||
| $(document).on('mousedown', '.note-detail-render a', goToLink); | ||||
| $(document).on('mousedown', '.note-detail-text.ck-read-only a', goToLink); | ||||
| $(document).on('mousedown', 'span.ck-button__label', e => { | ||||
|     // this is a link preview dialog from CKEditor link editing | ||||
|     // for some reason clicked element is span | ||||
|  | ||||
| @@ -163,5 +171,6 @@ export default { | ||||
|     createNoteLink, | ||||
|     addLinkToEditor, | ||||
|     addTextToEditor, | ||||
|     init | ||||
|     init, | ||||
|     goToLink | ||||
| }; | ||||
| @@ -46,7 +46,8 @@ function initNoteAutocomplete($el, options) { | ||||
|                 .prop("title", "Show recent notes"); | ||||
|  | ||||
|         const $goToSelectedNoteButton = $("<a>") | ||||
|             .addClass("input-group-text go-to-selected-note-button jam jam-arrow-right"); | ||||
|             .addClass("input-group-text go-to-selected-note-button jam jam-arrow-right") | ||||
|             .attr("data-action", "note"); | ||||
|  | ||||
|         const $sideButtons = $("<div>") | ||||
|             .addClass("input-group-append") | ||||
| @@ -69,14 +70,6 @@ function initNoteAutocomplete($el, options) { | ||||
|             return false; | ||||
|         }); | ||||
|  | ||||
|         $goToSelectedNoteButton.click(() => { | ||||
|             if ($el.hasClass("disabled")) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             treeService.activateNote($el.getSelectedPath()); | ||||
|         }); | ||||
|  | ||||
|         $el.autocomplete({ | ||||
|             appendTo: document.querySelector('body'), | ||||
|             hint: false, | ||||
|   | ||||
| @@ -372,11 +372,14 @@ $(tabRow.el).on('contextmenu', '.note-tab', e => { | ||||
|     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 === 'removeAllTabsExceptForThis') { | ||||
|             if (cmd === 'removeAllTabs') { | ||||
|                 tabRow.removeAllTabs(); | ||||
|             } else if (cmd === 'removeAllTabsExceptForThis') { | ||||
|                 tabRow.removeAllTabsExceptForThis(tab[0]); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -90,6 +90,7 @@ class NoteDetailRelationMap { | ||||
|             contextMenuWidget.initContextMenu(e, { | ||||
|                 getContextMenuItems: () => { | ||||
|                     return [ | ||||
|                         {title: "Open in new tab", cmd: "open-in-new-tab", uiIcon: "empty"}, | ||||
|                         {title: "Remove note", cmd: "remove", uiIcon: "trash"}, | ||||
|                         {title: "Edit title", cmd: "edit-title", uiIcon: "pencil"}, | ||||
|                     ]; | ||||
| @@ -125,7 +126,7 @@ class NoteDetailRelationMap { | ||||
|  | ||||
|         this.$resetPanZoomButton.click(() => { | ||||
|             // reset to initial pan & zoom state | ||||
|             this.pzInstance.zoomTo(0, 0, 1 / getZoom()); | ||||
|             this.pzInstance.zoomTo(0, 0, 1 / this.getZoom()); | ||||
|             this.pzInstance.moveTo(0, 0); | ||||
|         }); | ||||
|  | ||||
| @@ -138,7 +139,10 @@ class NoteDetailRelationMap { | ||||
|         const $title = $noteBox.find(".title a"); | ||||
|         const noteId = this.idToNoteId($noteBox.prop("id")); | ||||
|  | ||||
|         if (cmd === "remove") { | ||||
|         if (cmd === "open-in-new-tab") { | ||||
|             noteDetailService.openInTab(noteId); | ||||
|         } | ||||
|         else if (cmd === "remove") { | ||||
|             if (!await confirmDialog.confirmDeleteNoteBoxWithNote($title.text())) { | ||||
|                 return; | ||||
|             } | ||||
| @@ -310,7 +314,7 @@ class NoteDetailRelationMap { | ||||
|             maxZoom: 2, | ||||
|             minZoom: 0.3, | ||||
|             smoothScroll: false, | ||||
|             onMouseDown: function(event) { | ||||
|             onMouseDown: event => { | ||||
|                 if (this.clipboard) { | ||||
|                     let {x, y} = this.getMousePosition(event); | ||||
|  | ||||
| @@ -402,9 +406,6 @@ class NoteDetailRelationMap { | ||||
|         this.jsPlumbInstance.registerConnectionType("link", { anchor:"Continuous", connector:"StateMachine", overlays: linkOverlays }); | ||||
|  | ||||
|         this.jsPlumbInstance.bind("connection", (info, originalEvent) => this.connectionCreatedHandler(info, originalEvent)); | ||||
|  | ||||
|         // so that canvas is not panned when clicking/dragging note box | ||||
|         this.$relationMapContainer.on('mousedown touchstart', '.note-box, .connection-label', e => e.stopPropagation()); | ||||
|     } | ||||
|  | ||||
|     async connectionCreatedHandler(info, originalEvent) { | ||||
| @@ -490,10 +491,17 @@ class NoteDetailRelationMap { | ||||
|     } | ||||
|  | ||||
|     async createNoteBox(noteId, title, x, y) { | ||||
|         const $link = await linkService.createNoteLink(noteId, title); | ||||
|         $link.mousedown(e => { | ||||
|             console.log(e); | ||||
|  | ||||
|             linkService.goToLink(e); | ||||
|         }); | ||||
|  | ||||
|         const $noteBox = $("<div>") | ||||
|             .addClass("note-box") | ||||
|             .prop("id", this.noteIdToId(noteId)) | ||||
|             .append($("<span>").addClass("title").html(await linkService.createNoteLink(noteId, title))) | ||||
|             .append($("<span>").addClass("title").append($link)) | ||||
|             .append($("<div>").addClass("endpoint").attr("title", "Start dragging relations from here and drop them on another note.")) | ||||
|             .css("left", x + "px") | ||||
|             .css("top", y + "px"); | ||||
|   | ||||
| @@ -177,6 +177,14 @@ class TabRow { | ||||
|  | ||||
|     setTabCloseEventListener(tabEl) { | ||||
|         tabEl.querySelector('.note-tab-close').addEventListener('click', _ => this.removeTab(tabEl)); | ||||
|  | ||||
|         tabEl.addEventListener('mousedown', e => { | ||||
|             if (e.which === 2) { | ||||
|                 this.removeTab(tabEl); | ||||
|  | ||||
|                 return true; // event has been handled | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     get activeTabEl() { | ||||
| @@ -251,6 +259,12 @@ class TabRow { | ||||
|         this.setVisibility(); | ||||
|     } | ||||
|  | ||||
|     async removeAllTabs() { | ||||
|         for (const tabEl of this.tabEls) { | ||||
|             await this.removeTab(tabEl); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async removeAllTabsExceptForThis(remainingTabEl) { | ||||
|         for (const tabEl of this.tabEls) { | ||||
|             if (remainingTabEl !== tabEl) { | ||||
|   | ||||
| @@ -852,6 +852,22 @@ $(window).bind('hashchange', async function() { | ||||
|     } | ||||
| }); | ||||
|  | ||||
| // fancytree doesn't support middle click so this is a way to support it | ||||
| $tree.on('mousedown', '.fancytree-title', e => { | ||||
|     if (e.which === 2) { | ||||
|         const node = $.ui.fancytree.getNode(e); | ||||
|  | ||||
|         treeUtils.getNotePath(node).then(notePath => { | ||||
|             if (notePath) { | ||||
|                 noteDetailService.openInTab(notePath); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         e.stopPropagation(); | ||||
|         e.preventDefault(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| utils.bindShortcut('alt+c', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument | ||||
| $collapseTreeButton.click(() => collapseTree()); | ||||
|  | ||||
|   | ||||
| @@ -93,6 +93,7 @@ body { | ||||
|  | ||||
| #context-menu-container, #context-menu-container .dropdown-menu { | ||||
|     padding: 3px 0 0; | ||||
|     z-index: 1111; | ||||
| } | ||||
|  | ||||
| #context-menu-container .dropdown-item { | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| #note-detail-relation-map { | ||||
| .note-detail-relation-map { | ||||
|     height: 100%; | ||||
|     overflow: hidden !important; | ||||
| } | ||||
|  | ||||
| #relation-map-wrapper { | ||||
| .relation-map-wrapper { | ||||
|     position: relative; | ||||
|     height: 100%; | ||||
|     outline: none; /* remove dotted outline on click */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user