VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/testmanager/htdocs/js/common.js@ 65091

Last change on this file since 65091 was 65091, checked in by vboxsync, 8 years ago

TestManager: Merged OS versions into the OS filter.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.3 KB
Line 
1/* $Id: common.js 65091 2017-01-04 02:04:12Z vboxsync $ */
2/** @file
3 * Common JavaScript functions
4 */
5
6/*
7 *
8 * Copyright (C) 2012-2015 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * The contents of this file may alternatively be used under the terms
19 * of the Common Development and Distribution License Version 1.0
20 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
21 * VirtualBox OSE distribution, in which case the provisions of the
22 * CDDL are applicable instead of those of the GPL.
23 *
24 * You may elect to license modified versions of this file under the
25 * terms and conditions of either the GPL or the CDDL or both.
26 */
27
28
29/*********************************************************************************************************************************
30* Global Variables *
31*********************************************************************************************************************************/
32/** Same as WuiDispatcherBase.ksParamRedirectTo. */
33var g_ksParamRedirectTo = 'RedirectTo';
34
35
36/**
37 * Checks if the given value is a decimal integer value.
38 *
39 * @returns true if it is, false if it's isn't.
40 * @param sValue The value to inspect.
41 */
42function isInteger(sValue)
43{
44 if (typeof sValue != 'undefined')
45 {
46 var intRegex = /^\d+$/;
47 if (intRegex.test(sValue))
48 {
49 return true;
50 }
51 }
52 return false;
53}
54
55/**
56 * Removes the element with the specified ID.
57 */
58function removeHtmlNode(sContainerId)
59{
60 var oElement = document.getElementById(sContainerId);
61 if (oElement)
62 {
63 oElement.parentNode.removeChild(oElement);
64 }
65}
66
67/**
68 * Sets the value of the element with id @a sInputId to the keys of aoItems
69 * (comma separated).
70 */
71function setElementValueToKeyList(sInputId, aoItems)
72{
73 var sKey;
74 var oElement = document.getElementById(sInputId);
75 oElement.value = '';
76
77 for (sKey in aoItems)
78 {
79 if (oElement.value.length > 0)
80 {
81 oElement.value += ',';
82 }
83
84 oElement.value += sKey;
85 }
86}
87
88/**
89 * Get the Window.devicePixelRatio in a safe way.
90 *
91 * @returns Floating point ratio. 1.0 means it's a 1:1 ratio.
92 */
93function getDevicePixelRatio()
94{
95 var fpRatio = 1.0;
96 if (window.devicePixelRatio)
97 {
98 fpRatio = window.devicePixelRatio;
99 if (fpRatio < 0.5 || fpRatio > 10.0)
100 fpRatio = 1.0;
101 }
102 return fpRatio;
103}
104
105/**
106 * Tries to figure out the DPI of the device in the X direction.
107 *
108 * @returns DPI on success, null on failure.
109 */
110function getDeviceXDotsPerInch()
111{
112 if (window.deviceXDPI && window.deviceXDPI > 48 && window.deviceXDPI < 2048)
113 {
114 return window.deviceXDPI;
115 }
116 else if (window.devicePixelRatio && window.devicePixelRatio >= 0.5 && window.devicePixelRatio <= 10.0)
117 {
118 cDotsPerInch = Math.round(96 * window.devicePixelRatio);
119 }
120 else
121 {
122 cDotsPerInch = null;
123 }
124 return cDotsPerInch;
125}
126
127/**
128 * Gets the width of the given element (downscaled).
129 *
130 * Useful when using the element to figure the size of a image
131 * or similar.
132 *
133 * @returns Number of pixels. null if oElement is bad.
134 * @param oElement The element (not ID).
135 */
136function getElementWidth(oElement)
137{
138 if (oElement && oElement.offsetWidth)
139 return oElement.offsetWidth;
140 return null;
141}
142
143/** By element ID version of getElementWidth. */
144function getElementWidthById(sElementId)
145{
146 return getElementWidth(document.getElementById(sElementId));
147}
148
149/**
150 * Gets the real unscaled width of the given element.
151 *
152 * Useful when using the element to figure the size of a image
153 * or similar.
154 *
155 * @returns Number of screen pixels. null if oElement is bad.
156 * @param oElement The element (not ID).
157 */
158function getUnscaledElementWidth(oElement)
159{
160 if (oElement && oElement.offsetWidth)
161 return Math.round(oElement.offsetWidth * getDevicePixelRatio());
162 return null;
163}
164
165/** By element ID version of getUnscaledElementWidth. */
166function getUnscaledElementWidthById(sElementId)
167{
168 return getUnscaledElementWidth(document.getElementById(sElementId));
169}
170
171/**
172 * Gets the part of the URL needed for a RedirectTo parameter.
173 *
174 * @returns URL string.
175 */
176function getCurrentBrowerUrlPartForRedirectTo()
177{
178 var sWhere = window.location.href;
179 var offTmp;
180 var offPathKeep;
181
182 /* Find the end of that URL 'path' component. */
183 var offPathEnd = sWhere.indexOf('?');
184 if (offPathEnd < 0)
185 offPathEnd = sWhere.indexOf('#');
186 if (offPathEnd < 0)
187 offPathEnd = sWhere.length;
188
189 /* Go backwards from the end of the and find the start of the last component. */
190 offPathKeep = sWhere.lastIndexOf("/", offPathEnd);
191 offTmp = sWhere.lastIndexOf(":", offPathEnd);
192 if (offPathKeep < offTmp)
193 offPathKeep = offTmp;
194 offTmp = sWhere.lastIndexOf("\\", offPathEnd);
195 if (offPathKeep < offTmp)
196 offPathKeep = offTmp;
197
198 return sWhere.substring(offPathKeep + 1);
199}
200
201
202/**
203 * Sets the value of an input field element (give by ID).
204 *
205 * @returns Returns success indicator (true/false).
206 * @param sFieldId The field ID (required for updating).
207 * @param sValue The field value.
208 */
209function setInputFieldValue(sFieldId, sValue)
210{
211 var oInputElement = document.getElementById(sFieldId);
212 if (oInputElement)
213 {
214 oInputElement.value = sValue;
215 return true;
216 }
217 return false;
218}
219
220/**
221 * Adds a hidden input field to a form.
222 *
223 * @returns The new input field element.
224 * @param oFormElement The form to append it to.
225 * @param sName The field name.
226 * @param sValue The field value.
227 * @param sFieldId The field ID (optional).
228 */
229function addHiddenInputFieldToForm(oFormElement, sName, sValue, sFieldId)
230{
231 var oNew = document.createElement('input');
232 oNew.type = 'hidden';
233 oNew.name = sName;
234 oNew.value = sValue;
235 if (sFieldId)
236 oNew.id = sFieldId;
237 oFormElement.appendChild(oNew);
238 return oNew;
239}
240
241/** By element ID version of addHiddenInputFieldToForm. */
242function addHiddenInputFieldToFormById(sFormId, sName, sValue, sFieldId)
243{
244 return addHiddenInputFieldToForm(document.getElementById(sFormId), sName, sValue, sFieldId);
245}
246
247/**
248 * Adds or updates a hidden input field to/on a form.
249 *
250 * @returns The new input field element.
251 * @param sFormId The ID of the form to amend.
252 * @param sName The field name.
253 * @param sValue The field value.
254 * @param sFieldId The field ID (required for updating).
255 */
256function addUpdateHiddenInputFieldToFormById(sFormId, sName, sValue, sFieldId)
257{
258 var oInputElement = null;
259 if (sFieldId)
260 {
261 oInputElement = document.getElementById(sFieldId);
262 }
263 if (oInputElement)
264 {
265 oInputElement.name = sName;
266 oInputElement.value = sValue;
267 }
268 else
269 {
270 oInputElement = addHiddenInputFieldToFormById(sFormId, sName, sValue, sFieldId);
271 }
272 return oInputElement;
273}
274
275/**
276 * Adds a width and a dpi input to the given form element if possible to
277 * determine the values.
278 *
279 * This is normally employed in an onlick hook, but then you must specify IDs or
280 * the browser may end up adding it several times.
281 *
282 * @param sFormId The ID of the form to amend.
283 * @param sWidthSrcId The ID of the element to calculate the width
284 * value from.
285 * @param sWidthName The name of the width value.
286 * @param sDpiName The name of the dpi value.
287 */
288function addDynamicGraphInputs(sFormId, sWidthSrcId, sWidthName, sDpiName)
289{
290 var cx = getUnscaledElementWidthById(sWidthSrcId);
291 var cDotsPerInch = getDeviceXDotsPerInch();
292
293 if (cx)
294 {
295 addUpdateHiddenInputFieldToFormById(sFormId, sWidthName, cx, sFormId + '-' + sWidthName + '-id');
296 }
297
298 if (cDotsPerInch)
299 {
300 addUpdateHiddenInputFieldToFormById(sFormId, sDpiName, cDotsPerInch, sFormId + '-' + sDpiName + '-id');
301 }
302
303}
304
305/**
306 * Adds the RedirecTo field with the current URL to the form.
307 *
308 * This is a 'onsubmit' action.
309 *
310 * @returns Returns success indicator (true/false).
311 * @param oForm The form being submitted.
312 */
313function addRedirectToInputFieldWithCurrentUrl(oForm)
314{
315 /* Constant used here is duplicated in WuiDispatcherBase.ksParamRedirectTo */
316 return addHiddenInputFieldToForm(oForm, 'RedirectTo', getCurrentBrowerUrlPartForRedirectTo(), null);
317}
318
319/**
320 * Adds the RedirecTo parameter to the href of the given anchor.
321 *
322 * This is a 'onclick' action.
323 *
324 * @returns Returns success indicator (true/false).
325 * @param oAnchor The anchor element being clicked on.
326 */
327function addRedirectToAnchorHref(oAnchor)
328{
329 var sRedirectToParam = g_ksParamRedirectTo + '=' + encodeURIComponent(getCurrentBrowerUrlPartForRedirectTo());
330 var sHref = oAnchor.href;
331 if (sHref.indexOf(sRedirectToParam) < 0)
332 {
333 var sHash;
334 var offHash = sHref.indexOf('#');
335 if (offHash >= 0)
336 sHash = sHref.substring(offHash);
337 else
338 {
339 sHash = '';
340 offHash = sHref.length;
341 }
342 sHref = sHref.substring(0, offHash)
343 if (sHref.indexOf('?') >= 0)
344 sHref += '&';
345 else
346 sHref += '?';
347 sHref += sRedirectToParam;
348 sHref += sHash;
349 oAnchor.href = sHref;
350 }
351 return true;
352}
353
354
355/** @name Collapsible / Expandable items
356 * @{
357 */
358
359
360/**
361 * Toggles the collapsible / expandable state of a parent DD and DT uncle.
362 *
363 * @returns true
364 * @param oAnchor The anchor object.
365 */
366function toggleCollapsibleDtDd(oAnchor)
367{
368 var oParent = oAnchor.parentElement;
369 var sClass = oParent.className;
370
371 /* Find the DD sibling tag */
372 var oDdElement = oParent.nextSibling;
373 while (oDdElement != null && oDdElement.tagName != 'DD')
374 oDdElement = oDdElement.nextSibling;
375
376 /* Determin the new class and arrow char. */
377 var sNewClass;
378 var sNewChar;
379 if ( sClass.substr(-11) == 'collapsible')
380 {
381 sNewClass = sClass.substr(0, sClass.length - 11) + 'expandable';
382 sNewChar = '\u25B6'; /* black right-pointing triangle */
383 }
384 else if (sClass.substr(-10) == 'expandable')
385 {
386 sNewClass = sClass.substr(0, sClass.length - 10) + 'collapsible';
387 sNewChar = '\u25BC'; /* black down-pointing triangle */
388 }
389 else
390 {
391 console.log('toggleCollapsibleParent: Invalid class: ' + sClass);
392 return true;
393 }
394
395 /* Update the parent (DT) class and anchor text. */
396 oParent.className = sNewClass;
397 oAnchor.firstChild.textContent = sNewChar + oAnchor.firstChild.textContent.substr(1);
398
399 /* Update the uncle (DD) class. */
400 if (oDdElement)
401 oDdElement.className = sNewClass;
402 return true;
403}
404
405/**
406 * Shows/hides a sub-category UL according to checkbox status.
407 *
408 * The checkbox is expected to be within a label element or something.
409 *
410 * @returns true
411 * @param oInput The input checkbox.
412 */
413function toggleCollapsibleCheckbox(oInput)
414{
415 var oParent = oInput.parentElement;
416
417 /* Find the UL sibling element. */
418 var oUlElement = oParent.nextSibling;
419 while (oUlElement != null && oUlElement.tagName != 'UL')
420 oUlElement = oUlElement.nextSibling;
421
422 /* Change the visibility. */
423 if (oInput.checked)
424 oUlElement.className = oUlElement.className.replace('expandable', 'collapsible');
425 else
426 oUlElement.className = oUlElement.className.replace('collapsible', 'expandable');
427 return true;
428}
429
430/** @} */
431
432
433/** @name Custom Tooltips
434 * @{
435 */
436
437/** Where we keep tooltip elements when not displayed. */
438var g_dTooltips = {};
439var g_oCurrentTooltip = null;
440var g_idTooltipShowTimer = null;
441var g_idTooltipHideTimer = null;
442var g_cTooltipSvnRevisions = 12;
443
444/**
445 * Cancel showing/replacing/repositing a tooltip.
446 */
447function tooltipResetShowTimer()
448{
449 if (g_idTooltipShowTimer)
450 {
451 clearTimeout(g_idTooltipShowTimer);
452 g_idTooltipShowTimer = null;
453 }
454}
455
456/**
457 * Cancel hiding of the current tooltip.
458 */
459function tooltipResetHideTimer()
460{
461 if (g_idTooltipHideTimer)
462 {
463 clearTimeout(g_idTooltipHideTimer);
464 g_idTooltipHideTimer = null;
465 }
466}
467
468/**
469 * Really hide the tooltip.
470 */
471function tooltipReallyHide()
472{
473 if (g_oCurrentTooltip)
474 {
475 //console.log('tooltipReallyHide: ' + g_oCurrentTooltip);
476 g_oCurrentTooltip.oElm.style.display = 'none';
477 g_oCurrentTooltip = null;
478 }
479}
480
481/**
482 * Schedule the tooltip for hiding.
483 */
484function tooltipHide()
485{
486 function tooltipDelayedHide()
487 {
488 tooltipResetHideTimer();
489 tooltipReallyHide();
490 }
491
492 /*
493 * Cancel any pending show and schedule hiding if necessary.
494 */
495 tooltipResetShowTimer();
496 if (g_oCurrentTooltip && !g_idTooltipHideTimer)
497 {
498 g_idTooltipHideTimer = setTimeout(tooltipDelayedHide, 700);
499 }
500
501 return true;
502}
503
504/**
505 * Function that is repositions the tooltip when it's shown.
506 *
507 * Used directly, via onload, and hackish timers to catch all browsers and
508 * whatnot.
509 *
510 * Will set several tooltip member variables related to position and space.
511 */
512function tooltipRepositionOnLoad()
513{
514 if (g_oCurrentTooltip)
515 {
516 var oRelToRect = g_oCurrentTooltip.oRelToRect;
517 var cxNeeded = g_oCurrentTooltip.oElm.offsetWidth + 8;
518 var cyNeeded = g_oCurrentTooltip.oElm.offsetHeight + 8;
519
520 var yScroll = window.pageYOffset || document.documentElement.scrollTop;
521 var yScrollBottom = yScroll + window.innerHeight;
522 var xScroll = window.pageXOffset || document.documentElement.scrollLeft;
523 var xScrollRight = xScroll + window.innerWidth;
524
525 var cyAbove = Math.max(oRelToRect.top - yScroll, 0);
526 var cyBelow = Math.max(yScrollBottom - oRelToRect.bottom, 0);
527 var cxLeft = Math.max(oRelToRect.left - xScroll, 0);
528 var cxRight = Math.max(xScrollRight - oRelToRect.right, 0);
529
530 var xPos;
531 var yPos;
532
533 /*
534 * Decide where to put the thing.
535 */
536 if (cyNeeded < cyBelow)
537 {
538 yPos = oRelToRect.bottom;
539 g_oCurrentTooltip.cyMax = cyBelow;
540 }
541 else if (cyBelow >= cyAbove)
542 {
543 yPos = yScrollBottom - cyNeeded;
544 g_oCurrentTooltip.cyMax = yScrollBottom - yPos;
545 }
546 else
547 {
548 yPos = oRelToRect.top - cyNeeded;
549 g_oCurrentTooltip.cyMax = yScrollBottom - yPos;
550 }
551 if (yPos < yScroll)
552 {
553 yPos = yScroll;
554 g_oCurrentTooltip.cyMax = yScrollBottom - yPos;
555 }
556 g_oCurrentTooltip.yPos = yPos;
557 g_oCurrentTooltip.yScroll = yScroll;
558 g_oCurrentTooltip.cyMaxUp = yPos - yScroll;
559
560 if (cxNeeded < cxRight || cxNeeded > cxRight)
561 {
562 xPos = oRelToRect.right;
563 g_oCurrentTooltip.cxMax = cxRight;
564 }
565 else
566 {
567 xPos = oRelToRect.left - cxNeeded;
568 g_oCurrentTooltip.cxMax = cxNeeded;
569 }
570 g_oCurrentTooltip.xPos = xPos;
571 g_oCurrentTooltip.xScroll = xScroll;
572
573 g_oCurrentTooltip.oElm.style.top = yPos + 'px';
574 g_oCurrentTooltip.oElm.style.left = xPos + 'px';
575 }
576 return true;
577}
578
579
580/**
581 * Really show the tooltip.
582 *
583 * @param oTooltip The tooltip object.
584 * @param oRelTo What to put the tooltip adjecent to.
585 */
586function tooltipReallyShow(oTooltip, oRelTo)
587{
588 var oRect;
589
590 tooltipResetShowTimer();
591 tooltipResetHideTimer();
592
593 if (g_oCurrentTooltip == oTooltip)
594 {
595 //console.log('moving tooltip');
596 }
597 else if (g_oCurrentTooltip)
598 {
599 //console.log('removing current tooltip and showing new');
600 tooltipReallyHide();
601 }
602 else
603 {
604 //console.log('showing tooltip');
605 }
606
607 oTooltip.oElm.style.display = 'block';
608 oTooltip.oElm.style.position = 'absolute';
609 oRect = oRelTo.getBoundingClientRect();
610 oTooltip.oRelToRect = oRect;
611 oTooltip.oElm.style.left = oRect.right + 'px';
612 oTooltip.oElm.style.top = oRect.bottom + 'px';
613
614 g_oCurrentTooltip = oTooltip;
615
616 /*
617 * This function does the repositioning at some point.
618 */
619 tooltipRepositionOnLoad();
620 if (oTooltip.oElm.onload === null)
621 {
622 oTooltip.oElm.onload = function(){ tooltipRepositionOnLoad(); setTimeout(tooltipRepositionOnLoad, 0); };
623 }
624}
625
626/**
627 * Tooltip onmouseenter handler .
628 */
629function tooltipElementOnMouseEnter()
630{
631 //console.log('tooltipElementOnMouseEnter: arguments.length='+arguments.length+' [0]='+arguments[0]);
632 //console.log('ENT: currentTarget='+arguments[0].currentTarget);
633 tooltipResetShowTimer();
634 tooltipResetHideTimer();
635 return true;
636}
637
638/**
639 * Tooltip onmouseout handler.
640 *
641 * @remarks We only use this and onmouseenter for one tooltip element (iframe
642 * for svn, because chrome is sending onmouseout events after
643 * onmouseneter for the next element, which would confuse this simple
644 * code.
645 */
646function tooltipElementOnMouseOut()
647{
648 //console.log('tooltipElementOnMouseOut: arguments.length='+arguments.length+' [0]='+arguments[0]);
649 //console.log('OUT: currentTarget='+arguments[0].currentTarget);
650 tooltipHide();
651 return true;
652}
653
654/**
655 * iframe.onload hook that repositions and resizes the tooltip.
656 *
657 * This is a little hacky and we're calling it one or three times too many to
658 * work around various browser differences too.
659 */
660function svnHistoryTooltipOnLoad()
661{
662 //console.log('svnHistoryTooltipOnLoad');
663
664 /*
665 * Resize the tooltip to better fit the content.
666 */
667 tooltipRepositionOnLoad(); /* Sets cxMax and cyMax. */
668 if (g_oCurrentTooltip && g_oCurrentTooltip.oIFrame.contentWindow)
669 {
670 var oSubElement = g_oCurrentTooltip.oIFrame;
671 var cxSpace = Math.max(oSubElement.offsetLeft * 2, 0); /* simplified */
672 var cySpace = Math.max(oSubElement.offsetTop * 2, 0); /* simplified */
673 var cxNeeded = oSubElement.contentWindow.document.body.scrollWidth + cxSpace;
674 var cyNeeded = oSubElement.contentWindow.document.body.scrollHeight + cySpace;
675 var cx = Math.min(cxNeeded, g_oCurrentTooltip.cxMax);
676 var cy;
677
678 g_oCurrentTooltip.oElm.width = cx + 'px';
679 oSubElement.width = (cx - cxSpace) + 'px';
680 if (cx >= cxNeeded)
681 {
682 //console.log('svnHistoryTooltipOnLoad: overflowX -> hidden');
683 oSubElement.style.overflowX = 'hidden';
684 }
685 else
686 {
687 oSubElement.style.overflowX = 'scroll';
688 }
689
690 cy = Math.min(cyNeeded, g_oCurrentTooltip.cyMax);
691 if (cyNeeded > g_oCurrentTooltip.cyMax && g_oCurrentTooltip.cyMaxUp > 0)
692 {
693 var cyMove = Math.min(cyNeeded - g_oCurrentTooltip.cyMax, g_oCurrentTooltip.cyMaxUp);
694 g_oCurrentTooltip.cyMax += cyMove;
695 g_oCurrentTooltip.yPos -= cyMove;
696 g_oCurrentTooltip.oElm.style.top = g_oCurrentTooltip.yPos + 'px';
697 cy = Math.min(cyNeeded, g_oCurrentTooltip.cyMax);
698 }
699
700 g_oCurrentTooltip.oElm.height = cy + 'px';
701 oSubElement.height = (cy - cySpace) + 'px';
702 if (cy >= cyNeeded)
703 {
704 //console.log('svnHistoryTooltipOnLoad: overflowY -> hidden');
705 oSubElement.style.overflowY = 'hidden';
706 }
707 else
708 {
709 oSubElement.style.overflowY = 'scroll';
710 }
711
712 //console.log('cyNeeded='+cyNeeded+' cyMax='+g_oCurrentTooltip.cyMax+' cySpace='+cySpace+' cy='+cy);
713 //console.log('oSubElement.offsetTop='+oSubElement.offsetTop);
714 //console.log('svnHistoryTooltipOnLoad: cx='+cx+'cxMax='+g_oCurrentTooltip.cxMax+' cxNeeded='+cxNeeded+' cy='+cy+' cyMax='+g_oCurrentTooltip.cyMax);
715
716 tooltipRepositionOnLoad();
717 }
718 return true;
719}
720
721/**
722 * Calculates the last revision to get when showing a tooltip for @a iRevision.
723 *
724 * A tooltip covers several change log entries, both to limit the number of
725 * tooltips to load and to give context. The exact number is defined by
726 * g_cTooltipSvnRevisions.
727 *
728 * @returns Last revision in a tooltip.
729 * @param iRevision The revision number.
730 */
731function svnHistoryTooltipCalcLastRevision(iRevision)
732{
733 var iFirstRev = Math.floor(iRevision / g_cTooltipSvnRevisions) * g_cTooltipSvnRevisions;
734 return iFirstRev + g_cTooltipSvnRevisions - 1;
735}
736
737/**
738 * Calculates a unique ID for the tooltip element.
739 *
740 * This is also used as dictionary index.
741 *
742 * @returns tooltip ID value (string).
743 * @param sRepository The repository name.
744 * @param iRevision The revision number.
745 */
746function svnHistoryTooltipCalcId(sRepository, iRevision)
747{
748 return 'svnHistoryTooltip_' + sRepository + '_' + svnHistoryTooltipCalcLastRevision(iRevision);
749}
750
751/**
752 * The onmouseenter event handler for creating the tooltip.
753 *
754 * @param oEvt The event.
755 * @param sRepository The repository name.
756 * @param iRevision The revision number.
757 *
758 * @remarks onmouseout must be set to call tooltipHide.
759 */
760function svnHistoryTooltipShow(oEvt, sRepository, iRevision)
761{
762 var sKey = svnHistoryTooltipCalcId(sRepository, iRevision);
763 var oTooltip = g_dTooltips[sKey];
764 var oParent = oEvt.currentTarget;
765 //console.log('svnHistoryTooltipShow ' + sRepository);
766
767 function svnHistoryTooltipDelayedShow()
768 {
769 var oSubElement;
770 var sSrc;
771
772 oTooltip = g_dTooltips[sKey];
773 //console.log('svnHistoryTooltipDelayedShow ' + sRepository + ' ' + oTooltip);
774 if (!oTooltip)
775 {
776 /*
777 * Create a new tooltip element.
778 */
779 //console.log('creating ' + sKey);
780 oTooltip = {};
781 oTooltip.oElm = document.createElement('div');
782 oTooltip.oElm.setAttribute('id', sKey);
783 oTooltip.oElm.setAttribute('class', 'tmvcstooltip');
784 oTooltip.oElm.style.position = 'absolute';
785 oTooltip.oElm.style.zIndex = 6001;
786 oTooltip.xPos = 0;
787 oTooltip.yPos = 0;
788 oTooltip.cxMax = 0;
789 oTooltip.cyMax = 0;
790 oTooltip.cyMaxUp = 0;
791 oTooltip.xScroll = 0;
792 oTooltip.yScroll = 0;
793
794 oSubElement = document.createElement('iframe');
795 oSubElement.setAttribute('id', sKey + '_iframe');
796 oSubElement.setAttribute('style', 'position: relative;"');
797 oSubElement.onload = function() {svnHistoryTooltipOnLoad(); setTimeout(svnHistoryTooltipOnLoad,0);};
798 oSubElement.onmouseenter = tooltipElementOnMouseEnter;
799 oSubElement.onmouseout = tooltipElementOnMouseOut;
800 oTooltip.oElm.appendChild(oSubElement);
801 oTooltip.oIFrame = oSubElement;
802 g_dTooltips[sKey] = oTooltip;
803
804 document.body.appendChild(oTooltip.oElm);
805 }
806 else
807 {
808 oSubElement = oTooltip.oIFrame;
809 }
810
811 oSubElement.setAttribute('src', 'index.py?Action=VcsHistoryTooltip&repo=' + sRepository
812 + '&rev=' + svnHistoryTooltipCalcLastRevision(iRevision)
813 + '&cEntries=' + g_cTooltipSvnRevisions
814 + '#r' + iRevision);
815 tooltipReallyShow(oTooltip, oParent);
816 /* Resize and repositioning hacks. */
817 svnHistoryTooltipOnLoad();
818 setTimeout(svnHistoryTooltipOnLoad, 0);
819 }
820
821 /*
822 * Delay the change.
823 */
824 tooltipResetShowTimer();
825 g_idTooltipShowTimer = setTimeout(svnHistoryTooltipDelayedShow, 512);
826}
827
828/** @} */
829
830
831/** @name Debugging and Introspection
832 * @{
833 */
834
835/**
836 * Python-like dir() implementation.
837 *
838 * @returns Array of names associated with oObj.
839 * @param oObj The object under inspection. If not specified we'll
840 * look at the window object.
841 */
842function pythonlikeDir(oObj, fDeep)
843{
844 var aRet = [];
845 var dTmp = {};
846
847 if (!oObj)
848 {
849 oObj = window;
850 }
851
852 for (var oCur = oObj; oCur; oCur = Object.getPrototypeOf(oCur))
853 {
854 var aThis = Object.getOwnPropertyNames(oCur);
855 for (var i = 0; i < aThis.length; i++)
856 {
857 if (!(aThis[i] in dTmp))
858 {
859 dTmp[aThis[i]] = 1;
860 aRet.push(aThis[i]);
861 }
862 }
863 }
864
865 return aRet;
866}
867
868
869/**
870 * Python-like dir() implementation, shallow version.
871 *
872 * @returns Array of names associated with oObj.
873 * @param oObj The object under inspection. If not specified we'll
874 * look at the window object.
875 */
876function pythonlikeShallowDir(oObj, fDeep)
877{
878 var aRet = [];
879 var dTmp = {};
880
881 if (oObj)
882 {
883 for (var i in oObj)
884 {
885 aRet.push(i);
886 }
887 }
888
889 return aRet;
890}
891
892
893
894function dbgGetObjType(oObj)
895{
896 var sType = typeof oObj;
897 if (sType == "object" && oObj !== null)
898 {
899 if (oObj.constructor && oObj.constructor.name)
900 {
901 sType = oObj.constructor.name;
902 }
903 else
904 {
905 var fnToString = Object.prototype.toString;
906 var sTmp = fnToString.call(oObj);
907 if (sTmp.indexOf('[object ') === 0)
908 {
909 sType = sTmp.substring(8, sTmp.length);
910 }
911 }
912 }
913 return sType;
914}
915
916
917/**
918 * Dumps the given object to the console.
919 *
920 * @param oObj The object under inspection.
921 * @param sPrefix What to prefix the log output with.
922 */
923function dbgDumpObj(oObj, sName, sPrefix)
924{
925 var aMembers;
926 var sType;
927
928 /*
929 * Defaults
930 */
931 if (!oObj)
932 {
933 oObj = window;
934 }
935
936 if (!sPrefix)
937 {
938 if (sName)
939 {
940 sPrefix = sName + ':';
941 }
942 else
943 {
944 sPrefix = 'dbgDumpObj:';
945 }
946 }
947
948 if (!sName)
949 {
950 sName = '';
951 }
952
953 /*
954 * The object itself.
955 */
956 sPrefix = sPrefix + ' ';
957 console.log(sPrefix + sName + ' ' + dbgGetObjType(oObj));
958
959 /*
960 * The members.
961 */
962 sPrefix = sPrefix + ' ';
963 aMembers = pythonlikeDir(oObj);
964 for (i = 0; i < aMembers.length; i++)
965 {
966 console.log(sPrefix + aMembers[i]);
967 }
968
969 return true;
970}
971
972function dbgDumpObjWorker(sType, sName, oObj, sPrefix)
973{
974 var sRet;
975 switch (sType)
976 {
977 case 'function':
978 {
979 sRet = sPrefix + 'function ' + sName + '()' + '\n';
980 break;
981 }
982
983 case 'object':
984 {
985 sRet = sPrefix + 'var ' + sName + '(' + dbgGetObjType(oObj) + ') =';
986 if (oObj !== null)
987 {
988 sRet += '\n';
989 }
990 else
991 {
992 sRet += ' null\n';
993 }
994 break;
995 }
996
997 case 'string':
998 {
999 sRet = sPrefix + 'var ' + sName + '(string, ' + oObj.length + ')';
1000 if (oObj.length < 80)
1001 {
1002 sRet += ' = "' + oObj + '"\n';
1003 }
1004 else
1005 {
1006 sRet += '\n';
1007 }
1008 break;
1009 }
1010
1011 case 'Oops!':
1012 sRet = sPrefix + sName + '(??)\n';
1013 break;
1014
1015 default:
1016 sRet = sPrefix + 'var ' + sName + '(' + sType + ')\n';
1017 break;
1018 }
1019 return sRet;
1020}
1021
1022
1023function dbgObjInArray(aoObjs, oObj)
1024{
1025 var i = aoObjs.length;
1026 while (i > 0)
1027 {
1028 i--;
1029 if (aoObjs[i] === oObj)
1030 {
1031 return true;
1032 }
1033 }
1034 return false;
1035}
1036
1037function dbgDumpObjTreeWorker(oObj, sPrefix, aParentObjs, cMaxDepth)
1038{
1039 var sRet = '';
1040 var aMembers = pythonlikeShallowDir(oObj);
1041 var i;
1042
1043 for (i = 0; i < aMembers.length; i++)
1044 {
1045 //var sName = i;
1046 var sName = aMembers[i];
1047 var oMember;
1048 var sType;
1049 var oEx;
1050
1051 try
1052 {
1053 oMember = oObj[sName];
1054 sType = typeof oObj[sName];
1055 }
1056 catch (oEx)
1057 {
1058 oMember = null;
1059 sType = 'Oops!';
1060 }
1061
1062 //sRet += '[' + i + '/' + aMembers.length + ']';
1063 sRet += dbgDumpObjWorker(sType, sName, oMember, sPrefix);
1064
1065 if ( sType == 'object'
1066 && oObj !== null)
1067 {
1068
1069 if (dbgObjInArray(aParentObjs, oMember))
1070 {
1071 sRet += sPrefix + '! parent recursion\n';
1072 }
1073 else if ( sName == 'previousSibling'
1074 || sName == 'previousElement'
1075 || sName == 'lastChild'
1076 || sName == 'firstElementChild'
1077 || sName == 'lastElementChild'
1078 || sName == 'nextElementSibling'
1079 || sName == 'prevElementSibling'
1080 || sName == 'parentElement'
1081 || sName == 'ownerDocument')
1082 {
1083 sRet += sPrefix + '! potentially dangerous element name\n';
1084 }
1085 else if (aParentObjs.length >= cMaxDepth)
1086 {
1087 sRet = sRet.substring(0, sRet.length - 1);
1088 sRet += ' <too deep>!\n';
1089 }
1090 else
1091 {
1092
1093 aParentObjs.push(oMember);
1094 if (i + 1 < aMembers.length)
1095 {
1096 sRet += dbgDumpObjTreeWorker(oMember, sPrefix + '| ', aParentObjs, cMaxDepth);
1097 }
1098 else
1099 {
1100 sRet += dbgDumpObjTreeWorker(oMember, sPrefix.substring(0, sPrefix.length - 2) + ' | ', aParentObjs, cMaxDepth);
1101 }
1102 aParentObjs.pop();
1103 }
1104 }
1105 }
1106 return sRet;
1107}
1108
1109/**
1110 * Dumps the given object and all it's subobjects to the console.
1111 *
1112 * @returns String dump of the object.
1113 * @param oObj The object under inspection.
1114 * @param sName The object name (optional).
1115 * @param sPrefix What to prefix the log output with (optional).
1116 * @param cMaxDepth The max depth, optional.
1117 */
1118function dbgDumpObjTree(oObj, sName, sPrefix, cMaxDepth)
1119{
1120 var sType;
1121 var sRet;
1122 var oEx;
1123
1124 /*
1125 * Defaults
1126 */
1127 if (!sPrefix)
1128 {
1129 sPrefix = '';
1130 }
1131
1132 if (!sName)
1133 {
1134 sName = '??';
1135 }
1136
1137 if (!cMaxDepth)
1138 {
1139 cMaxDepth = 2;
1140 }
1141
1142 /*
1143 * The object itself.
1144 */
1145 try
1146 {
1147 sType = typeof oObj;
1148 }
1149 catch (oEx)
1150 {
1151 sType = 'Oops!';
1152 }
1153 sRet = dbgDumpObjWorker(sType, sName, oObj, sPrefix);
1154 if (sType == 'object' && oObj !== null)
1155 {
1156 var aParentObjs = Array();
1157 aParentObjs.push(oObj);
1158 sRet += dbgDumpObjTreeWorker(oObj, sPrefix + '| ', aParentObjs, cMaxDepth);
1159 }
1160
1161 return sRet;
1162}
1163
1164function dbgLogString(sLongString)
1165{
1166 var aStrings = sLongString.split("\n");
1167 var i;
1168 for (i = 0; i < aStrings.length; i++)
1169 {
1170 console.log(aStrings[i]);
1171 }
1172 console.log('dbgLogString - end - ' + aStrings.length + '/' + sLongString.length);
1173 return true;
1174}
1175
1176function dbgLogObjTree(oObj, sName, sPrefix, cMaxDepth)
1177{
1178 return dbgLogString(dbgDumpObjTree(oObj, sName, sPrefix, cMaxDepth));
1179}
1180
1181/** @} */
1182
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette