1a2,63
> function tdl($text,$color=null,$start=null)
> {
>   global $exelana;
>   return; # disabled
> 
>   if (is_array($text)) // in case called with an array instead of a text string
>     $text = implode('',$text);
>   echo (is_null($color) ? '' : "<span style='color:$color'>").
>     date('H:i:s',time()).' '.$text."<br>".
>     (is_null($color) ? "\n" : "</span>\n");
>   if (!is_null($start))
>     echo $exelana->stackTrace("<br>\n",$start+1)."<br>\n";
>   flush();
> }
> 
> function tdlExit($text,$color=null,$start=null)
> {
>   global $exelana_wordpress;
>   return; # disabled
>   tdl($text,$color,is_null($start) ? null : $start+1);
> 
>   $bt = debug_backtrace();
>   $file = str_replace($exelana_wordpress['root'].'/','',$bt[1]['file']);
>   tdl('tdlExit: Exiting from line '.$bt[1]['line']." of $file",'red');
>   exit();
> }
> 
> function tdlStackTrace($ignore=0)
> {
>   $bt = debug_backtrace();
>   for ($i = $ignore + 1; $i < count($bt); $i++)
>   {
>     echo (array_key_exists('class',$bt[$i]) ? $bt[$i]['class'].'::' : '').$bt[$i]['function'].' '.
>       (array_key_exists('line',$bt[$i]) ? 'from line '.$bt[$i]['line'].' of ' : '').
>       (array_key_exists('file',$bt[$i]) ? $bt[$i]['file'] : '')."<br>\n";
>   }
> }
> 
> function tdlDie($text)
> {
>   echo "$text<br>\n";
>   tdlStackTrace();
>   exit();
> }
> 
> function tdlDieExport($text,$v)
> {
>   echo "$text<br>\n";
>   echo var_export($v,true)."<br>\n";
>   tdlStackTrace();
>   exit();
> }
> 
> $time_stamp = "2025-07-17 16:21:28";
> if (is_callable('tdl'))
> {
>   tdl("Time stamp: $time_stamp",'olive');
>   if (array_key_exists('SERVER_NAME',$_SERVER))
>     tdl($_SERVER['SERVER_NAME'],'olive');
> }
> 
> # TODO: remove _start_is_safe restrictions
13,14c75,77
<    Time stamp: 2025-02-25 16:47:12
<  */
---
>   Time stamp: 2025-06-23 07:10:34
>   TODO: remove debugging time stamp above so this one becomes active
> */
16c79
< /* Copyright 2017-2022  Tony Lewis  (email : tlewis@exelana.com)
---
> /* Copyright 2017-2025  Tony Lewis  (email : tlewis@exelana.com)
162c225,228
< # Allow debugging messages to be added to the bar without invoking the class
---
> #################################################################################################
> # Allow debugging messages to be added to the Debug Bar prior to invoking the Exelana class
> # (controlled by settings in EXELANA_WORDPRESS - often for a specific site such as a
> # development machine).
167,168d232
<   if (file_exists($_SERVER['DOCUMENT_ROOT'].'/exelana_wordpress.php'))
<     $file = $_SERVER['DOCUMENT_ROOT'].'/exelana_wordpress.php';
171a236,248
> if (isset($exelana_wordpress) && array_key_exists('annotate',$exelana_wordpress['enable']))
>   $exelana_annotate = $exelana_wordpress['enable']['annotate'];
> if (isset($exelana_wordpress) && array_key_exists('development',$exelana_wordpress['enable']))
>   $exelana_development = $exelana_wordpress['enable']['development'];
> 
> #################################################################################################
> # Collect usage information as early as possible
> global $exelana_usage;
> $exelana_usage = array('rusage' => getrusage(), 'timestamp' => microtime(true));
> global $exelana_annotate;
> $exelana_annotate = false;
> global $exelana_development;
> $exelana_development = false;
203d279
<   public static $static = 'static test';
233c309,310
<   private $_die_after;            // (float) Die is start is called after this much elapseed time
---
>   private $_development;          // (boolean) Plugin is running in a development environment
>   private $_die_after_seconds;    // (float) Die if executes more this many seconds
245c322
<   private $_emit_die_header;      // (boolean) Die header has been emitted
---
>   private $_emit_die_header;      // (boolean) Die header has been emiteted
289a367
>   private $_progress;             // (array) Information about progress logging
304a383
>   private $_start_is_safe;        // (boolean) Exelana::start is safe to call
306a386
>   private $_style_queue;          // (array) Caller requested styles to enqueue
342a423,426
>     global $exelana_annotate;     // from EXELANA_WORDPRESS <annotate>
>     global $exelana_development;  // from EXELANA_WORDPRESS <deveopment>
>     global $exelana_wordpress;    // set after EXELANA_WORDPRESS is loaded
> 
377c461
<     $this->_debugging = false;
---
>     $this->_debugging = $exelana_development;   // Development machine
379c463,464
<     $this->_die_after = null;
---
>     $this->_development = $exelana_development; // Development machine
>     $this->_die_after_seconds = null;
432a518
>     $this->_progress = null;
446a533
>     $this->_start_is_safe = false;
448a536
>     $this->_style_queue = array();
465a554,581
>     /* Find the WordPress root */
>     $this->_root = __DIR__;
>     while (!file_exists($this->_root.'/wp-load.php') && $this->_root != '/')
>       $this->_root = dirname($this->_root);
>     tdl("root: ".$this->_root);
> 
>     // Create the progress file (if requested)
>     tdl('$exelana_wordpress[progress]='.var_export($exelana_wordpress['progress'],true));
>     if ($exelana_wordpress['progress'] != false)
>     {
>       $progress_path = $this->outputPath($exelana_wordpress['progress']);
>       tdl("Opening progress: $progress_path");
>       if ($progress_fh = fopen($progress_path, 'w'))
>       {
>         // Note: The following things are not available this early and will not appear in the
>         // prefix:
>         //   - standalone implementation
>         //   - banner hooks
>         //   - dump path
>         $this->_progress = array('fh' => $progress_fh, 'path' => $progress_path);
>         fputs($progress_fh,$this->_renderLogPrefix($progress_path,false,"\n"));
>         tdl("Opened ".$this->_progress['path']);
>       } else {
>         $e = error_get_last();
>         tdlExit("Failed to open $progress_path: ".$e['message']);
>       }
>     }
> 
467c583,584
<     $this->tdl('Call start');
---
>     $this->_start_is_safe = true;
> tdl("Exelana::__construct calling start");
468a586,590
>     if (!is_null($this->_progress))
>       $this->debug("$fn: ",$this->_progress['path']);
> 
> tdl("Exelana::__construct: returned $fn");
> tdlExit("OK so far");
478c600
<     $this->debug("$fn: loaded ".($refactor_db ? 'refactored' : 'original').' database code');
---
> #    $this->debug("$fn: loaded ".($refactor_db ? 'refactored' : 'original').' database code');
486,490d607
<     /* Find the WordPress root */
<     $this->_root = __DIR__;
<     while (!file_exists($this->_root.'/wp-load.php') && $this->_root != '/')
<       $this->_root = dirname($this->_root);
< 
533a651
>     $this->progressWrite("$fn: initialize ExelanaDB");
546a665,666
>       $this->progressWrite("$fn: memory capacity processing: limit ".
>         $this->intToSize($this->_memory_cap));
552a673
>     $this->progressWrite($this->fn());
601c722
<      Note: This should be called before $this->init(). */
---
>      Note: This should be called before $this->exelanaInit(). */
610c731
<   public function init()
---
>   public function exelanaInit()
620a742
>     $this->progressWrite("$fn: administrator=",$this->export($this->_administrator));
640d761
<     $this->tdl("option 'debug_usage': ".$this->export($this->getOption('debug_usage')));
646d766
<         $this->tdl("Write usage to $path");
673a794
>         $this->progressWrite("$fn: add actions for admin page");
680a802
>         $this->progressWrite("$fn: add actions for regular page");
705a828,829
>     $this->progressWrite("$fn: standalone ".$this->_standalone);
> 
710a835
>       $this->progressWrite("$fn: load Wordpress");
715c840
<     if (!$this->isInit() && !$this->init())
---
>     if (!$this->isInit() && !$this->exelanaInit())
716a842
>       $this->progressWrite("$fn: plugin deactivated");
728d853
<       error_log($this->_name.": Running standalone module $name");
1079,1080c1204,1206
<   /* Die if start is called more than $seconds after execution started */
<   public function dieAfter($seconds)
---
>   /* Die if start is called more than $seconds after execution started. If a value is not
>      specified, the limit will be the configured max_execution_time minus five seconds. */
>   public function dieAfter($seconds=-1)
1082c1208,1221
<     $this->_die_after = $seconds;
---
>     if ($seconds < 0)
>     {
>       $max = ini_get('max_execution_time');
>       if ($max <= 0)
>         return $this->dieAfter(30);
>       $this->_die_after_seconds = $max - 5;
>     } else {
>       $this->_die_after_seconds = $seconds;
> 
>       // Give a little bit of breathing room for error code to run after the specified time has
>       // expired.
>       $this->set_time_limit($seconds + 5);
>     }
>     return $this->_die_after_seconds;
1096a1236,1237
>     # TODO: This may be written too soon; it could precede <!DOCTYPE>; need a queue (such as what
>     # is done for styles and scripts).
1309a1451,1458
>   /* Return dump path */
>   public function getDumpPath()
>   {
>     if (is_null($this->_dump_path))
>       $this->_dump_path = $this->outputPath('dump','%t-%p.html');
>     return $this->_dump_path;
>   }
> 
1430a1580,1627
>   /* Return URL that corresponds to a file searching in the following order:
>        - path starts with '/' and is an existing file relative to the root
>        - path is a file relative to the plugin directory
>        - path is a file relative to the root directory */
>   # TODO: remove logic variable (used for debugging)
>   public function getURL($path)
>   {
>     $fn = $this->fn();
>     $res = false;
>     $logic = 0;
>     if (substr($path,0,1) == '/')
>     {
>       $len = strlen($this->_root);
>       if ($this->isFile($path))
>       {
>         if (strlen($path) > $len
>          && substr($path,0,$len+1) == $this->_root.'/')
>         {
>           $res = str_replace($this->_root,'',$path);
>           $logic = 1;
>         } else {
>           $logic = 2;
>         }
>       } elseif ($this->isFile($this->_root.$path)) {
>         $res = $path;
>         $logic = 3;
>       } else {
>         $logic = 4;
>       }
>     } else {
>       if ($this->isFile($this->_dir."/$path"))
>       {
>         $res = str_replace($this->_root,'',$this->_dir."/$path");
>         $logic = 5;
>       } elseif ($this->isFile($this->_root."/$path")) {
>         $res = "/$path";
>         $logic = 6;
>       } else {
>         $logic = 7;
>       }
>     }
> 
>     if ($res == false)
>       $this->dieTagExport('path',$path,'res',$res,'logic',$logic);
>     $this->debugData("$fn: $path => ".$this->export($res),"logic: $logic");
>     return $res;
>   }
> 
1452a1650,1661
>   /* Is the code currently running on a development machine? */
>   public function isDevelopment()
>   {
>     return $this->_development;
>   }
> 
>   /* Test whether a path refers to a file */
>   public function isFile($path)
>   {
>     return file_exists($path) && !is_dir($path);
>   }
> 
1581c1790
< // $out = $this->export($dir).','.$this->export($name).': ';
---
>     tdl("outputPath: called with ".var_export($dir,true).','.var_export($name,true));
1601a1811
>         tdl("outputPath: returning temp ",var_export($path,true));
1606a1817
>     # tdl("outputPath: interim ".var_export($dir,true).','.var_export($name,true));
1616a1828
>       # tdl('outputPath: $fname '.var_export($fname,true));
1621a1834,1835
>       # tdl('outputPath: makePath '.$this->_root.'/'.$this->_prefix."/$dir".
>       #  ($fname == '' ? '' : "/$fname"));
1623a1838
>       # tdl('outputPath: makePath returned '.var_export($path,true));
1644c1859
< // error_log("$out$path");
---
>     tdl("outputPath: returning ".var_export($path,true));
2143c2358
<     return "<div class='exelana-debug-output' style='max-height:400px'$onclick>".
---
>     return "<div class='exelana-output-box' style='max-height:400px'$onclick>".
2223a2439,2443
>   public function setInclude($field,$value)
>   {
>     $this->_js_include[$field] = $value;
>   }
> 
2258a2479,2489
>   /* Shorten full paths to reasonable relative file names */
>   public function showPath($path)
>   {
>     $path = $this->relativePath($path);
>     if (preg_match('/^\/wp\-content\/(plugins\/.*)$/',$path,$matches))
>       $path = $matches[1];
>     elseif (preg_match('/^'.$this->_prefix.'\/(.*)/',$path,$matches))
>       $path = $matches[1];
>     return $path;
>   }
> 
2413a2645,2659
>   public function enqueueStyle($handle,$sheet,$deps=array())
>   {
>     $fn = $this->start();
>     if (!$this->_init)
>       $this->diePrintf('%s %s',$fn,__('called before init','exelana'));
>     if ($this->_ended_head)
>       $this->diePrintf('%s %s',$fn,__('called after head ended','exelana'));
> 
>     $url = $this->getURL($sheet);
>     $this->_style_queue[$handle] = array('url' => $url, 'deps' => $deps);
>     foreach ($deps as $dep)
>       $this->_style_deps[$dep] = null;
>     $this->wp_enqueue_style($handle,$url,$deps);
>   }
> 
2791c3037
<     $valid = $this->wp_verify_nonce($nonce,$page_action);
---
>     $valid = $this->_verify_nonce($nonce,$page_action);
2795a3042,3047
>   private function _verifyNonce($nonce,$action)
>   {
>     $result = $this->wp_verify_nonce($nonce,action);
> 
>     // Note: wp_verify_nonce returns 1, 2, or null (meaning not verified)
>     return ($result === false) ? false : true;
3600a3853,3855
> # echo "addEventBar called with $type $message<br>\n";
> tdl("addBarEvent called by",null,1);
> tdl("addBarEvent: checkMemoryUtilization");
3604a3860
>     $show = $type;
3611a3868,3869
>         $show = "$type:".join(',',$subtype);
>         $this->progressWriteExport("$fn: #CHECK# $type subtype is array",$subtype);
3614a3873
>         $show = "$type:$subtype";
3615a3875
>         $this->progressWriteExport("$fn: #CHECK# type array collapsed to first element",$type);
3616a3877
>         $show = $type;
3618a3880
> tdl("$fn: type $show message $message");
3622a3885,3887
> #      $this->echoBackTrace($trace,false);
> #      echo "Shown? ".var_export($trace[0]['shown'],true)."<br>\n";
> #      exit();
3623a3889
>       $source = 'caller';
3626a3893
>       $source = 'debug_backtrace';
3627a3895,3896
> # tdl("$fn: calling checkBacktrace");
> #     $this->checkBacktrace($bt,$source); # not necessary - this wasn't the problem!
3636a3906
> tdl("$fn: \$trace=".var_export($trace,true)."; \$start=".var_export($start,true));
3639,3641c3909,3923
<       $stack = array();
<       while ($bt[$start]['function'] == '__call' || $bt[$start]['function'] == 'start')
<         $start++;
---
>       while ($start < count($bt))
>       {
> #        if (gettype($bt[$start]['function']) != 'string')
> #        {
> #          echo "Expecting a string for function at index $start\n";
> #          $this->echoBackTrace($bt,false);
> #        }
>         if ($bt[$start]['function'] == '__call' || $bt[$start]['function'] == 'start')
>         {
>           tdl("$fn: skip over ".$bt[$start]['function']." at $start; now ".($start + 1));
>           $start++;
>         } else {
>           break;
>         }
>       }
3642a3925
>       $stack = array();
3652a3936
> tdl("$fn: created stack: ".count($stack));
3653a3938
> tdlExit("Resume debugging...");
3704a3990,3997
> #echo "addBarEvent progressWrite: event ".$event['type'].': '.$event['message']."<br>\n";
>     $this->progressWrite('event '.$event['type'].': '.$event['message']);
> #echo "addBarEvent maybe event stack<br>\n";
>     if (array_key_exists('stack',$event))
>       $this->progressWrite(implode("\n",$event['stack']));
> #echo "addBarEvent maybe data<br>\n";
>     if (array_key_exists('data',$event))
>       $this->progressWrite($this->export($event['data']));
3708a4002
> #echo "addBarEvent failing code<br>\n";
3711a4006
> #echo "addBarEvent maybe event handlers<br>\n";
3722a4018
> #echo "addBarEvent record usage<br>\n";
3723a4020
> #echo "addEventBar complete for $type $message<br>\n";
3725a4023,4099
> #
>   ###############################################################################################
>   # EXTREME DEBUGGING: Echo a backtrace and possibly exit
>   private function echoBacktrace(&$trace,$exit=false)
>   {
>     if (is_array($trace))
>     {
>       if (!array_key_exists('shown',$trace[0]))
>       {
>         $trace[0]['shown'] = true;
>         echo "Depth: ".count($trace)."<br>\n";
>         for ($i = 0; $i < count($trace); $i++)
>         {
>           echo "trace[$i][file]: ".    $this->echoBacktraceValue($trace[$i],'file')."<br>\n";
>           echo "trace[$i][line]: ".    $this->echoBacktraceValue($trace[$i],'line')."<br>\n";
>           echo "trace[$i][function]: ".$this->echoBacktraceValue($trace[$i],'function')."<br>\n";
>           echo "trace[$i][class]: ".   $this->echoBacktraceValue($trace[$i],'class')."<br>\n";
>           echo "trace[$i][object]: ".  $this->echoBacktraceValue($trace[$i],'object')."<br>\n";
>           echo "trace[$i][type]: ".    $this->echoBacktraceValue($trace[$i],'type')."<br>\n";
>           for ($j = 0; $j < count($trace[$i]['args']); $j++)
>           {
>             echo "trace[$i][args][$j]: ".$this->echoBacktraceValue($trace[$i]['args'],$j)."<br>\n";
>           }
>         }
>         echo "--------------------------------------------------<br>\n";
>       }
>     } else {
>       echo "\$trace: ".$this->echoBacktraceValue($trace,true)."<br>\n";
>     }
>     if ($exit)
>     {
>       echo "Exiting...<br>\n";
>       exit();
>     }
>   }
> 
>   private function echoBacktraceValue($trace,$field)
>   {
>     if (array_key_exists($field,$trace))
>     {
>       $v = $trace[$field];
>       if (is_null($v))
>       {
>         return 'null';
>       } elseif (is_object($v)) {
>         return 'object class '.get_class($v);
>       } elseif (is_array($v)) {
>         if (count($v) == 2 && is_object($v[0]) && gettype($v[1]) == 'string')
>         {
>           return get_class($v[0]).'::'.$v[1];
>         } else {
>           return 'array['.count($v).']';
>         }
>       }
>       return gettype($v).' '.var_export($v,true);
>     } else {
>       return 'missing';
>     }
>   }
> 
>   private function checkBacktrace($bt,$source='debug_backtrace',$exit=false)
>   {
>     $check_result = $this->inspectBacktrace($bt);
>     $bt[1]['line'] = 'line '.$bt[1]['line'];
>     if (!$check_result[0])
>     {
>       error_log('checkBacktrace: '.$check_result[1]);
>       echo $this->renderExecutionTime()." Error(s) in trace from $source<br>\n";
>       echo "$check_result[1]<br>\n";
>       $this->echoBackTrace($bt,$exit);
>     }
>   }
> 
>   private function inspectBacktrace($trace)
>   {
>     if (!is_array($trace))
>       return array(false,'backtrace is not an array; is '.gettype($trace));
3726a4101,4143
>     if (!is_array($trace))
>       return array(false,'backtrace is not a simple array; keys: '.array_keys($trace));
>     #                            expected type   required
>     $keys = array('file'     => array('string',  true),
>                   'line'     => array('integer', true),
>                   'function' => array('string',  true),
>                   'class'    => array('string',  false),
>                   'object'   => array('object',  false)
>                  );
>     for ($i = 0; $i < count($trace); $i++)
>     {
>       $missing = array();
>       foreach ($keys as $field => $info)
>       {
>         if ($info[1] && !array_key_exists($field,$trace[$i]))
>           array_push($missing,$field);
>       }
>       if (count($missing) > 0)
>         return array(false,'index '.$i.': missing '.implode(',',$missing));
> 
>       $type_error = array();
>       foreach ($keys as $field => $info)
>       {
>         if (array_key_exists($field,$trace[$i]))
>         {
>           $field_type = gettype($trace[$i][$field]);
>           if ($info[0] == 'object' && ($field_type != 'object' && !is_null($trace[$i][$field])))
>           {
>             array_push($type_error,"$field: $field_type not $info[0] [".
>                          $this->echoBackTraceValue($trace[$i],$field).']');
>           } elseif ($info[0] != $field_type) {
>             array_push($type_error,"$field: $field_type not $info[0]");
>           }
>         }
>       }
>       if (count($type_error) > 0)
>         return array(false,'index '.$i.': '.implode('; ',$type_error));
>     }
>     return array(true,'passed');
>   }
> # END EXTREME DEBUGGING
> #################################################################################################
> # 
3802,3803d4218
<     $this->tdl('recordUsage: '.$this->export($entry));
< 
3818d4232
<       $this->tdl("Usage entry saved to array");
3820,3821d4233
<       if (!isset($this->_usage_fh))
<         $this->tdl("usage file not opened",true);
4094a4507,4508
>     if (!$this->_start_is_safe) # Some of the side effects of debug can interfere with debugging
>       return $this->fn();
4125,4132d4538
<     # TODO: Remove this temporary debugging code
<     # if (get_option('exksc_development',false) && !headers_sent())
<     # {
<     #   $count = count($this->_dump_at_shutdown);
<     #   if ($count == 1)
<     #     $this->header("X-exksc_dump-to: ".$this->outputPath('dump','%t-%p.html'), false);
<     #   $this->header("X-exksc-dump-$count: $reason", false);
<     # }
4200c4606
<     if (array_key_exists('plain',$format))
---
>     if (array_key_exists('plain',$format) && $format['plain'])
4202,4207c4608,4610
<       if ($format['plain'])
<       {
<         $format['sp'] = ' ';
<         $format['nl'] = '';
<         $format['nlsp'] = ' ';
<       }
---
>       $format['sp'] = ' ';
>       $format['nl'] = '';
>       $format['nlsp'] = ' ';
4213c4616
<     } elseif (is_array($format)) {
---
>     } else {
4399c4802,4804
<     $offset = $bt[1]['function'] == '__call' ? 2 : 1;
---
>     $offset = 1;
>     while ($bt[$offset]['function'] == '__call')
>       $offset++;
4403a4809,4810
> 
>     $this->_checkExecutionTime();
4491a4899,5024
>   private function progressHandle()
>   {
>     if ($this->_progress == false)
>       return false;
> 
>     if (!array_key_exists('fh',$this->_progress))
>       return false;
> 
>     $fh = $this->_progress['fh'];
>     if (gettype($fh) == 'resource (closed)' || !is_resource($fh))
>     {
>       $text = $this->caller().': messages file is not open';
>       error_log($text);
>       echo("\n$text\n");
>       exit();
>     }
>     return $fh;
>   }
> 
>   public function progressPut($fh,$text)
>   {
>     tdl($text,'green');
>     global $exelana_wordpress;
>     $bt = debug_backtrace();
>     $file = str_replace($exelana_wordpress['root'].'/','',$bt[0]['file']);
>     # echo "progressPut: $text<br>\n"; # ."by ".$bt[1]['function']." from line ".$bt[0]['line']." of $file<br>\n";
>     fputs($fh,preg_replace('/\n\n+/',"\n",$this->htmlToLog($text)));
>     fflush($fh);
>   }
> 
>   public function progressExport($tag,$data)
>   {
>     $fh = $this->progressHandle();
>     if ($fh === false)
>       return;
> 
>     $this->progressPut($fh,str_repeat('+',97)."\n$tag\n".
>        $this->export($data,array('sp'=>' ','nl'=>"\n"))."\n");
>   }
> 
>   public function progressExportListFromArray($values)
>   {
>     $fh = $this->progressHandle();
>     if ($fh === false)
>       return;
> 
>     for ($i = 0; $i < count($values); $i += 2)
>     {
>       $this->progressPut($fh,str_repeat('+',97)."\n$values[$i]\n".
>         $this->export($values[$i+1],array('sp'=>' ','nl'=>"\n"))."\n");
>     }
>   }
> 
>   public function progressExportList(...$values)
>   {
>     $this->progressExportFromArray($values);
>   }
> 
>   public function progressRecordEvent($event)
>   {
>     $fh = $this->progressHandle();
>     if ($fh === false)
>       return;
> 
>     $type = $event['type'];
>     if ($type == 'action' || $type == 'filter')
>       return $this->progressPut($fh,str_repeat('*',97)."\n* ".ucfirst($type).': '.
>         $event['message']."\n".str_repeat('*',97)."\n");
> 
>     $this->progressPut($fh,str_repeat('=',97)."\n[$type] ".$event['message']."\n");
>     if (array_key_exists('stack',$event))
>     {
>       $file = "file $this->_dir";
>       foreach ($event['stack'] as $row)
>         $this->progressPut($fh,str_replace($file,'file ',$row)."\n");
>     }
>     if (array_key_exists('data',$event))
>     {
>       if (is_array($event['data']) && array_key_exists('_taggedData',$event['data']))
>       {
>         foreach ($event['data']['_taggedData'] as $key)
>         {
>           $this->progressPut($fh,substr("----- $key ".str_repeat('-',90),0,97)."\n".
>             $this->export($event['data'][$key],array('sp'=>' ','nl'=>"\n")));
>         }
>       } else {
>         $this->progressPut($fh,str_repeat('-',97)."\n".
>           $this->export($event['data'],array('sp'=>' ','nl'=>"\n"))."\n");
>       }
>     }
>     // $this->progressPut($fh,"[end of message]\n");
>   }
> 
>   public function progressStackTrace($ignore=0)
>   {
>     $fh = $this->progressHandle();
>     if ($fh === false)
>       return;
>     $this->progressPut($fh,$this->stackTrace("\n",$ignore+1)."\n");
>   }
> 
>   public function progressWrite($text,$star=false,$trace=false)
>   {
>     $fh = $this->progressHandle();
>     if ($fh === false)
>       return;
> 
>     if ($star)
>     {
>       $this->progressPut($fh,substr("***** $text ".str_repeat('*',90),0,97)."\n");
>     } else {
>       $this->progressPut($fh,"$text\n");
>     }
> 
>     if ($trace == true || is_int($trace))
>       $this->progressStackTrace(is_int($trace) ? $trace + 1 : 1);
>   }
> 
>   public function progressWriteExport($text,...$values)
>   {
>     if ($this->_progress == false)
>       return false;
>     $this->progressWrite($text);
>     $this->progressExportListFromArray($values);
>   }
> 
4528a5062,5064
>     if (!$this->_start_is_safe) # Some of the side effects of start can interfere with debugging
>       return $this->fn();
> 
4529a5066,5067
>     {
>       tdlExit('failed _checkMemoryUtilization','red');
4530a5069
>     }
4565c5104,5112
<     if (!is_null($this->_die_after))
---
> 
>     $this->_checkExecutionTime();
>     return $fn;
>   }
> 
>   public function tagData(...$values)
>   {
>     $output = array('_taggedData' => array());
>     for ($i = 0; $i < count($values); $i += 2)
4567,4574c5114,5118
<       $elapsed = $this->elapsedTime();
<       if ($elapsed >= $this->_die_after)
<       {
<         $message = "$fn: Elapsed time ".round($elapsed,3).' seconds exceeds '.$this->_die_after.
<           ' seconds';
<         $this->_die_after = null; # avoid loops
<         $this->dieMessage($message);
<       }
---
>        $output['_taggedData'][] = $values[$i];
>        if (is_array($values[$i+1]))
>          $output[$values[$i]] = $this->arrayClone($values[$i+1]);
>        else
>          $output[$values[$i]] = $values[$i+1];
4576c5120
<     return $fn;
---
>     return $output;
6186,6189c6730,6733
<           'name' => 'output',
<           'icon' => "\f491",
<           'text' => __("Output",'exelana'),
<           'data' => 0,
---
>           'name'    => 'output',
>           'icon'    => "\f491", // Media: text [text file]
>           'text'    => __("Output",'exelana'),
>           'data'    => 0,
6191c6735
<           'cb'   => array($this,'_cbBarRenderOutput')),
---
>           'cb'      => array($this,'_cbBarRenderOutput')),
6193,6196c6737,6740
<           'name' => 'errors',
<           'icon' => "\f534",
<           'text' => __("Errors",'exelana'),
<           'data' => array('render' => false, 'from' => 0, 'bar' => 0, 'total' => 0),
---
>           'name'    => 'errors',
>           'icon'    => "\f534", // Notifications: warning [exclamation point]
>           'text'    => __("Errors",'exelana'),
>           'data'    => array('render' => false, 'from' => 0, 'bar' => 0, 'total' => 0),
6198c6742
<           'cb'   => array($this,'_cbBarRenderErrors')),
---
>           'cb'      => array($this,'_cbBarRenderErrors')),
6200,6203c6744,6747
<           'name' => 'options',
<           'icon' => "\f108",
<           'text' => __("Options",'exelana'),
<           'cb'   => array($this,'_cbBarRenderOptions')),
---
>           'name'    => 'options',
>           'icon'    => "\f108", // Admin Menu: settings [sliders]
>           'text'    => __("Options",'exelana'),
>           'cb'      => array($this,'_cbBarRenderOptions')),
6205,6208c6749,6752
<           'name' => 'globals',
<           'icon' => "\f107",
<           'text' => __("Globals",'exelana'),
<           'cb'   => array($this,'_cbBarRenderGlobals')),
---
>           'name'    => 'globals',
>           'icon'    => "\f107", // Admin Menu: tools [wrench]
>           'text'    => __("Globals",'exelana'),
>           'cb'      => array($this,'_cbBarRenderGlobals')),
6210,6215c6754,6759
<           'name' => 'actions',
<           'icon' => "\f163",
<           'text' => __("Actions",'exelana'),
<           'data' => '',
<           'run'  => 'shutdown',
<           'cb'   => array($this,'_cbBarRenderActions')),
---
>           'name'    => 'actions',
>           'icon'    => "\f163", // Sorting: list view [bulleted list]
>           'text'    => __("Actions",'exelana'),
>           'data'    => '',
>           'run'     => 'shutdown',
>           'cb'      => array($this,'_cbBarRenderActions')),
6217,6220c6761,6764
<           'name' => 'filters',
<           'icon' => "\f536",
<           'text' => __("Filters",'exelana'),
<           'cb'   => array($this,'_cbBarRenderFilters')),
---
>           'name'    => 'filters',
>           'icon'    => "\f536", // Admin Menu: filter [funnel]
>           'text'    => __("Filters",'exelana'),
>           'cb'      => array($this,'_cbBarRenderFilters')),
6222,6227c6766,6771
<           'name' => 'evaluator',
<           'icon' => "\f16e",
<           'text' => __("Evaluator",'exelana'),
<           'cond' => 'eval',
<           'orphan' => 'true',
<           'cb'   => array($this,'_cbBarRenderEvaluator')),
---
>           'name'    => 'evaluator',
>           'icon'    => "\f16e", // Miscellaneous: calculator [calculator]
>           'text'    => __("Evaluator",'exelana'),
>           'cond'    => 'eval',
>           'orphan'  => 'true',
>           'cb'      => array($this,'_cbBarRenderEvaluator')),
6600d7143
<         // Define inline styles for the Debug Bar icons
7381c7924
<   /* Export a variable for use in JavaScript */
---
>   /* Export a variable for use in JavaScript */ # TODO: remove understore
8040a8584
>     $this->checkBacktrace($bt,'debug_backtrace');
8084a8629
>         tdl("_emitCurrentFilter $filter_type $current_filter");
8200c8745
<     $this->_die_after = null; # Ignore execution time error
---
>     $this->_die_after_seconds = null; # Ignore execution time error
8508a9054
>     tdlExit('__call: undefined method '.__CLASS__.'::'.$name,'red');
8512a9059,9073
>   private function _checkExecutionTime()
>   {
>     if (!is_null($this->_die_after_seconds))
>     {
>       $elapsed = $this->elapsedTime();
>       if ($elapsed >= $this->_die_after_seconds)
>       {
>         $message = "$fn: Elapsed time ".round($elapsed,3).' seconds exceeds '.$this->_die_after_seconds.
>           ' seconds';
>         $this->_die_after_seconds = null; # avoid loops
>         $this->dieMessage($message);
>       }
>     }
>   }
> 
8544a9106
>       # TODO: remove the debugging code
8704c9266,9267
<     $fn = "_dieHandler";
---
>     $fn = __CLASS__.'::_dieHandler';
>     tdlExit("$fn: $message",'red');
8921a9485,9517
>   public /* private */ function _renderLogPrefix($path,$marker=false,$sep='<br>')
>   {
>     $fn = $this->fn();
>     $out = ($marker ? str_repeat('=',97)."\n" : '').
>       $this->renderExecution($sep).$sep;
>     if (!is_null($this->_generated))
>       $out .= 'Plugin: '.$this->_generated;
>     if (isset($this->_version) && is_array($this->_version))
>     {
>       foreach ($this->_version as $name => $value)
>         $out .= "; $name: $value";
>       $out .= $sep;
>     } else {
>       $out .= $sep.getmypid()." Variable _version is not set or not an array$sep";
>       $out .= $this->export_self(array()).$sep;
>       error_log(getmypid()." Variable _version is not set or not an array; see $path");
>       $out .= $this->stackTrace($sep).$sep;
>     }
>     if (!is_null($this->_standalone))
>       $out .= 'Standalone implementation: '.$this->_standalone.$sep;
>     foreach ($this->_banner_hook as $hook)
>     {
>       $hook_out = explode('<br>',preg_replace('/<br>$/','',call_user_func($hook)));
>       foreach ($hook_out as $t)
>         if ($t != '')
>           $out .= $this->htmlToLog($t).$sep;
>     }
>     if ((!is_null($this->_dump_path)) && $this->_dump_path != $path)
>       $out .= 'Dump: '.$this->_dump_path.$sep;
>     return $out;
>   }
> 
>   # Used by ExelanaKickStartCart; remove underscore
9154,9190d9749
< 
<   public function share_methods($list)
<   {
<     $fn = $this->start(array(0=>'$list'));
<     $rc = new ReflectionClass($this);
<     $methods = $this->makeArray($rc->getMethods(),'name');
<     $result = array();
<     foreach ($list as $name)
<     {
<       $reason = $this->method_is_callable($this,$name);
< #      if ($reason === true)
< #        if ($methods[$name] != __CLASS__)
< #          $this->exportAndExit('share_methods: $name',$name,'found in class',$methods[$name]);
< #          $reason = sprintf(__("does not exist (found in class '%1s')",'exelana'),$methods[$name]);
<       if ($reason === true)
<       {
<         # $this->debug("$fn shared: $name");
<         $result[$name] = array($this,$name);
<       } else {
<         $this->debug("$fn failed to share: $name");
<         $this->exportAndExit('share_methods: $name',$name,'reason',$reason);
< #        $this->diePrintf('%1$s: method "%2$s::%3$s" %4$s',$fn,__CLASS__,$name,$reason);
<       }
<     }
<     $this->debugData("$fn returning",$result);
<     return $result;
<   }
< 
<   private function method_is_callable($obj,$method)
<   {
<     if (!method_exists($obj,$method))
<       return "$method does not exist in ".get_class($obj);
<     $rm = new ReflectionMethod($obj,$method);
<     if ($rm->isPrivate())
<       return "$method is private and cannot be shared";
<     return true;
<   }
9235,9244d9793
< 
<   function tdl($s,$exit=false)
<   {
<     return;
<     if (!preg_match('/test.php/',$_SERVER['REQUEST_URI']))
<       return;
<     echo($s."<br>\n");
<     if ($exit === true)
<       exit();
<   }
