Edit via SFTP
<?php
/*::::
  USAGE: use in whatever element should contain the Login Widget node but:
    * ONLY USE IN ONE ELEMENT, else you will get multiple LWs, resulting
      in stuff happening twice whenever it's supposed to happen once.
    * DO NOT CALL EVENTS (including Render()) directly, otherwise the above will happen.
      Render_LoginWidget() is an exception, because it probably needs to display
      within a different element than the one where the login widget proper lives.
*/
 
interface fiEventAware {
    function DoEvent(int $nEvent) : void;
    function Render() : string;
}
 
// event codes:
define('KI_NODE_EVENT_DO_BUILDING',1);	// set up node structure
define('KI_NODE_EVENT_DO_FIGURING',2);	// do any necessary communication between nodes
// PURPOSE: So an ExecutableTwig node will pass events down to its subnodes
trait ftExecutableTree {
    use ftExecutableTwig;
 
    // ++ OVERRIDE ++ //
 
    public function DoEvent(int $nEvent) : void {
        $this->OnEventBefore($nEvent);
        if ($this->HasNodes()) {
            $ar = $this->GetNodes();
            foreach ($ar as $sName => $oNode) {
                //echo "DOING EVENT [$nEvent] IN [$sName]<br>";
                $oNode->DoEvent($nEvent);
            }
        }
        $this->OnEventAfter($nEvent);
    }
 
    // -- OVERRIDE -- //
    // ++ NEW ++ //
 
    // PURPOSE: by default, Self handles event before passing it down
    protected function OnEventBefore(int $nEvent) : void	{ $this->OnEventDispatch($nEvent); }
    protected function OnEventAfter(int $nEvent) : void	{}	// stub
 
    // -- NEW -- //
 
}
trait ftRenderableTree {
    protected function RenderNodes() : string {
        $out = '';
        if ($this->HasNodes()) {
            $out = $this->RenderBeforeNodes();
            $ar = $this->GetNodes();
            foreach ($ar as $name => $oNode) {
                $out .= $oNode->Render();
            }
            $out .= $this->RenderAfterNodes();
        }
        return $out;
    }
    protected function RenderBeforeNodes() : string { return ''; }
    protected function RenderAfterNodes() : string { return ''; }
}
// PURPOSE: So a node can respond to events (does not pass them down to subnodes)
trait ftExecutableTwig {
    public function DoEvent(int $nEvent) : void { $this->OnEventDispatch($nEvent); }
    protected function OnEventDispatch(int $nEvent) : void {
        switch ($nEvent) {
          case KI_NODE_EVENT_DO_BUILDING:
            $this->OnCreateElements();
            break;
          case KI_NODE_EVENT_DO_FIGURING:
            $this->OnRunCalculations();            break;
        }
      }
    abstract protected function OnCreateElements() : void;
    abstract protected function OnRunCalculations() : void;
}