mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	zoom icons
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/zoom-in-24.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/public/images/icons/zoom-in-24.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 455 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/zoom-out-24.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/public/images/icons/zoom-out-24.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 455 B | 
| @@ -6,6 +6,8 @@ import libraryLoader from "./library_loader.js"; | ||||
| const $noteDetailRelationMap = $("#note-detail-relation-map"); | ||||
| const $relationMapCanvas = $("#relation-map-canvas"); | ||||
| const $addChildNotesButton = $("#relation-map-add-child-notes"); | ||||
| const $zoomInButton = $("#relation-map-zoom-in"); | ||||
| const $zoomOutButton = $("#relation-map-zoom-out"); | ||||
|  | ||||
| let mapData; | ||||
| let instance; | ||||
| @@ -87,25 +89,10 @@ async function loadNotesAndRelations() { | ||||
|     mapData.notes = mapData.notes.filter(note => note.id in data.noteTitles); | ||||
|  | ||||
|     instance.batch(async function () { | ||||
|         const maxY = mapData.notes.filter(note => !!note.y).map(note => note.y).reduce((a, b) => Math.max(a, b), 0); | ||||
|         let curX = 100; | ||||
|         let curY = maxY + 200; | ||||
|  | ||||
|         for (const note of mapData.notes) { | ||||
|             const title = data.noteTitles[note.id]; | ||||
|  | ||||
|             if (note.x && note.y) { | ||||
|             await createNoteBox(note.id, title, note.x, note.y); | ||||
|             } else { | ||||
|                 await createNoteBox(note.id, title, curX, curY); | ||||
|  | ||||
|                 if (curX > 1000) { | ||||
|                     curX = 100; | ||||
|                     curY += 200; | ||||
|                 } else { | ||||
|                     curX += 200; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for (const relation of relations) { | ||||
| @@ -134,19 +121,37 @@ function initPanZoom() { | ||||
|     }); | ||||
|  | ||||
|     if (mapData.transform) { | ||||
|         console.log(mapData.transform); | ||||
|  | ||||
|         pz.moveTo(mapData.transform.x, mapData.transform.y); | ||||
|         pz.zoomTo(0, 0, mapData.transform.scale); | ||||
|     } | ||||
|  | ||||
|     $relationMapCanvas[0].addEventListener('zoom', function (e) { | ||||
|     pz.on('zoom', function (e) { | ||||
|         mapData.transform = pz.getTransform(); | ||||
|  | ||||
|         console.log(mapData.transform); | ||||
|  | ||||
|         saveData(); | ||||
|     }); | ||||
|  | ||||
|     $relationMapCanvas[0].addEventListener('panend', function (e) { | ||||
|     pz.on('panend', function (e) { | ||||
|         mapData.transform = pz.getTransform(); | ||||
|  | ||||
|         saveData(); | ||||
|     }, true); | ||||
|  | ||||
|     $zoomInButton.click(() => { | ||||
|         const transform = pz.getTransform(); | ||||
|  | ||||
|         pz.zoomTo(0, 0, 1.2); | ||||
|     }); | ||||
|  | ||||
|     $zoomOutButton.click(() => { | ||||
|         const transform = pz.getTransform(); | ||||
|  | ||||
|         pz.zoomTo(0, 0, 0.8); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| async function initJsPlumb () { | ||||
| @@ -206,6 +211,7 @@ async function connectionCreatedHandler(info, originalEvent) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const connection = info.connection; | ||||
|     const name = prompt("Specify new relation name:"); | ||||
|  | ||||
|     if (!name || !name.trim()) { | ||||
| @@ -214,8 +220,6 @@ async function connectionCreatedHandler(info, originalEvent) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const connection = info.connection; | ||||
|  | ||||
|     const targetNoteId = connection.target.id; | ||||
|     const sourceNoteId = connection.source.id; | ||||
|  | ||||
| @@ -300,7 +304,7 @@ async function createNoteBox(id, title, x, y) { | ||||
|         .addClass("note-box") | ||||
|         .prop("id", id) | ||||
|         .append($("<span>").addClass("title").html(await linkService.createNoteLink(id, title))) | ||||
|         .append($("<div>").addClass("endpoint")) | ||||
|         .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"); | ||||
|  | ||||
| @@ -340,12 +344,16 @@ async function createNoteBox(id, title, x, y) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function getFreePosition() { | ||||
|     const maxY = mapData.notes.filter(note => !!note.y).map(note => note.y).reduce((a, b) => Math.max(a, b), 0); | ||||
|  | ||||
|     return [100, maxY + 200]; | ||||
| } | ||||
|  | ||||
| $addChildNotesButton.click(async () => { | ||||
|     const children = await server.get("notes/" + noteDetailService.getCurrentNoteId() + "/children"); | ||||
|  | ||||
|     const maxY = mapData.notes.filter(note => !!note.y).map(note => note.y).reduce((a, b) => Math.max(a, b), 0); | ||||
|     let curX = 100; | ||||
|     let curY = maxY + 200; | ||||
|     let [curX, curY] = getFreePosition(); | ||||
|  | ||||
|     for (const child of children) { | ||||
|         if (mapData.notes.some(note => note.id === child.noteId)) { | ||||
| @@ -353,11 +361,11 @@ $addChildNotesButton.click(async () => { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         const note = { id: child.noteId }; | ||||
|  | ||||
|         mapData.notes.push(note); | ||||
|  | ||||
|         await createNoteBox(note.id, note.title, curX, curY); | ||||
|         mapData.notes.push({ | ||||
|             id: child.noteId, | ||||
|             x: curX, | ||||
|             y: curY | ||||
|         }); | ||||
|  | ||||
|         if (curX > 1000) { | ||||
|             curX = 100; | ||||
| @@ -368,25 +376,12 @@ $addChildNotesButton.click(async () => { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     for (const child of children) { | ||||
|         for (const relation of child.relations) { | ||||
|             const connection = instance.connect({ | ||||
|                 id: relation.attributeId, | ||||
|                 source: child.noteId, | ||||
|                 target: relation.targetNoteId, | ||||
|                 type: "basic" | ||||
|             }); | ||||
|  | ||||
|             if (!connection) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             connection.getOverlay("label").setLabel(relation.name); | ||||
|             connection.canvas.setAttribute("data-connection-id", connection.id); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     saveData(); | ||||
|  | ||||
|     // delete all endpoints and connections | ||||
|     instance.deleteEveryEndpoint(); | ||||
|  | ||||
|     await loadNotesAndRelations(); | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|   | ||||
| @@ -1,50 +1,50 @@ | ||||
| (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.panzoom = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||||
|         'use strict'; | ||||
| (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.panzoom = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | ||||
| 'use strict'; | ||||
|  | ||||
|         /* globals SVGElement */ | ||||
|         /** | ||||
| /* globals SVGElement */ | ||||
| /** | ||||
|  * Allows to drag and zoom svg elements | ||||
|  */ | ||||
|         var wheel = require('wheel') | ||||
|         var animate = require('amator') | ||||
|         var kinetic = require('./lib/kinetic.js') | ||||
|         var createEvent = require('./lib/createEvent.js') | ||||
|         var preventTextSelection = require('./lib/textSelectionInterceptor.js')() | ||||
|         var Transform = require('./lib/transform.js'); | ||||
|         var makeSvgController = require('./lib/svgController.js') | ||||
|         var makeDomController = require('./lib/domController.js') | ||||
| var wheel = require('wheel') | ||||
| var animate = require('amator') | ||||
| var eventify = require('ngraph.events'); | ||||
| var kinetic = require('./lib/kinetic.js') | ||||
| var preventTextSelection = require('./lib/textSelectionInterceptor.js')() | ||||
| var Transform = require('./lib/transform.js'); | ||||
| var makeSvgController = require('./lib/svgController.js') | ||||
| var makeDomController = require('./lib/domController.js') | ||||
|  | ||||
|         var defaultZoomSpeed = 0.065 | ||||
|         var defaultDoubleTapZoomSpeed = 1.75 | ||||
|         var doubleTapSpeedInMS = 300 | ||||
| var defaultZoomSpeed = 0.065 | ||||
| var defaultDoubleTapZoomSpeed = 1.75 | ||||
| var doubleTapSpeedInMS = 300 | ||||
|  | ||||
|         module.exports = createPanZoom | ||||
| module.exports = createPanZoom | ||||
|  | ||||
|         /** | ||||
| /** | ||||
|  * Creates a new instance of panzoom, so that an object can be panned and zoomed | ||||
|  * | ||||
|  * @param {DOMElement} domElement where panzoom should be attached. | ||||
|  * @param {Object} options that configure behavior. | ||||
|  */ | ||||
|         function createPanZoom(domElement, options) { | ||||
| function createPanZoom(domElement, options) { | ||||
|   options = options || {} | ||||
|  | ||||
|             var domController = options.controller | ||||
|   var panController = options.controller | ||||
|  | ||||
|             if (!domController) { | ||||
|   if (!panController) { | ||||
|     if (domElement instanceof SVGElement) { | ||||
|                     domController = makeSvgController(domElement) | ||||
|       panController = makeSvgController(domElement) | ||||
|     } | ||||
|  | ||||
|     if (domElement instanceof HTMLElement) { | ||||
|                     domController = makeDomController(domElement) | ||||
|       panController = makeDomController(domElement) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|             if (!domController) { | ||||
|   if (!panController) { | ||||
|     throw new Error('Cannot create panzoom for the current type of dom element') | ||||
|   } | ||||
|             var owner = domController.getOwner() | ||||
|   var owner = panController.getOwner() | ||||
|   // just to avoid GC pressure, every time we do intermediate transform | ||||
|   // we return this object. For internal use only. Never give it back to the consumer of this library | ||||
|   var storedCTMResult = {x: 0, y: 0} | ||||
| @@ -52,8 +52,8 @@ | ||||
|   var isDirty = false | ||||
|   var transform = new Transform() | ||||
|  | ||||
|             if (domController.initTransform) { | ||||
|                 domController.initTransform(transform) | ||||
|   if (panController.initTransform) { | ||||
|     panController.initTransform(transform) | ||||
|   } | ||||
|  | ||||
|   var filterKey = typeof options.filterKey === 'function' ? options.filterKey : noop; | ||||
| @@ -106,7 +106,7 @@ | ||||
|  | ||||
|   listenForEvents() | ||||
|  | ||||
|             return { | ||||
|   var api = { | ||||
|     dispose: dispose, | ||||
|     moveBy: internalMoveBy, | ||||
|     moveTo: moveTo, | ||||
| @@ -122,6 +122,10 @@ | ||||
|     isPaused: isPaused, | ||||
|   } | ||||
|  | ||||
|   eventify(api); | ||||
|  | ||||
|   return api; | ||||
|  | ||||
|   function pause() { | ||||
|     releaseEvents() | ||||
|     paused = true | ||||
| @@ -157,8 +161,8 @@ | ||||
|   } | ||||
|  | ||||
|   function transformToScreen(x, y) { | ||||
|                 if (domController.getScreenCTM) { | ||||
|                     var parentCTM = domController.getScreenCTM() | ||||
|     if (panController.getScreenCTM) { | ||||
|       var parentCTM = panController.getScreenCTM() | ||||
|       var parentScaleX = parentCTM.a | ||||
|       var parentScaleY = parentCTM.d | ||||
|       var parentOffsetX = parentCTM.e | ||||
| @@ -191,7 +195,7 @@ | ||||
|       w = ownerRect.width | ||||
|       h = ownerRect.height | ||||
|     } | ||||
|                 var bbox = domController.getBBox() | ||||
|     var bbox = panController.getBBox() | ||||
|     if (bbox.width === 0 || bbox.height === 0) { | ||||
|       // we probably do not have any elements in the SVG | ||||
|       // just bail out; | ||||
| @@ -293,7 +297,7 @@ | ||||
|   } | ||||
|  | ||||
|   function getClientRect() { | ||||
|                 var bbox = domController.getBBox() | ||||
|     var bbox = panController.getBBox() | ||||
|     var leftTop = client(bbox.left, bbox.top) | ||||
|  | ||||
|     return { | ||||
| @@ -441,7 +445,7 @@ | ||||
|     isDirty = false | ||||
|  | ||||
|     // TODO: Should I allow to cancel this? | ||||
|                 domController.applyTransform(transform) | ||||
|     panController.applyTransform(transform) | ||||
|  | ||||
|     triggerEvent('transform') | ||||
|     frameAnimation = 0 | ||||
| @@ -532,6 +536,7 @@ | ||||
|     mouseX = offset.x | ||||
|     mouseY = offset.y | ||||
|  | ||||
|     smoothScroll.cancel() | ||||
|     startTouchListenerIfNeeded() | ||||
|   } | ||||
|  | ||||
| @@ -636,6 +641,8 @@ | ||||
|     var isLeftButton = ((e.button === 1 && window.event !== null) || e.button === 0) | ||||
|     if (!isLeftButton) return | ||||
|  | ||||
|     smoothScroll.cancel() | ||||
|  | ||||
|     var offset = getOffsetXY(e); | ||||
|     var point = transformToScreen(offset.x, offset.y) | ||||
|     mouseX = point.x | ||||
| @@ -721,9 +728,6 @@ | ||||
|       smoothScroll.cancel() | ||||
|       cancelZoomAnimation() | ||||
|  | ||||
|                 // TODO: should consolidate this and publicZoomTo | ||||
|                 triggerEvent('zoom') | ||||
|  | ||||
|       zoomToAnimation = animate(from, to, { | ||||
|         step: function(v) { | ||||
|           zoomAbs(clientX, clientY, v.scale) | ||||
| @@ -772,14 +776,13 @@ | ||||
|   } | ||||
|  | ||||
|   function triggerEvent(name) { | ||||
|                 var event = createEvent(name) | ||||
|                 domElement.dispatchEvent(event) | ||||
|             } | ||||
|     api.fire(name, api); | ||||
|   } | ||||
| } | ||||
|  | ||||
|         function noop() { } | ||||
| function noop() { } | ||||
|  | ||||
|         function validateBounds(bounds) { | ||||
| function validateBounds(bounds) { | ||||
|   var boundsType = typeof bounds | ||||
|   if (boundsType === 'undefined' || boundsType === 'boolean') return // this is okay | ||||
|   // otherwise need to be more thorough: | ||||
| @@ -788,31 +791,31 @@ | ||||
|  | ||||
|   if (!validBounds) throw new Error('Bounds object is not valid. It can be: ' + | ||||
|     'undefined, boolean (true|false) or an object {left, top, right, bottom}') | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function isNumber(x) { | ||||
| function isNumber(x) { | ||||
|   return Number.isFinite(x) | ||||
|         } | ||||
| } | ||||
|  | ||||
| // IE 11 does not support isNaN: | ||||
|         function isNaN(value) { | ||||
| function isNaN(value) { | ||||
|   if (Number.isNaN) { | ||||
|     return Number.isNaN(value) | ||||
|   } | ||||
|  | ||||
|   return value !== value | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function rigidScroll() { | ||||
| function rigidScroll() { | ||||
|   return { | ||||
|     start: noop, | ||||
|     stop: noop, | ||||
|     cancel: noop | ||||
|   } | ||||
|         } | ||||
| } | ||||
|  | ||||
|  | ||||
|         function autoRun() { | ||||
| function autoRun() { | ||||
|   if (typeof document === 'undefined') return | ||||
|  | ||||
|   var scripts = document.getElementsByTagName('script'); | ||||
| @@ -878,35 +881,14 @@ | ||||
|     var value = JSON.parse(attr.value); | ||||
|     return {name: name, value: value}; | ||||
|   } | ||||
|         } | ||||
| } | ||||
|  | ||||
|         autoRun(); | ||||
| autoRun(); | ||||
|  | ||||
|     },{"./lib/createEvent.js":2,"./lib/domController.js":3,"./lib/kinetic.js":4,"./lib/svgController.js":5,"./lib/textSelectionInterceptor.js":6,"./lib/transform.js":7,"amator":8,"wheel":10}],2:[function(require,module,exports){ | ||||
|         /* global Event */ | ||||
|         module.exports = createEvent; | ||||
| },{"./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 | ||||
|  | ||||
|         var isIE = typeof Event !== 'function' | ||||
|  | ||||
|         /** | ||||
|          * Constructs custom event. Works in IE too | ||||
|          */ | ||||
|         function createEvent(name) { | ||||
|             if (isIE) { | ||||
|                 var evt = document.createEvent('CustomEvent') | ||||
|                 evt.initCustomEvent(name, true, true, undefined) | ||||
|                 return evt | ||||
|             } else { | ||||
|                 return new Event(name, { | ||||
|                     bubbles: true | ||||
|                 }) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     },{}],3:[function(require,module,exports){ | ||||
|         module.exports = makeDomController | ||||
|  | ||||
|         function makeDomController(domElement) { | ||||
| function makeDomController(domElement) { | ||||
|   var elementValid = (domElement instanceof HTMLElement) | ||||
|   if (!elementValid) { | ||||
|     throw new Error('svg element is required for svg.panzoom to work') | ||||
| @@ -952,15 +934,15 @@ | ||||
|       transform.scale + ', ' + | ||||
|       transform.x + ', ' + transform.y + ')' | ||||
|   } | ||||
|         } | ||||
| } | ||||
|  | ||||
|     },{}],4:[function(require,module,exports){ | ||||
|         /** | ||||
| },{}],3:[function(require,module,exports){ | ||||
| /** | ||||
|  * Allows smooth kinetic scrolling of the surface | ||||
|  */ | ||||
|         module.exports = kinetic; | ||||
| module.exports = kinetic; | ||||
|  | ||||
|         function kinetic(getPoint, scroll, settings) { | ||||
| function kinetic(getPoint, scroll, settings) { | ||||
|   if (typeof settings !== 'object') { | ||||
|     // setting could come as boolean, we should ignore it, and use an object. | ||||
|     settings = {} | ||||
| @@ -1074,12 +1056,12 @@ | ||||
|     } | ||||
|   } | ||||
|  | ||||
|         } | ||||
| } | ||||
|  | ||||
|     },{}],5:[function(require,module,exports){ | ||||
|         module.exports = makeSvgController | ||||
| },{}],4:[function(require,module,exports){ | ||||
| module.exports = makeSvgController | ||||
|  | ||||
|         function makeSvgController(svgElement) { | ||||
| function makeSvgController(svgElement) { | ||||
|   var elementValid = (svgElement instanceof SVGElement) | ||||
|   if (!elementValid) { | ||||
|     throw new Error('svg element is required for svg.panzoom to work') | ||||
| @@ -1137,14 +1119,14 @@ | ||||
|       transform.scale + ' ' + | ||||
|       transform.x + ' ' + transform.y + ')') | ||||
|   } | ||||
|         } | ||||
|     },{}],6:[function(require,module,exports){ | ||||
|         /** | ||||
| } | ||||
| },{}],5:[function(require,module,exports){ | ||||
| /** | ||||
|  * Disallows selecting text. | ||||
|  */ | ||||
|         module.exports = createTextSelectionInterceptor | ||||
| module.exports = createTextSelectionInterceptor | ||||
|  | ||||
|         function createTextSelectionInterceptor() { | ||||
| function createTextSelectionInterceptor() { | ||||
|   var dragObject | ||||
|   var prevSelectStart | ||||
|   var prevDragStart | ||||
| @@ -1168,41 +1150,41 @@ | ||||
|     window.document.onselectstart = prevSelectStart | ||||
|     if (dragObject) dragObject.ondragstart = prevDragStart | ||||
|   } | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function disabled(e) { | ||||
| function disabled(e) { | ||||
|   e.stopPropagation() | ||||
|   return false | ||||
|         } | ||||
| } | ||||
|  | ||||
|     },{}],7:[function(require,module,exports){ | ||||
|         module.exports = Transform; | ||||
| },{}],6:[function(require,module,exports){ | ||||
| module.exports = Transform; | ||||
|  | ||||
|         function Transform() { | ||||
| function Transform() { | ||||
|   this.x = 0; | ||||
|   this.y = 0; | ||||
|   this.scale = 1; | ||||
|         } | ||||
| } | ||||
|  | ||||
|     },{}],8:[function(require,module,exports){ | ||||
|         var BezierEasing = require('bezier-easing') | ||||
| },{}],7:[function(require,module,exports){ | ||||
| var BezierEasing = require('bezier-easing') | ||||
|  | ||||
| // Predefined set of animations. Similar to CSS easing functions | ||||
|         var animations = { | ||||
| var animations = { | ||||
|   ease:  BezierEasing(0.25, 0.1, 0.25, 1), | ||||
|   easeIn: BezierEasing(0.42, 0, 1, 1), | ||||
|   easeOut: BezierEasing(0, 0, 0.58, 1), | ||||
|   easeInOut: BezierEasing(0.42, 0, 0.58, 1), | ||||
|   linear: BezierEasing(0, 0, 1, 1) | ||||
|         } | ||||
| } | ||||
|  | ||||
|  | ||||
|         module.exports = animate; | ||||
|         module.exports.makeAggregateRaf = makeAggregateRaf; | ||||
|         module.exports.sharedScheduler = makeAggregateRaf(); | ||||
| module.exports = animate; | ||||
| module.exports.makeAggregateRaf = makeAggregateRaf; | ||||
| module.exports.sharedScheduler = makeAggregateRaf(); | ||||
|  | ||||
|  | ||||
|         function animate(source, target, options) { | ||||
| function animate(source, target, options) { | ||||
|   var start = Object.create(null) | ||||
|   var diff = Object.create(null) | ||||
|   options = options || {} | ||||
| @@ -1262,11 +1244,11 @@ | ||||
|       source[key] = diff[key] * t + start[key] | ||||
|     }) | ||||
|   } | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function noop() { } | ||||
| function noop() { } | ||||
|  | ||||
|         function getScheduler(scheduler) { | ||||
| function getScheduler(scheduler) { | ||||
|   if (!scheduler) { | ||||
|     var canRaf = typeof window !== 'undefined' && window.requestAnimationFrame | ||||
|     return canRaf ? rafScheduler() : timeoutScheduler() | ||||
| @@ -1275,16 +1257,16 @@ | ||||
|   if (typeof scheduler.cancel !== 'function') throw new Error('Scheduler is supposed to have cancel(handle) function') | ||||
|  | ||||
|   return scheduler | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function rafScheduler() { | ||||
| function rafScheduler() { | ||||
|   return { | ||||
|     next: window.requestAnimationFrame.bind(window), | ||||
|     cancel: window.cancelAnimationFrame.bind(window) | ||||
|   } | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function timeoutScheduler() { | ||||
| function timeoutScheduler() { | ||||
|   return { | ||||
|     next: function(cb) { | ||||
|       return setTimeout(cb, 1000/60) | ||||
| @@ -1293,9 +1275,9 @@ | ||||
|       return clearTimeout(id) | ||||
|     } | ||||
|   } | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function makeAggregateRaf() { | ||||
| function makeAggregateRaf() { | ||||
|   var frontBuffer = new Set(); | ||||
|   var backBuffer = new Set(); | ||||
|   var frameToken = 0; | ||||
| @@ -1338,37 +1320,37 @@ | ||||
|   function cancel(callback) { | ||||
|     backBuffer.delete(callback); | ||||
|   } | ||||
|         } | ||||
| } | ||||
|  | ||||
|     },{"bezier-easing":9}],9:[function(require,module,exports){ | ||||
|         /** | ||||
| },{"bezier-easing":8}],8:[function(require,module,exports){ | ||||
| /** | ||||
|  * https://github.com/gre/bezier-easing | ||||
|  * BezierEasing - use bezier curve for transition easing function | ||||
|  * by Gaëtan Renaudeau 2014 - 2015 – MIT License | ||||
|  */ | ||||
|  | ||||
| // These values are established by empiricism with tests (tradeoff: performance VS precision) | ||||
|         var NEWTON_ITERATIONS = 4; | ||||
|         var NEWTON_MIN_SLOPE = 0.001; | ||||
|         var SUBDIVISION_PRECISION = 0.0000001; | ||||
|         var SUBDIVISION_MAX_ITERATIONS = 10; | ||||
| var NEWTON_ITERATIONS = 4; | ||||
| var NEWTON_MIN_SLOPE = 0.001; | ||||
| var SUBDIVISION_PRECISION = 0.0000001; | ||||
| var SUBDIVISION_MAX_ITERATIONS = 10; | ||||
|  | ||||
|         var kSplineTableSize = 11; | ||||
|         var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); | ||||
| var kSplineTableSize = 11; | ||||
| var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); | ||||
|  | ||||
|         var float32ArraySupported = typeof Float32Array === 'function'; | ||||
| var float32ArraySupported = typeof Float32Array === 'function'; | ||||
|  | ||||
|         function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } | ||||
|         function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } | ||||
|         function C (aA1)      { return 3.0 * aA1; } | ||||
| function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } | ||||
| function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } | ||||
| function C (aA1)      { return 3.0 * aA1; } | ||||
|  | ||||
| // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. | ||||
|         function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } | ||||
| function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } | ||||
|  | ||||
| // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. | ||||
|         function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } | ||||
| function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } | ||||
|  | ||||
|         function binarySubdivide (aX, aA, aB, mX1, mX2) { | ||||
| function binarySubdivide (aX, aA, aB, mX1, mX2) { | ||||
|   var currentX, currentT, i = 0; | ||||
|   do { | ||||
|     currentT = aA + (aB - aA) / 2.0; | ||||
| @@ -1380,9 +1362,9 @@ | ||||
|     } | ||||
|   } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); | ||||
|   return currentT; | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) { | ||||
| function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) { | ||||
|  for (var i = 0; i < NEWTON_ITERATIONS; ++i) { | ||||
|    var currentSlope = getSlope(aGuessT, mX1, mX2); | ||||
|    if (currentSlope === 0.0) { | ||||
| @@ -1392,13 +1374,13 @@ | ||||
|    aGuessT -= currentX / currentSlope; | ||||
|  } | ||||
|  return aGuessT; | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function LinearEasing (x) { | ||||
| function LinearEasing (x) { | ||||
|   return x; | ||||
|         } | ||||
| } | ||||
|  | ||||
|         module.exports = function bezier (mX1, mY1, mX2, mY2) { | ||||
| module.exports = function bezier (mX1, mY1, mX2, mY2) { | ||||
|   if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { | ||||
|     throw new Error('bezier x values must be in [0, 1] range'); | ||||
|   } | ||||
| @@ -1447,10 +1429,100 @@ | ||||
|     } | ||||
|     return calcBezier(getTForX(x), mY1, mY2); | ||||
|   }; | ||||
|         }; | ||||
| }; | ||||
|  | ||||
|     },{}],10:[function(require,module,exports){ | ||||
|         /** | ||||
| },{}],9:[function(require,module,exports){ | ||||
| module.exports = function(subject) { | ||||
|   validateSubject(subject); | ||||
|  | ||||
|   var eventsStorage = createEventsStorage(subject); | ||||
|   subject.on = eventsStorage.on; | ||||
|   subject.off = eventsStorage.off; | ||||
|   subject.fire = eventsStorage.fire; | ||||
|   return subject; | ||||
| }; | ||||
|  | ||||
| function createEventsStorage(subject) { | ||||
|   // Store all event listeners to this hash. Key is event name, value is array | ||||
|   // of callback records. | ||||
|   // | ||||
|   // A callback record consists of callback function and its optional context: | ||||
|   // { 'eventName' => [{callback: function, ctx: object}] } | ||||
|   var registeredEvents = Object.create(null); | ||||
|  | ||||
|   return { | ||||
|     on: function (eventName, callback, ctx) { | ||||
|       if (typeof callback !== 'function') { | ||||
|         throw new Error('callback is expected to be a function'); | ||||
|       } | ||||
|       var handlers = registeredEvents[eventName]; | ||||
|       if (!handlers) { | ||||
|         handlers = registeredEvents[eventName] = []; | ||||
|       } | ||||
|       handlers.push({callback: callback, ctx: ctx}); | ||||
|  | ||||
|       return subject; | ||||
|     }, | ||||
|  | ||||
|     off: function (eventName, callback) { | ||||
|       var wantToRemoveAll = (typeof eventName === 'undefined'); | ||||
|       if (wantToRemoveAll) { | ||||
|         // Killing old events storage should be enough in this case: | ||||
|         registeredEvents = Object.create(null); | ||||
|         return subject; | ||||
|       } | ||||
|  | ||||
|       if (registeredEvents[eventName]) { | ||||
|         var deleteAllCallbacksForEvent = (typeof callback !== 'function'); | ||||
|         if (deleteAllCallbacksForEvent) { | ||||
|           delete registeredEvents[eventName]; | ||||
|         } else { | ||||
|           var callbacks = registeredEvents[eventName]; | ||||
|           for (var i = 0; i < callbacks.length; ++i) { | ||||
|             if (callbacks[i].callback === callback) { | ||||
|               callbacks.splice(i, 1); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       return subject; | ||||
|     }, | ||||
|  | ||||
|     fire: function (eventName) { | ||||
|       var callbacks = registeredEvents[eventName]; | ||||
|       if (!callbacks) { | ||||
|         return subject; | ||||
|       } | ||||
|  | ||||
|       var fireArguments; | ||||
|       if (arguments.length > 1) { | ||||
|         fireArguments = Array.prototype.splice.call(arguments, 1); | ||||
|       } | ||||
|       for(var i = 0; i < callbacks.length; ++i) { | ||||
|         var callbackInfo = callbacks[i]; | ||||
|         callbackInfo.callback.apply(callbackInfo.ctx, fireArguments); | ||||
|       } | ||||
|  | ||||
|       return subject; | ||||
|     } | ||||
|   }; | ||||
| } | ||||
|  | ||||
| function validateSubject(subject) { | ||||
|   if (!subject) { | ||||
|     throw new Error('Eventify cannot use falsy object as events subject'); | ||||
|   } | ||||
|   var reservedWords = ['on', 'fire', 'off']; | ||||
|   for (var i = 0; i < reservedWords.length; ++i) { | ||||
|     if (subject.hasOwnProperty(reservedWords[i])) { | ||||
|       throw new Error("Subject cannot be eventified, since it already has property '" + reservedWords[i] + "'"); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| },{}],10:[function(require,module,exports){ | ||||
| /** | ||||
|  * This module unifies handling of mouse whee event across different browsers | ||||
|  * | ||||
|  * See https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel?redirectlocale=en-US&redirectslug=DOM%2FMozilla_event_reference%2Fwheel | ||||
| @@ -1466,40 +1538,40 @@ | ||||
|  */ | ||||
| // by default we shortcut to 'addEventListener': | ||||
|  | ||||
|         module.exports = addWheelListener; | ||||
| module.exports = addWheelListener; | ||||
|  | ||||
| // But also expose "advanced" api with unsubscribe: | ||||
|         module.exports.addWheelListener = addWheelListener; | ||||
|         module.exports.removeWheelListener = removeWheelListener; | ||||
| module.exports.addWheelListener = addWheelListener; | ||||
| module.exports.removeWheelListener = removeWheelListener; | ||||
|  | ||||
|  | ||||
|         var prefix = "", _addEventListener, _removeEventListener,  support; | ||||
| var prefix = "", _addEventListener, _removeEventListener,  support; | ||||
|  | ||||
|         detectEventModel(typeof window !== 'undefined' && window, | ||||
| detectEventModel(typeof window !== 'undefined' && window, | ||||
|                 typeof document !== 'undefined' && document); | ||||
|  | ||||
|         function addWheelListener( elem, callback, useCapture ) { | ||||
| function addWheelListener( elem, callback, useCapture ) { | ||||
|     _addWheelListener( elem, support, callback, useCapture ); | ||||
|  | ||||
|     // handle MozMousePixelScroll in older Firefox | ||||
|     if( support == "DOMMouseScroll" ) { | ||||
|         _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture ); | ||||
|     } | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function removeWheelListener( elem, callback, useCapture ) { | ||||
| function removeWheelListener( elem, callback, useCapture ) { | ||||
|     _removeWheelListener( elem, support, callback, useCapture ); | ||||
|  | ||||
|     // handle MozMousePixelScroll in older Firefox | ||||
|     if( support == "DOMMouseScroll" ) { | ||||
|         _removeWheelListener( elem, "MozMousePixelScroll", callback, useCapture ); | ||||
|     } | ||||
|         } | ||||
| } | ||||
|  | ||||
|   // TODO: in theory this anonymous function may result in incorrect | ||||
|   // 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 ) { | ||||
| function _addWheelListener( elem, eventName, callback, useCapture ) { | ||||
|   elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) { | ||||
|     !originalEvent && ( originalEvent = window.event ); | ||||
|  | ||||
| @@ -1543,13 +1615,13 @@ | ||||
|     return callback( event ); | ||||
|  | ||||
|   }, useCapture || false ); | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function _removeWheelListener( elem, eventName, callback, useCapture ) { | ||||
| function _removeWheelListener( elem, eventName, callback, useCapture ) { | ||||
|   elem[ _removeEventListener ]( prefix + eventName, callback, useCapture || false ); | ||||
|         } | ||||
| } | ||||
|  | ||||
|         function detectEventModel(window, document) { | ||||
| function detectEventModel(window, document) { | ||||
|   if ( window && window.addEventListener ) { | ||||
|       _addEventListener = "addEventListener"; | ||||
|       _removeEventListener = "removeEventListener"; | ||||
| @@ -1567,7 +1639,7 @@ | ||||
|   } else { | ||||
|     support = "wheel"; | ||||
|   } | ||||
|         } | ||||
| } | ||||
|  | ||||
|     },{}]},{},[1])(1) | ||||
| },{}]},{},[1])(1) | ||||
| }); | ||||
| @@ -433,6 +433,12 @@ button.icon-button { | ||||
|     background: no-repeat center; | ||||
| } | ||||
|  | ||||
| button.icon-button24 { | ||||
|     height: 32px; | ||||
|     width: 32px; | ||||
|     background: no-repeat center; | ||||
| } | ||||
|  | ||||
| #note-actions { | ||||
|     margin-left: 10px; | ||||
|     margin-right: 10px; | ||||
|   | ||||
| @@ -265,6 +265,20 @@ | ||||
|           <div id="note-detail-relation-map" class="note-detail-component"> | ||||
|             <button id="relation-map-add-child-notes" class="btn" type="button">Add child notes</button> | ||||
|  | ||||
|             <div class="btn-group" style="float: right; padding-right: 20px;"> | ||||
|               <button type="button" | ||||
|                       class="btn icon-button24" | ||||
|                       title="Zoom In" | ||||
|                       id="relation-map-zoom-in" | ||||
|                       style="background-image: url('/images/icons/zoom-in-24.png');"/> | ||||
|  | ||||
|               <button type="button" | ||||
|                       class="btn icon-button24" | ||||
|                       title="Zoom Out" | ||||
|                       id="relation-map-zoom-out" | ||||
|                       style="background-image: url('/images/icons/zoom-out-24.png');"/> | ||||
|             </div> | ||||
|  | ||||
|             <div id="relation-map-canvas"></div> | ||||
|           </div> | ||||
|         </div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user