middle click can now open links in new tab (and close tab)

This commit is contained in:
zadam 2019-05-15 21:50:27 +02:00
parent dd1fc23fe8
commit f22cc37df7
10 changed files with 1411 additions and 956 deletions

File diff suppressed because it is too large Load diff

View file

@ -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,
@ -1315,7 +1308,7 @@ function makeAggregateRaf() {
var t = backBuffer;
backBuffer = frontBuffer;
frontBuffer = t;
frontBuffer = t;
frontBuffer.forEach(function(callback) {
callback();
@ -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 ) {

View file

@ -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
};

View file

@ -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,

View file

@ -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]);
}
}

View file

@ -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");

View file

@ -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) {

View file

@ -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());

View file

@ -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 {

View file

@ -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 */