| | @@ -288,18 +288,19 @@ |
| 288 | 288 | during initialization and stashed away for use in a PopupWidget |
| 289 | 289 | when the botton is clicked. |
| 290 | 290 | |
| 291 | 291 | */ |
| 292 | 292 | setup: function f(){ |
| 293 | | - if(!f.clickHandler){ |
| 293 | + if(!f.hasOwnProperty('clickHandler')){ |
| 294 | 294 | f.clickHandler = function fch(ev){ |
| 295 | 295 | if(!fch.popup){ |
| 296 | 296 | fch.popup = new F.PopupWidget({ |
| 297 | 297 | cssClass: ['fossil-tooltip', 'help-buttonlet-content'], |
| 298 | 298 | refresh: function(){ |
| 299 | 299 | } |
| 300 | 300 | }); |
| 301 | + fch.popup.e.style.maxWidth = '80%'/*of body*/; |
| 301 | 302 | const hide = ()=>fch.popup.hide(); |
| 302 | 303 | fch.popup.e.addEventListener('click', hide, false); |
| 303 | 304 | document.body.addEventListener('click', hide, true); |
| 304 | 305 | document.body.addEventListener('keydown', function(ev){ |
| 305 | 306 | if(fch.popup.isShown() && 27===ev.which){ |
| | @@ -306,24 +307,58 @@ |
| 306 | 307 | fch.popup.hide(); |
| 307 | 308 | } |
| 308 | 309 | }, true); |
| 309 | 310 | } |
| 310 | 311 | D.append(D.clearElement(fch.popup.e), ev.target.$helpContent); |
| 311 | | - const rect1 = ev.target.getClientRects()[0]; |
| 312 | | - var x = rect1.left, y = rect1.top; |
| 312 | + var popupRect = ev.target.getClientRects()[0]; |
| 313 | + var x = popupRect.left, y = popupRect.top; |
| 313 | 314 | if(x<0) x = 0; |
| 314 | 315 | if(y<0) y = 0; |
| 315 | | - /* shift help to the left 1/2 the width of fch.popup.e. However, |
| 316 | | - fch.popup.e.getClientRects() is empty until the popup is shown, |
| 317 | | - so we have to show it, calculate that size, then move it. */ |
| 316 | + /* Shift the help around a bit to "better" fit the |
| 317 | + screen. However, fch.popup.e.getClientRects() is empty |
| 318 | + until the popup is shown, so we have to show it, |
| 319 | + calculate the resulting size, then move and/or resize it. |
| 320 | + |
| 321 | + This algorithm/these heuristics can certainly be improved |
| 322 | + upon. Just be careful to mess only with the X coordinate |
| 323 | + and the width. The browser will try to keep the widget |
| 324 | + from being truncated off-screen on the right, shifting it |
| 325 | + to the left if needed, and we cannot generically be sure |
| 326 | + that an enforced fully on-screen size will actually fit |
| 327 | + the current help text. |
| 328 | + */ |
| 318 | 329 | fch.popup.show(x, y); |
| 319 | | - const rect2 = fch.popup.e.getClientRects()[0]; |
| 320 | | - x -= rect2.width/2; |
| 330 | + x = popupRect.left, y = popupRect.top; |
| 331 | + popupRect = fch.popup.e.getBoundingClientRect(); |
| 332 | + const rectBody = document.body.getClientRects()[0]; |
| 333 | + if(popupRect.right > rectBody.right){ |
| 334 | + x -= (popupRect.right - rectBody.right); |
| 335 | + } |
| 336 | + if(x + popupRect.width > rectBody.right){ |
| 337 | + x = rectBody.x + (rectBody.width*0.1); |
| 338 | + fch.popup.e.style.minWidth = '70%'; |
| 339 | + }else{ |
| 340 | + fch.popup.e.style.removeProperty('min-width'); |
| 341 | + x -= popupRect.width/2; |
| 342 | + } |
| 321 | 343 | if(x<0) x = 0; |
| 344 | + //console.debug("dimensions",x,y, popupRect, rectBody); |
| 322 | 345 | fch.popup.show(x, y); |
| 323 | 346 | }; |
| 324 | | - } |
| 347 | + f.foreachElement = function(e){ |
| 348 | + if(e.classList.contains('processed')) return; |
| 349 | + e.classList.add('processed'); |
| 350 | + e.$helpContent = []; |
| 351 | + /* We have to move all child nodes out of the way because we |
| 352 | + cannot hide TEXT nodes via CSS (which cannot select TEXT |
| 353 | + nodes). We have to do it in two steps to avoid invaliding |
| 354 | + the list during traversal. */ |
| 355 | + e.childNodes.forEach((ch)=>e.$helpContent.push(ch)); |
| 356 | + e.$helpContent.forEach((ch)=>ch.remove()); |
| 357 | + e.addEventListener('click', f.clickHandler, false); |
| 358 | + }; |
| 359 | + }/*static init*/ |
| 325 | 360 | var elems; |
| 326 | 361 | if(!arguments.length){ |
| 327 | 362 | arguments[0] = '.help-buttonlet:not(.processed)'; |
| 328 | 363 | arguments.length = 1; |
| 329 | 364 | } |
| | @@ -330,27 +365,15 @@ |
| 330 | 365 | if(arguments.length){ |
| 331 | 366 | if('string'===typeof arguments[0]){ |
| 332 | 367 | elems = document.querySelectorAll(arguments[0]); |
| 333 | 368 | }else if(arguments[0] instanceof HTMLElement){ |
| 334 | 369 | elems = [arguments[0]]; |
| 335 | | - }else{/* assume DOM element list or array */ |
| 370 | + }else if(arguments[0].forEach){/* assume DOM element list or array */ |
| 336 | 371 | elems = arguments[0]; |
| 337 | 372 | } |
| 338 | 373 | } |
| 339 | | - if(!elems) return; |
| 340 | | - elems.forEach(function(e){ |
| 341 | | - if(e.classList.contains('processed')) return; |
| 342 | | - e.classList.add('processed'); |
| 343 | | - e.$helpContent = []; |
| 344 | | - /* We have to move all child nodes out of the way because we |
| 345 | | - cannot hide TEXT nodes via CSS (which cannot select TEXT |
| 346 | | - nodes). We have to do it in two steps to avoid invaliding |
| 347 | | - the list during traversal. */ |
| 348 | | - e.childNodes.forEach((ch)=>e.$helpContent.push(ch)); |
| 349 | | - e.$helpContent.forEach((ch)=>ch.remove()); |
| 350 | | - e.addEventListener('click', f.clickHandler, false); |
| 351 | | - }); |
| 374 | + if(elems) elems.forEach(f.foreachElement); |
| 352 | 375 | }, |
| 353 | 376 | |
| 354 | 377 | /** |
| 355 | 378 | Sets up the given element as a "help buttonlet", adding the CSS |
| 356 | 379 | class help-buttonlet to it. Any (optional) arguments after the |
| 357 | 380 | |