VirtualBox

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

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

testmanager/webui: started on test result filtering.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.4 KB
Line 
1/* $Id: common.js 64986 2016-12-21 14:36:33Z 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 Collapsable / Expandable items
356 * @{
357 */
358
359
360/**
361 * Toggles the collapsable / expandable state of a parent DD and DT unclke.
362 *
363 * @returns true
364 * @param oAnchor The anchor object.
365 */
366function toggleCollapsableDtDd(oAnchor)
367{
368 var oParent = oAnchor.parentElement;
369 var sClass = oParent.className;
370 var oUncle = oParent.nextSibling;
371 var sNewClass;
372 var sNewChar;
373
374 /* Determin the new class and arrow char. */
375 if (sClass.endsWith('collapsable'))
376 {
377 sNewClass = sClass.substr(0, sClass.length - 11) + 'expandable';
378 sNewChar = '\u25B6'; /* black right-pointing triangle */
379 }
380 else if (sClass.endsWith('expandable'))
381 {
382 sNewClass = sClass.substr(0, sClass.length - 10) + 'collapsable';
383 sNewChar = '\u25BC'; /* black down-pointing triangle */
384 }
385 else
386 {
387 console.log('toggleCollapsableParent: Invalid class: ' + sClass);
388 return true;
389 }
390
391 /* Update the parent (DT) class and anchor text. */
392 oParent.className = sNewClass;
393 oAnchor.firstChild.textContent = sNewChar + oAnchor.firstChild.textContent.substr(1);
394
395 /* Update the uncle (DD) class.*/
396 if (oUncle)
397 oUncle.className = sNewClass;
398 return true;
399}
400
401/** @} */
402
403
404/** @name Custom Tooltips
405 * @{
406 */
407
408/** Where we keep tooltip elements when not displayed. */
409var g_dTooltips = {};
410var g_oCurrentTooltip = null;
411var g_idTooltipShowTimer = null;
412var g_idTooltipHideTimer = null;
413var g_cTooltipSvnRevisions = 12;
414
415/**
416 * Cancel showing/replacing/repositing a tooltip.
417 */
418function tooltipResetShowTimer()
419{
420 if (g_idTooltipShowTimer)
421 {
422 clearTimeout(g_idTooltipShowTimer);
423 g_idTooltipShowTimer = null;
424 }
425}
426
427/**
428 * Cancel hiding of the current tooltip.
429 */
430function tooltipResetHideTimer()
431{
432 if (g_idTooltipHideTimer)
433 {
434 clearTimeout(g_idTooltipHideTimer);
435 g_idTooltipHideTimer = null;
436 }
437}
438
439/**
440 * Really hide the tooltip.
441 */
442function tooltipReallyHide()
443{
444 if (g_oCurrentTooltip)
445 {
446 //console.log('tooltipReallyHide: ' + g_oCurrentTooltip);
447 g_oCurrentTooltip.oElm.style.display = 'none';
448 g_oCurrentTooltip = null;
449 }
450}
451
452/**
453 * Schedule the tooltip for hiding.
454 */
455function tooltipHide()
456{
457 function tooltipDelayedHide()
458 {
459 tooltipResetHideTimer();
460 tooltipReallyHide();
461 }
462
463 /*
464 * Cancel any pending show and schedule hiding if necessary.
465 */
466 tooltipResetShowTimer();
467 if (g_oCurrentTooltip && !g_idTooltipHideTimer)
468 {
469 g_idTooltipHideTimer = setTimeout(tooltipDelayedHide, 700);
470 }
471
472 return true;
473}
474
475/**
476 * Function that is repositions the tooltip when it's shown.
477 *
478 * Used directly, via onload, and hackish timers to catch all browsers and
479 * whatnot.
480 *
481 * Will set several tooltip member variables related to position and space.
482 */
483function tooltipRepositionOnLoad()
484{
485 if (g_oCurrentTooltip)
486 {
487 var oRelToRect = g_oCurrentTooltip.oRelToRect;
488 var cxNeeded = g_oCurrentTooltip.oElm.offsetWidth + 8;
489 var cyNeeded = g_oCurrentTooltip.oElm.offsetHeight + 8;
490
491 var yScroll = window.pageYOffset || document.documentElement.scrollTop;
492 var yScrollBottom = yScroll + window.innerHeight;
493 var xScroll = window.pageXOffset || document.documentElement.scrollLeft;
494 var xScrollRight = xScroll + window.innerWidth;
495
496 var cyAbove = Math.max(oRelToRect.top - yScroll, 0);
497 var cyBelow = Math.max(yScrollBottom - oRelToRect.bottom, 0);
498 var cxLeft = Math.max(oRelToRect.left - xScroll, 0);
499 var cxRight = Math.max(xScrollRight - oRelToRect.right, 0);
500
501 var xPos;
502 var yPos;
503
504 /*
505 * Decide where to put the thing.
506 */
507 if (cyNeeded < cyBelow)
508 {
509 yPos = oRelToRect.bottom;
510 g_oCurrentTooltip.cyMax = cyBelow;
511 }
512 else if (cyBelow >= cyAbove)
513 {
514 yPos = yScrollBottom - cyNeeded;
515 g_oCurrentTooltip.cyMax = yScrollBottom - yPos;
516 }
517 else
518 {
519 yPos = oRelToRect.top - cyNeeded;
520 g_oCurrentTooltip.cyMax = yScrollBottom - yPos;
521 }
522 if (yPos < yScroll)
523 {
524 yPos = yScroll;
525 g_oCurrentTooltip.cyMax = yScrollBottom - yPos;
526 }
527 g_oCurrentTooltip.yPos = yPos;
528 g_oCurrentTooltip.yScroll = yScroll;
529 g_oCurrentTooltip.cyMaxUp = yPos - yScroll;
530
531 if (cxNeeded < cxRight || cxNeeded > cxRight)
532 {
533 xPos = oRelToRect.right;
534 g_oCurrentTooltip.cxMax = cxRight;
535 }
536 else
537 {
538 xPos = oRelToRect.left - cxNeeded;
539 g_oCurrentTooltip.cxMax = cxNeeded;
540 }
541 g_oCurrentTooltip.xPos = xPos;
542 g_oCurrentTooltip.xScroll = xScroll;
543
544 g_oCurrentTooltip.oElm.style.top = yPos + 'px';
545 g_oCurrentTooltip.oElm.style.left = xPos + 'px';
546 }
547 return true;
548}
549
550
551/**
552 * Really show the tooltip.
553 *
554 * @param oTooltip The tooltip object.
555 * @param oRelTo What to put the tooltip adjecent to.
556 */
557function tooltipReallyShow(oTooltip, oRelTo)
558{
559 var oRect;
560
561 tooltipResetShowTimer();
562 tooltipResetHideTimer();
563
564 if (g_oCurrentTooltip == oTooltip)
565 {
566 //console.log('moving tooltip');
567 }
568 else if (g_oCurrentTooltip)
569 {
570 //console.log('removing current tooltip and showing new');
571 tooltipReallyHide();
572 }
573 else
574 {
575 //console.log('showing tooltip');
576 }
577
578 oTooltip.oElm.style.display = 'block';
579 oTooltip.oElm.style.position = 'absolute';
580 oRect = oRelTo.getBoundingClientRect();
581 oTooltip.oRelToRect = oRect;
582 oTooltip.oElm.style.left = oRect.right + 'px';
583 oTooltip.oElm.style.top = oRect.bottom + 'px';
584
585 g_oCurrentTooltip = oTooltip;
586
587 /*
588 * This function does the repositioning at some point.
589 */
590 tooltipRepositionOnLoad();
591 if (oTooltip.oElm.onload === null)
592 {
593 oTooltip.oElm.onload = function(){ tooltipRepositionOnLoad(); setTimeout(tooltipRepositionOnLoad, 0); };
594 }
595}
596
597/**
598 * Tooltip onmouseenter handler .
599 */
600function tooltipElementOnMouseEnter()
601{
602 //console.log('tooltipElementOnMouseEnter: arguments.length='+arguments.length+' [0]='+arguments[0]);
603 //console.log('ENT: currentTarget='+arguments[0].currentTarget);
604 tooltipResetShowTimer();
605 tooltipResetHideTimer();
606 return true;
607}
608
609/**
610 * Tooltip onmouseout handler.
611 *
612 * @remarks We only use this and onmouseenter for one tooltip element (iframe
613 * for svn, because chrome is sending onmouseout events after
614 * onmouseneter for the next element, which would confuse this simple
615 * code.
616 */
617function tooltipElementOnMouseOut()
618{
619 //console.log('tooltipElementOnMouseOut: arguments.length='+arguments.length+' [0]='+arguments[0]);
620 //console.log('OUT: currentTarget='+arguments[0].currentTarget);
621 tooltipHide();
622 return true;
623}
624
625/**
626 * iframe.onload hook that repositions and resizes the tooltip.
627 *
628 * This is a little hacky and we're calling it one or three times too many to
629 * work around various browser differences too.
630 */
631function svnHistoryTooltipOnLoad()
632{
633 //console.log('svnHistoryTooltipOnLoad');
634
635 /*
636 * Resize the tooltip to better fit the content.
637 */
638 tooltipRepositionOnLoad(); /* Sets cxMax and cyMax. */
639 if (g_oCurrentTooltip && g_oCurrentTooltip.oIFrame.contentWindow)
640 {
641 var oSubElement = g_oCurrentTooltip.oIFrame;
642 var cxSpace = Math.max(oSubElement.offsetLeft * 2, 0); /* simplified */
643 var cySpace = Math.max(oSubElement.offsetTop * 2, 0); /* simplified */
644 var cxNeeded = oSubElement.contentWindow.document.body.scrollWidth + cxSpace;
645 var cyNeeded = oSubElement.contentWindow.document.body.scrollHeight + cySpace;
646 var cx = Math.min(cxNeeded, g_oCurrentTooltip.cxMax);
647 var cy;
648
649 g_oCurrentTooltip.oElm.width = cx + 'px';
650 oSubElement.width = (cx - cxSpace) + 'px';
651 if (cx >= cxNeeded)
652 {
653 //console.log('svnHistoryTooltipOnLoad: overflowX -> hidden');
654 oSubElement.style.overflowX = 'hidden';
655 }
656 else
657 {
658 oSubElement.style.overflowX = 'scroll';
659 }
660
661 cy = Math.min(cyNeeded, g_oCurrentTooltip.cyMax);
662 if (cyNeeded > g_oCurrentTooltip.cyMax && g_oCurrentTooltip.cyMaxUp > 0)
663 {
664 var cyMove = Math.min(cyNeeded - g_oCurrentTooltip.cyMax, g_oCurrentTooltip.cyMaxUp);
665 g_oCurrentTooltip.cyMax += cyMove;
666 g_oCurrentTooltip.yPos -= cyMove;
667 g_oCurrentTooltip.oElm.style.top = g_oCurrentTooltip.yPos + 'px';
668 cy = Math.min(cyNeeded, g_oCurrentTooltip.cyMax);
669 }
670
671 g_oCurrentTooltip.oElm.height = cy + 'px';
672 oSubElement.height = (cy - cySpace) + 'px';
673 if (cy >= cyNeeded)
674 {
675 //console.log('svnHistoryTooltipOnLoad: overflowY -> hidden');
676 oSubElement.style.overflowY = 'hidden';
677 }
678 else
679 {
680 oSubElement.style.overflowY = 'scroll';
681 }
682
683 //console.log('cyNeeded='+cyNeeded+' cyMax='+g_oCurrentTooltip.cyMax+' cySpace='+cySpace+' cy='+cy);
684 //console.log('oSubElement.offsetTop='+oSubElement.offsetTop);
685 //console.log('svnHistoryTooltipOnLoad: cx='+cx+'cxMax='+g_oCurrentTooltip.cxMax+' cxNeeded='+cxNeeded+' cy='+cy+' cyMax='+g_oCurrentTooltip.cyMax);
686
687 tooltipRepositionOnLoad();
688 }
689 return true;
690}
691
692/**
693 * Calculates the last revision to get when showing a tooltip for @a iRevision.
694 *
695 * A tooltip covers several change log entries, both to limit the number of
696 * tooltips to load and to give context. The exact number is defined by
697 * g_cTooltipSvnRevisions.
698 *
699 * @returns Last revision in a tooltip.
700 * @param iRevision The revision number.
701 */
702function svnHistoryTooltipCalcLastRevision(iRevision)
703{
704 var iFirstRev = Math.floor(iRevision / g_cTooltipSvnRevisions) * g_cTooltipSvnRevisions;
705 return iFirstRev + g_cTooltipSvnRevisions - 1;
706}
707
708/**
709 * Calculates a unique ID for the tooltip element.
710 *
711 * This is also used as dictionary index.
712 *
713 * @returns tooltip ID value (string).
714 * @param sRepository The repository name.
715 * @param iRevision The revision number.
716 */
717function svnHistoryTooltipCalcId(sRepository, iRevision)
718{
719 return 'svnHistoryTooltip_' + sRepository + '_' + svnHistoryTooltipCalcLastRevision(iRevision);
720}
721
722/**
723 * The onmouseenter event handler for creating the tooltip.
724 *
725 * @param oEvt The event.
726 * @param sRepository The repository name.
727 * @param iRevision The revision number.
728 *
729 * @remarks onmouseout must be set to call tooltipHide.
730 */
731function svnHistoryTooltipShow(oEvt, sRepository, iRevision)
732{
733 var sKey = svnHistoryTooltipCalcId(sRepository, iRevision);
734 var oTooltip = g_dTooltips[sKey];
735 var oParent = oEvt.currentTarget;
736 //console.log('svnHistoryTooltipShow ' + sRepository);
737
738 function svnHistoryTooltipDelayedShow()
739 {
740 var oSubElement;
741 var sSrc;
742
743 oTooltip = g_dTooltips[sKey];
744 //console.log('svnHistoryTooltipDelayedShow ' + sRepository + ' ' + oTooltip);
745 if (!oTooltip)
746 {
747 /*
748 * Create a new tooltip element.
749 */
750 //console.log('creating ' + sKey);
751 oTooltip = {};
752 oTooltip.oElm = document.createElement('div');
753 oTooltip.oElm.setAttribute('id', sKey);
754 oTooltip.oElm.setAttribute('class', 'tmvcstooltip');
755 oTooltip.oElm.style.position = 'absolute';
756 oTooltip.oElm.style.zIndex = 6001;
757 oTooltip.xPos = 0;
758 oTooltip.yPos = 0;
759 oTooltip.cxMax = 0;
760 oTooltip.cyMax = 0;
761 oTooltip.cyMaxUp = 0;
762 oTooltip.xScroll = 0;
763 oTooltip.yScroll = 0;
764
765 oSubElement = document.createElement('iframe');
766 oSubElement.setAttribute('id', sKey + '_iframe');
767 oSubElement.setAttribute('style', 'position: relative;"');
768 oSubElement.onload = function() {svnHistoryTooltipOnLoad(); setTimeout(svnHistoryTooltipOnLoad,0);};
769 oSubElement.onmouseenter = tooltipElementOnMouseEnter;
770 oSubElement.onmouseout = tooltipElementOnMouseOut;
771 oTooltip.oElm.appendChild(oSubElement);
772 oTooltip.oIFrame = oSubElement;
773 g_dTooltips[sKey] = oTooltip;
774
775 document.body.appendChild(oTooltip.oElm);
776 }
777 else
778 {
779 oSubElement = oTooltip.oIFrame;
780 }
781
782 oSubElement.setAttribute('src', 'index.py?Action=VcsHistoryTooltip&repo=' + sRepository
783 + '&rev=' + svnHistoryTooltipCalcLastRevision(iRevision)
784 + '&cEntries=' + g_cTooltipSvnRevisions
785 + '#r' + iRevision);
786 tooltipReallyShow(oTooltip, oParent);
787 /* Resize and repositioning hacks. */
788 svnHistoryTooltipOnLoad();
789 setTimeout(svnHistoryTooltipOnLoad, 0);
790 }
791
792 /*
793 * Delay the change.
794 */
795 tooltipResetShowTimer();
796 g_idTooltipShowTimer = setTimeout(svnHistoryTooltipDelayedShow, 512);
797}
798
799/** @} */
800
801
802/** @name Debugging and Introspection
803 * @{
804 */
805
806/**
807 * Python-like dir() implementation.
808 *
809 * @returns Array of names associated with oObj.
810 * @param oObj The object under inspection. If not specified we'll
811 * look at the window object.
812 */
813function pythonlikeDir(oObj, fDeep)
814{
815 var aRet = [];
816 var dTmp = {};
817
818 if (!oObj)
819 {
820 oObj = window;
821 }
822
823 for (var oCur = oObj; oCur; oCur = Object.getPrototypeOf(oCur))
824 {
825 var aThis = Object.getOwnPropertyNames(oCur);
826 for (var i = 0; i < aThis.length; i++)
827 {
828 if (!(aThis[i] in dTmp))
829 {
830 dTmp[aThis[i]] = 1;
831 aRet.push(aThis[i]);
832 }
833 }
834 }
835
836 return aRet;
837}
838
839
840/**
841 * Python-like dir() implementation, shallow version.
842 *
843 * @returns Array of names associated with oObj.
844 * @param oObj The object under inspection. If not specified we'll
845 * look at the window object.
846 */
847function pythonlikeShallowDir(oObj, fDeep)
848{
849 var aRet = [];
850 var dTmp = {};
851
852 if (oObj)
853 {
854 for (var i in oObj)
855 {
856 aRet.push(i);
857 }
858 }
859
860 return aRet;
861}
862
863
864
865function dbgGetObjType(oObj)
866{
867 var sType = typeof oObj;
868 if (sType == "object" && oObj !== null)
869 {
870 if (oObj.constructor && oObj.constructor.name)
871 {
872 sType = oObj.constructor.name;
873 }
874 else
875 {
876 var fnToString = Object.prototype.toString;
877 var sTmp = fnToString.call(oObj);
878 if (sTmp.indexOf('[object ') === 0)
879 {
880 sType = sTmp.substring(8, sTmp.length);
881 }
882 }
883 }
884 return sType;
885}
886
887
888/**
889 * Dumps the given object to the console.
890 *
891 * @param oObj The object under inspection.
892 * @param sPrefix What to prefix the log output with.
893 */
894function dbgDumpObj(oObj, sName, sPrefix)
895{
896 var aMembers;
897 var sType;
898
899 /*
900 * Defaults
901 */
902 if (!oObj)
903 {
904 oObj = window;
905 }
906
907 if (!sPrefix)
908 {
909 if (sName)
910 {
911 sPrefix = sName + ':';
912 }
913 else
914 {
915 sPrefix = 'dbgDumpObj:';
916 }
917 }
918
919 if (!sName)
920 {
921 sName = '';
922 }
923
924 /*
925 * The object itself.
926 */
927 sPrefix = sPrefix + ' ';
928 console.log(sPrefix + sName + ' ' + dbgGetObjType(oObj));
929
930 /*
931 * The members.
932 */
933 sPrefix = sPrefix + ' ';
934 aMembers = pythonlikeDir(oObj);
935 for (i = 0; i < aMembers.length; i++)
936 {
937 console.log(sPrefix + aMembers[i]);
938 }
939
940 return true;
941}
942
943function dbgDumpObjWorker(sType, sName, oObj, sPrefix)
944{
945 var sRet;
946 switch (sType)
947 {
948 case 'function':
949 {
950 sRet = sPrefix + 'function ' + sName + '()' + '\n';
951 break;
952 }
953
954 case 'object':
955 {
956 sRet = sPrefix + 'var ' + sName + '(' + dbgGetObjType(oObj) + ') =';
957 if (oObj !== null)
958 {
959 sRet += '\n';
960 }
961 else
962 {
963 sRet += ' null\n';
964 }
965 break;
966 }
967
968 case 'string':
969 {
970 sRet = sPrefix + 'var ' + sName + '(string, ' + oObj.length + ')';
971 if (oObj.length < 80)
972 {
973 sRet += ' = "' + oObj + '"\n';
974 }
975 else
976 {
977 sRet += '\n';
978 }
979 break;
980 }
981
982 case 'Oops!':
983 sRet = sPrefix + sName + '(??)\n';
984 break;
985
986 default:
987 sRet = sPrefix + 'var ' + sName + '(' + sType + ')\n';
988 break;
989 }
990 return sRet;
991}
992
993
994function dbgObjInArray(aoObjs, oObj)
995{
996 var i = aoObjs.length;
997 while (i > 0)
998 {
999 i--;
1000 if (aoObjs[i] === oObj)
1001 {
1002 return true;
1003 }
1004 }
1005 return false;
1006}
1007
1008function dbgDumpObjTreeWorker(oObj, sPrefix, aParentObjs, cMaxDepth)
1009{
1010 var sRet = '';
1011 var aMembers = pythonlikeShallowDir(oObj);
1012 var i;
1013
1014 for (i = 0; i < aMembers.length; i++)
1015 {
1016 //var sName = i;
1017 var sName = aMembers[i];
1018 var oMember;
1019 var sType;
1020 var oEx;
1021
1022 try
1023 {
1024 oMember = oObj[sName];
1025 sType = typeof oObj[sName];
1026 }
1027 catch (oEx)
1028 {
1029 oMember = null;
1030 sType = 'Oops!';
1031 }
1032
1033 //sRet += '[' + i + '/' + aMembers.length + ']';
1034 sRet += dbgDumpObjWorker(sType, sName, oMember, sPrefix);
1035
1036 if ( sType == 'object'
1037 && oObj !== null)
1038 {
1039
1040 if (dbgObjInArray(aParentObjs, oMember))
1041 {
1042 sRet += sPrefix + '! parent recursion\n';
1043 }
1044 else if ( sName == 'previousSibling'
1045 || sName == 'previousElement'
1046 || sName == 'lastChild'
1047 || sName == 'firstElementChild'
1048 || sName == 'lastElementChild'
1049 || sName == 'nextElementSibling'
1050 || sName == 'prevElementSibling'
1051 || sName == 'parentElement'
1052 || sName == 'ownerDocument')
1053 {
1054 sRet += sPrefix + '! potentially dangerous element name\n';
1055 }
1056 else if (aParentObjs.length >= cMaxDepth)
1057 {
1058 sRet = sRet.substring(0, sRet.length - 1);
1059 sRet += ' <too deep>!\n';
1060 }
1061 else
1062 {
1063
1064 aParentObjs.push(oMember);
1065 if (i + 1 < aMembers.length)
1066 {
1067 sRet += dbgDumpObjTreeWorker(oMember, sPrefix + '| ', aParentObjs, cMaxDepth);
1068 }
1069 else
1070 {
1071 sRet += dbgDumpObjTreeWorker(oMember, sPrefix.substring(0, sPrefix.length - 2) + ' | ', aParentObjs, cMaxDepth);
1072 }
1073 aParentObjs.pop();
1074 }
1075 }
1076 }
1077 return sRet;
1078}
1079
1080/**
1081 * Dumps the given object and all it's subobjects to the console.
1082 *
1083 * @returns String dump of the object.
1084 * @param oObj The object under inspection.
1085 * @param sName The object name (optional).
1086 * @param sPrefix What to prefix the log output with (optional).
1087 * @param cMaxDepth The max depth, optional.
1088 */
1089function dbgDumpObjTree(oObj, sName, sPrefix, cMaxDepth)
1090{
1091 var sType;
1092 var sRet;
1093 var oEx;
1094
1095 /*
1096 * Defaults
1097 */
1098 if (!sPrefix)
1099 {
1100 sPrefix = '';
1101 }
1102
1103 if (!sName)
1104 {
1105 sName = '??';
1106 }
1107
1108 if (!cMaxDepth)
1109 {
1110 cMaxDepth = 2;
1111 }
1112
1113 /*
1114 * The object itself.
1115 */
1116 try
1117 {
1118 sType = typeof oObj;
1119 }
1120 catch (oEx)
1121 {
1122 sType = 'Oops!';
1123 }
1124 sRet = dbgDumpObjWorker(sType, sName, oObj, sPrefix);
1125 if (sType == 'object' && oObj !== null)
1126 {
1127 var aParentObjs = Array();
1128 aParentObjs.push(oObj);
1129 sRet += dbgDumpObjTreeWorker(oObj, sPrefix + '| ', aParentObjs, cMaxDepth);
1130 }
1131
1132 return sRet;
1133}
1134
1135function dbgLogString(sLongString)
1136{
1137 var aStrings = sLongString.split("\n");
1138 var i;
1139 for (i = 0; i < aStrings.length; i++)
1140 {
1141 console.log(aStrings[i]);
1142 }
1143 console.log('dbgLogString - end - ' + aStrings.length + '/' + sLongString.length);
1144 return true;
1145}
1146
1147function dbgLogObjTree(oObj, sName, sPrefix, cMaxDepth)
1148{
1149 return dbgLogString(dbgDumpObjTree(oObj, sName, sPrefix, cMaxDepth));
1150}
1151
1152/** @} */
1153
Note: See TracBrowser for help on using the repository browser.

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