1+ import '@popperjs/core' ;
12import 'bootstrap/dist/css/bootstrap.min.css' ;
2- import 'bootstrap/dist/js/bootstrap.bundle.min.js ' ;
3+ import { Popover } from 'bootstrap' ;
34import 'bootstrap-icons/font/bootstrap-icons.css' ;
45import jsMind from './jsmind/src/jsmind.js' ;
56// * Note: this import is important for proper manual node creation / addition
@@ -73,10 +74,12 @@ const options = {
7374 handles : { // Named shortcut key event processor
7475 'undo' : function ( jm , e ) {
7576 // display mind map's previous state (undo the last operation)
77+ hidePopovers ( ) ;
7678 jm . undo ( ) ;
7779 } ,
7880 'redo' : function ( jm , e ) {
7981 // display mind map's next state (redo the next operation)
82+ hidePopovers ( ) ;
8083 jm . redo ( ) ;
8184 } ,
8285 'toggleTag' : function ( jm , e ) {
@@ -102,7 +105,7 @@ const options = {
102105 addchild : [ 45 , 4096 + 13 ] , // <Insert>, <Ctrl> + <Enter>
103106 addbrother : 13 , // <Enter>
104107 editnode : 113 , // <F2>
105- delnode : 46 , // <Delete>
108+ delnode : [ 46 , 8 ] , // <Delete>
106109 toggle : 32 , // <Space>
107110 left : 37 , // <Left>
108111 up : 38 , // <Up>
@@ -179,7 +182,7 @@ function applyTag(selectedNode, iconKey) {
179182 * the selected node.
180183 * @param { object } selectedNode - The node a highlight should be
181184 * applied to / removed from.
182- * @param { string } highlightKey - The color of the highlight.
185+ * @param { string } highlight - The color of the highlight.
183186 */
184187function applyHighlight ( selectedNode , highlight ) {
185188 selectedNode . data . highlight = selectedNode . data . highlight !== highlight ?
@@ -203,7 +206,7 @@ function extendNode(node) {
203206 node . highlight = node . highlight ?? null ;
204207
205208 node . citeKey = node . citeKey ?? null ;
206- node . bibPreview = node . bibPreview ?? null ;
209+ node . preview = node . preview ?? null ;
207210
208211 assignNodeType ( node ) ;
209212
@@ -221,21 +224,83 @@ function assignNodeType(node) {
221224 type = 'BIBE' ;
222225 }
223226
224- // * Note: this will probably be extended to handle PDFF / PDFC types
227+ // * Note: this will probably be extended to handle PDFF / PDFC types
225228 node . type = type ;
226229}
227230// extend the default mind map
228231extendNode ( mind . data ) ;
229232
230- // create a render for mind maps and display the initial one
233+ // create a render for mind maps
231234const jm = new jsMind ( options ) ;
235+
236+ // add some logic to jsMind's events
237+ // * Note: this is called after the original logic is performed
238+ jm . add_event_listener ( ( type , data ) => {
239+ if ( type === jsMind . event_type . show ) {
240+ addPopoversToBibEntryNodes ( ) ;
241+ }
242+ if ( type === jsMind . event_type . edit ) {
243+ hidePopovers ( ) ;
244+ }
245+ if ( type === jsMind . event_type . select ) {
246+ hidePopovers ( ) ;
247+ }
248+ } ) ;
249+
250+ // display the initial state and add it to the action stack
232251jm . show ( mind ) ;
233- // add the initial state to the action stack
234252jm . resetStack ( ) ;
235253
236254// create a HTTP client instance
237255let httpClient = new HTTPClient ( ) ;
238256
257+ /**
258+ * Hides all existing Bootsrap's popovers.
259+ */
260+ function hidePopovers ( ) {
261+ // iterate through all Bootstrap's toggles as HTML elements
262+ document . querySelectorAll ( '.popover' ) . forEach ( ( bsToggle ) => {
263+ bsToggle . remove ( ) ;
264+ } ) ;
265+ }
266+
267+ /**
268+ * Attaches Bootstrap's popovers to all BibEntry nodes.
269+ */
270+ function addPopoversToBibEntryNodes ( ) {
271+ // iterate through all nodes as HTML elements
272+ const allNodes = document . querySelectorAll ( 'jmnode' ) ;
273+ allNodes . forEach ( nodeElem => {
274+ // if one already has a popover, skip it
275+ if ( nodeElem . getAttribute ( 'data-bs-toggle' ) === 'popover' ) {
276+ return ;
277+ }
278+
279+ // get node's instance
280+ const nodeId = nodeElem . getAttribute ( 'nodeid' ) ;
281+ if ( ! nodeId ) {
282+ return ;
283+ }
284+ const node = jm . get_node ( nodeId ) ;
285+ // if one isn't a BibEntry node, skip it
286+ if ( node ?. data ?. type !== 'BIBE' ) {
287+ return ;
288+ }
289+
290+ // otherwise create a popover for it
291+ const previewHTML = node . data . preview ;
292+
293+ nodeElem . setAttribute ( 'data-bs-toggle' , 'popover' ) ;
294+ nodeElem . setAttribute ( 'data-bs-trigger' , 'hover focus' ) ;
295+ nodeElem . setAttribute ( 'data-bs-placement' , 'bottom' ) ;
296+ nodeElem . setAttribute ( 'data-bs-html' , 'true' ) ;
297+ nodeElem . setAttribute ( 'title' , 'Entry Preview' ) ;
298+ nodeElem . setAttribute ( 'data-bs-content' , previewHTML ) ;
299+
300+ new Popover ( nodeElem , { container : 'body' } ) ;
301+ } ) ;
302+ }
303+
239304//--- Button click handlers ---
240305
241306// saving - sends mind map's content to JabRef's HTTP server
@@ -259,7 +324,7 @@ openBtn.onclick = async function () {
259324 `</option>` ;
260325 }
261326
262- if ( bsSelect . innerHTML != '' ) {
327+ if ( bsSelect . innerHTML != '' ) {
263328 // select first element
264329 bsSelect . selectedIndex = 0 ;
265330 }
@@ -282,6 +347,8 @@ openSelectedMapBtn.onclick = async function () {
282347 // if no mind map exists, show the default one
283348 let loadedMap = loadResponse . map ?? mind ;
284349
350+ extendNode ( loadedMap . data ) ;
351+
285352 // display the retrieved mind map
286353 jm . show ( loadedMap ) ;
287354 jm . resetStack ( ) ;
@@ -300,11 +367,13 @@ printMapToConsoleBtn.onclick = async function () {
300367
301368// undo - discard the last operation (display the previous state)
302369undoBtn . onclick = function ( ) {
370+ hidePopovers ( ) ;
303371 jm . undo ( ) ;
304372}
305373
306374// redo - reapply the next operation (display the following state)
307375redoBtn . onclick = function ( ) {
376+ hidePopovers ( ) ;
308377 jm . redo ( ) ;
309378}
310379
@@ -331,13 +400,13 @@ newChildBtn.onclick = function () {
331400async function getBibNodesProperties ( ) {
332401 // open cayw window and retrieve selected keys
333402 let selectedKeys = await httpClient . getCiteKeysWithCAYW ( ) ;
334-
403+
335404 // and get preview string for each selected key
336405 let bibNodesProperties = [ ] ;
337406 for ( let i = 0 ; i < selectedKeys . length ; i ++ ) {
338407 bibNodesProperties . push ( {
339408 key : selectedKeys [ i ] ,
340- preview : await httpClient . getPreviewString ( selectedKeys [ i ] )
409+ preview : await httpClient . getPreviewHTML ( selectedKeys [ i ] )
341410 } ) ;
342411 }
343412
@@ -397,6 +466,8 @@ addBibEntryAsChildBtn.onclick = async function () {
397466 } ) ;
398467 // save map state for undo/redo
399468 jm . saveState ( ) ;
469+ // and create popovers for new BibEntry nodes
470+ addPopoversToBibEntryNodes ( ) ;
400471}
401472
402473addBibEntryAsSiblingBtn . onclick = async function ( ) {
@@ -426,48 +497,50 @@ addBibEntryAsSiblingBtn.onclick = async function () {
426497 } ) ;
427498 // save map state for undo/redo
428499 jm . saveState ( ) ;
500+ // and create popovers for new BibEntry nodes
501+ addPopoversToBibEntryNodes ( ) ;
429502}
430503
431504// icon-dropdown menu button handlers
432505iconCycleBtn . onclick = function ( ) {
433- if ( jm != null ) {
434- applyTag ( jm . get_selected_node ( ) , 1 ) ;
506+ if ( jm != null ) {
507+ applyTag ( jm . get_selected_node ( ) , 1 ) ;
435508 }
436509}
437510
438511iconStarBtn . onclick = function ( ) {
439- if ( jm != null ) {
440- applyTag ( jm . get_selected_node ( ) , 2 ) ;
512+ if ( jm != null ) {
513+ applyTag ( jm . get_selected_node ( ) , 2 ) ;
441514 }
442515}
443516
444517iconQuestionBtn . onclick = function ( ) {
445- if ( jm != null ) {
446- applyTag ( jm . get_selected_node ( ) , 3 ) ;
518+ if ( jm != null ) {
519+ applyTag ( jm . get_selected_node ( ) , 3 ) ;
447520 }
448521}
449522
450523iconWarningBtn . onclick = function ( ) {
451- if ( jm != null ) {
452- applyTag ( jm . get_selected_node ( ) , 6 ) ;
524+ if ( jm != null ) {
525+ applyTag ( jm . get_selected_node ( ) , 6 ) ;
453526 }
454527}
455528
456529iconLightbulbBtn . onclick = function ( ) {
457- if ( jm != null ) {
458- applyTag ( jm . get_selected_node ( ) , 7 ) ;
530+ if ( jm != null ) {
531+ applyTag ( jm . get_selected_node ( ) , 7 ) ;
459532 }
460533}
461534
462535iconGreenFlagBtn . onclick = function ( ) {
463- if ( jm != null ) {
464- applyTag ( jm . get_selected_node ( ) , 8 ) ;
536+ if ( jm != null ) {
537+ applyTag ( jm . get_selected_node ( ) , 8 ) ;
465538 }
466539}
467540
468541iconRedFlagBtn . onclick = function ( ) {
469- if ( jm != null ) {
470- applyTag ( jm . get_selected_node ( ) , 9 ) ;
542+ if ( jm != null ) {
543+ applyTag ( jm . get_selected_node ( ) , 9 ) ;
471544 }
472545}
473546
0 commit comments