@@ -100,7 +100,8 @@ function ExecutionVisualizer(domRootID, dat, params) {
100100// create a unique ID, which is often necessary so that jsPlumb doesn't get confused
101101// due to multiple ExecutionVisualizer instances being displayed simultaneously
102102ExecutionVisualizer . prototype . generateID = function ( original_id ) {
103- return this . visualizerID + '__' + original_id ;
103+ // (it's safer to start names with a letter rather than a number)
104+ return 'v' + this . visualizerID + '__' + original_id ;
104105}
105106
106107
@@ -165,6 +166,14 @@ ExecutionVisualizer.prototype.render = function() {
165166 </tr>\
166167 </table>' ) ;
167168
169+
170+ // create a persistent globals frame
171+ this . domRoot . find ( "#stack" ) . append ( '<div class="stackFrame" id="'
172+ + myViz . generateID ( 'globals' ) + '"><div id="' + myViz . generateID ( 'globals_header' )
173+ + '" class="stackFrameHeader">Global variables</div><table class="stackFrameVarTable" id="'
174+ + myViz . generateID ( 'global_table' ) + '"></table></div>' ) ;
175+
176+
168177 if ( this . params && this . params . codeDivHeight ) {
169178 this . domRoot . find ( '#pyCodeOutputDiv' ) . css ( 'max-height' , this . params . codeDivHeight ) ;
170179 }
@@ -592,17 +601,12 @@ ExecutionVisualizer.prototype.renderPyCodeOutput = function() {
592601 . data ( this . codeOutputLines )
593602 . enter ( ) . append ( 'tr' )
594603 . selectAll ( 'td' )
595- . data ( function ( e , i ) { return [ e , e ] ; } ) // bind an alias of the element to both table columns
604+ . data ( function ( d , i ) { return [ d , d ] /* map full data item down both columns */ ; } )
596605 . enter ( ) . append ( 'td' )
597606 . attr ( 'class' , function ( d , i ) { return ( i == 0 ) ? 'lineNo' : 'cod' ; } )
598607 . style ( 'cursor' , function ( d , i ) { return 'pointer' } )
599608 . html ( function ( d , i ) {
600- if ( i == 0 ) {
601- return d . lineNumber ;
602- }
603- else {
604- return htmlspecialchars ( d . text ) ;
605- }
609+ return ( i == 0 ) ? d . lineNumber : htmlspecialchars ( d . text ) ;
606610 } )
607611 . on ( 'mouseover' , function ( ) {
608612 setHoverBreakpoint ( this ) ;
@@ -1123,11 +1127,6 @@ ExecutionVisualizer.prototype.renderDataStructures = function() {
11231127 var curEntry = this . curTrace [ this . curInstr ] ;
11241128 var curToplevelLayout = this . curTraceLayouts [ this . curInstr ] ;
11251129
1126- // clear the stack and render it from scratch.
1127- // the heap must be PERSISTENT so that d3 can render heap transitions.
1128- this . domRoot . find ( "#stack" ) . empty ( ) ;
1129- this . domRoot . find ( "#stack" ) . append ( '<div id="stackHeader">Frames</div>' ) ;
1130-
11311130
11321131 // Heap object rendering phase:
11331132
@@ -1164,20 +1163,20 @@ ExecutionVisualizer.prototype.renderDataStructures = function() {
11641163 . selectAll ( 'table.heapRow' )
11651164 . data ( curToplevelLayout , function ( objLst ) {
11661165 return objLst [ 0 ] ; // return first element, which is the row ID tag
1167- } )
1166+ } ) ;
11681167
11691168
11701169 // update an existing heap row
11711170 var heapColumns = heapRows
11721171 //.each(function(objLst, i) { console.log('UPDATE ROW:', objLst, i); })
11731172 . selectAll ( 'td' )
11741173 . data ( function ( d , i ) { return d . slice ( 1 , d . length ) ; } , /* map over each row, skipping row ID tag */
1175- function ( objID ) { return objID ; } /* each object ID is unique for constancy */ )
1174+ function ( objID ) { return objID ; } /* each object ID is unique for constancy */ ) ;
11761175
11771176 // ENTER
11781177 heapColumns . enter ( ) . append ( 'td' )
11791178 . attr ( 'class' , 'toplevelHeapObject' )
1180- . attr ( 'id' , function ( d , i ) { return 'toplevel_heap_object_' + d ; } )
1179+ . attr ( 'id' , function ( d , i ) { return 'toplevel_heap_object_' + d ; } ) ;
11811180 // remember that the enter selection is added to the update
11821181 // selection so that we can process it later ...
11831182
@@ -1191,11 +1190,11 @@ ExecutionVisualizer.prototype.renderDataStructures = function() {
11911190 // Right now, just delete the old element and render a new one in its place
11921191 $ ( this ) . empty ( ) ;
11931192 renderCompoundObject ( objID , $ ( this ) , true ) ;
1194- } )
1193+ } ) ;
11951194
11961195 // EXIT
11971196 heapColumns . exit ( )
1198- . remove ( )
1197+ . remove ( ) ;
11991198
12001199
12011200 // insert new heap rows
@@ -1450,58 +1449,112 @@ ExecutionVisualizer.prototype.renderDataStructures = function() {
14501449 }
14511450
14521451
1453- // Render globals and then stack frames:
1454- // TODO: could convert to using d3 to map globals and stack frames directly into stack frame divs
1455- // (which might make it easier to do smooth transitions)
1456- // However, I need to think carefully about what to use as object keys for stack objects.
1457- // Perhaps a combination of function name and current position index? This might handle
1458- // recursive calls well (i.e., when there are multiple invocations of the same function
1459- // on the stack)
1452+ // Render globals and then stack frames using d3:
1453+
1454+ // TODO: However, I need to think carefully about what to use as
1455+ // object keys for stack objects. Perhaps a combination of function
1456+ // name and current position index? This might handle recursive calls
1457+ // well (i.e., when there are multiple invocations of the same
1458+ // function on the stack)
1459+
14601460
14611461 // render all global variables IN THE ORDER they were created by the program,
14621462 // in order to ensure continuity:
1463- if ( curEntry . ordered_globals . length > 0 ) {
14641463
1465- var globalsID = myViz . generateID ( 'globals' ) ;
1466- var globalTblID = myViz . generateID ( 'global_table' ) ;
1464+ // Derive a list where each element contains a pair of
1465+ // [varname, value] as long as value is NOT undefined.
1466+ // (Sometimes entries in curEntry.ordered_globals are undefined,
1467+ // so filter those out.)
1468+ var realGlobalsLst = [ ] ;
1469+ $ . each ( curEntry . ordered_globals , function ( i , varname ) {
1470+ var val = curEntry . globals [ varname ] ;
1471+
1472+ // (use '!==' to do an EXACT match against undefined)
1473+ if ( val !== undefined ) { // might not be defined at this line, which is OKAY!
1474+ realGlobalsLst . push ( [ varname , varname ] ) ; /* purposely map varname down both columns */
1475+ }
1476+ } ) ;
14671477
1468- this . domRoot . find ( "#stack" ) . append ( '<div class="stackFrame" id="' + globalsID + '"><div id="' + myViz . generateID ( 'globals_header' ) + '" class="stackFrameHeader">Global variables</div></div> ') ;
1469- this . domRoot . find ( '#stack #' + globalsID ) . append ( '<table class="stackFrameVarTable" id="' + globalTblID + '"></table> ') ;
1478+ var globalsID = myViz . generateID ( 'globals ' ) ;
1479+ var globalTblID = myViz . generateID ( 'global_table ') ;
14701480
1471- var tbl = this . domRoot . find ( '#' + globalTblID ) ;
1481+ var globalsD3 = myViz . domRootD3 . select ( '#' + globalTblID )
1482+ . selectAll ( 'tr' )
1483+ . data ( realGlobalsLst , function ( d ) {
1484+ return d [ 0 ] ; // use variable name as key
1485+ } ) ;
1486+
14721487
1473- $ . each ( curEntry . ordered_globals , function ( i , varname ) {
1474- var val = curEntry . globals [ varname ] ;
1475- // (use '!==' to do an EXACT match against undefined)
1476- if ( val !== undefined ) { // might not be defined at this line, which is OKAY!
1477- tbl . append ( '<tr><td class="stackFrameVar">' + varname + '</td><td class="stackFrameValue"></td></tr>' ) ;
1478- var curTr = tbl . find ( 'tr:last' ) ;
1488+ // ENTER
1489+ globalsD3 . enter ( )
1490+ . append ( 'tr' )
1491+ . selectAll ( 'td' )
1492+ . data ( function ( d , i ) { return d ; } ) /* map varname down both columns */
1493+ . enter ( )
1494+ . append ( 'td' )
1495+ . attr ( 'class' , function ( d , i ) { return ( i == 0 ) ? 'stackFrameVar' : 'stackFrameValue' ; } )
1496+ . html ( function ( d , i ) {
1497+ return ( i == 0 ) ? d : '' /* initialize in each() later */ ;
1498+ } )
1499+ // remember that the enter selection is added to the update
1500+ // selection so that we can process it later ...
1501+
1502+ // UPDATE
1503+ globalsD3
1504+ . order ( ) // VERY IMPORTANT to put in the order corresponding to data elements
1505+ . selectAll ( 'td' )
1506+ . data ( function ( d , i ) { return d ; } ) /* map varname down both columns */
1507+ . each ( function ( varname , i ) {
1508+ console . log ( 'EACH!' , i , varname ) ;
1509+
1510+ if ( i == 1 ) {
1511+ var val = curEntry . globals [ varname ] ;
14791512
14801513 if ( isPrimitiveType ( val ) ) {
1481- renderPrimitiveObject ( val , curTr . find ( "td.stackFrameValue" ) ) ;
1514+ $ ( this ) . empty ( ) ; // crude but effective; maybe soften with a transition later
1515+ renderPrimitiveObject ( val , $ ( this ) ) ;
14821516 }
1483- else {
1517+ else {
1518+ $ ( this ) . empty ( ) ; // crude but effective; maybe soften with a transition later
1519+
1520+ // or even better, simply keep <div id=varDivID> around if it already exists
1521+ // so that jsPlumb connectors can persist.
1522+
1523+
14841524 // add a stub so that we can connect it with a connector later.
14851525 // IE needs this div to be NON-EMPTY in order to properly
14861526 // render jsPlumb endpoints, so that's why we add an " "!
14871527
14881528 // make sure varname doesn't contain any weird
14891529 // characters that are illegal for CSS ID's ...
14901530 var varDivID = myViz . generateID ( 'global__' + varnameToCssID ( varname ) ) ;
1491- curTr . find ( "td.stackFrameValue" ) . append ( '<div id="' + varDivID + '"> </div>' ) ;
1531+ $ ( this ) . append ( '<div id="' + varDivID + '"> </div>' ) ;
14921532
14931533 assert ( ! connectionEndpointIDs . has ( varDivID ) ) ;
14941534 var heapObjID = myViz . generateID ( 'heap_object_' + getRefID ( val ) ) ;
14951535 connectionEndpointIDs . set ( varDivID , heapObjID ) ;
14961536 }
14971537 }
14981538 } ) ;
1539+
1540+ globalsD3 . exit ( )
1541+ . remove ( ) ;
1542+
1543+
1544+ // for aesthetics, hide globals if there aren't any globals to display
1545+ if ( curEntry . ordered_globals . length == 0 ) {
1546+ this . domRoot . find ( '#' + globalsID ) . hide ( ) ;
1547+ }
1548+ else {
1549+ this . domRoot . find ( '#' + globalsID ) . show ( ) ;
14991550 }
15001551
15011552
1553+ /*
15021554 $.each(curEntry.stack_to_render, function(i, e) {
15031555 renderStackFrame(e, i, e.is_zombie);
15041556 });
1557+ */
15051558
15061559
15071560 function renderStackFrame ( frame , ind , is_zombie ) {
@@ -1601,9 +1654,9 @@ ExecutionVisualizer.prototype.renderDataStructures = function() {
16011654 var c = allConnections [ i ] ;
16021655
16031656 // this is VERY VERY fragile code, since it assumes that going up
1604- // five layers of parent() calls will get you from the source end
1657+ // FOUR layers of parent() calls will get you from the source end
16051658 // of the connector to the enclosing stack frame
1606- var stackFrameDiv = c . source . parent ( ) . parent ( ) . parent ( ) . parent ( ) . parent ( ) ;
1659+ var stackFrameDiv = c . source . parent ( ) . parent ( ) . parent ( ) . parent ( ) ;
16071660
16081661 // if this connector starts in the selected stack frame ...
16091662 if ( stackFrameDiv . attr ( 'id' ) == frameID ) {
0 commit comments