function TabManager() {
    /* reg: a map from UI name (probably the Agent address) to an
     * object with some UI info (such as the id of the div for the
     * UI); see add function.
     */
    this.ui_reg = {};

    /* constructors: a map from AgentClass name (string) to
     * constructor function.  Specific Agent UIs can add themselves to
     * this registry.
     */
    this.constructors = {};

    // Internal counter for generating ~unique ids for each UI panel.
    this._next_id = 1000000;

    /* We haven't bothered with putting the methods into .prototype,
       since this is likely to be a singleton class. */

    this.add = function(name, agent_class, args) {
        /* Create an Agent-specific UI tab.
         *
         * @param {string} name: the identifier that will be used for
         *     the UI.  This is likely to simply be the Agent address.
         * @param {string or function} agent_class: If this is a
         *     function, it is used to construct UI Tab.  If it is a
         *     string, it is used as a key to find a constructor
         *     function in this.constructors.  If it is null, the user
         *     will be presented with a modal dialog to select a
         *     constructor function (from this.constructors).
         * @param {Object} args: additional arguments needed to
         *     construct the UI.  For Agent UIs, the OCS address of
         *     the Agent must be present in member 'address'.
         *
         * @returns {Object} The UI info from this.ui_reg.
         *
         * Note that if the 'name' already has an associated UI tab, a
         * new tab is not created but the info block is still
         * returned.
         */

        if (this.ui_reg[name])
            return this.ui_reg[name];
        
        if (agent_class instanceof Function) {
            pop_func = agent_class;
        } else if (!agent_class || !this.constructors[agent_class]) {
            /* If the agent_class is null or does not match any
             * registered constructor, allow the user to select a
             * constructor from a modal dialog. */
            var ap = $('#agent_class_picker');
            var list = $('<ul>');
            var tabman = this;
            $.each(this.constructors, function(key, val) {
                var item = $(`<span>`).append(`${key}`).addClass('clickable').on('click', function () {
                    // Call self with the agent_class now populated.
                    if (val)
                        tabman.add(name, val, args);
                    $.modal.close();
                });
                list.append($(`<li>`).append(item));
            });
            ap.html(`<p>Select the agent class appropriate for use with ` +
                    `<tt>${name}</tt> which is of class <tt>${agent_class}</tt>:</p>`);
            ap.append(list);
            ap.append(`<p>If there is no class-specific handler, you might try GenericAgent.`);
            ap.modal();
            return;
        } else {
            pop_func = this.constructors[agent_class];
        }

        // Get a unique id - this will be the base ID for all the
        // controls in the tab.
        var uid = 'ocs'+(this._next_id++);

        // Create new div and add it to the tab registry.
        dest_div_id = `${uid}-tab`;
        var div = $('#tabs').append(`<div id="${dest_div_id}" class="ocs_tab">`);
        var destructor = pop_func($('#'+dest_div_id), uid, args);
        $(`<li><a id="${uid}-tab-link" href='#${dest_div_id}'>${name}</a><span id="${uid}-closer">` +
          `<i class="fa fa-window-close"></i></span></li>`)
            .appendTo("#tabs .ui-tabs-nav");
        $('#tabs').tabs( "refresh" );

        // Mark the tab clearly if agent connection drops.
        ocs_connection.agent_list.subscribe(uid + '-tab', args.address, function (addr, ok) {
            var el = $('#' + uid + '-tab-link');
            if (ok)
                el.removeClass('ocs_missing_agent');
            else
                el.addClass('ocs_missing_agent');
        });

        var tabman = this;
        $('#' + uid + '-closer')
            .on('click', () => tabman.del(uid))
            .addClass('obviously_clickable');
        this.ui_reg[name] = {'base_id': uid, 'div_id': dest_div_id,
                          'destructor': destructor};
        return this.ui_reg[name];
    }

    this.activate = function(base_id) {
        // This seems to be the most reliable.
        $('#' + base_id + '-tab-link').trigger('click');
    }

    this.del = function(base_id) {
        /* Tear down the tab specified by base_id.  This will remove
         * the tab from the document tree and also call the destructor
         * function for that UI. */
        ocs_connection.agent_list.unsubscribe(base_id + '-tab');

        // Un-tab...
        var tabIdStr = '#'+base_id+'-tab';
        $(tabIdStr).remove();
        var hrefStr = "a[href='" + tabIdStr + "']"
        $(hrefStr).closest("li").remove()
        $('#tabs').tabs("refresh");
        // Pop from registry and call destructors.
        var reg = this.ui_reg;
        $.each(reg, function (key, val) {
            if (val.base_id == base_id) {
                delete reg[key];
                if (val.destructor)
                    val.destructor();
            }
        });
    }
}
/*
  monitor_ui.js

  Support classes for monitor.js.  Include this before monitor.js and
  before any scripts that use the Ui construction functions.

  The TabManager class is used to create new tabs with Agent UIs for a
  particular Agent instance.  The UIs themselves can be implemented
  "manually" or by using OcsUiHelper objects to generate simple
  controls and indicators.

  The TabManager calls constructor functions associated with
  particular Agent Classes to construct controls and indicators.  The
  prototype for such constructor functions is:

    function constructor(p, base_id, args)

      @param {Object} p : Jquery element (probably a $('<div>')) where
          the UI should be inserted.
      @param {string} base_id : Unique string to use as a base for all
          element ids generated for the UI.
      @param {Object} args : Additional parameters for creating the UI and
          making any necessary Agent/other connections.

      The content of args is specific to the constructor but at a
      minimum you should expect 'address'.

      The constructor should return a destructor function that will be
      called after removing the UI elements.
    
 */


The TabManager class is used to create new tabs with Agent UIs for a
particular Agent instance.  The UIs themselves can be implemented
"manually" or by using OcsUiHelper objects to generate simple
controls and indicators.

The TabManager calls constructor functions associated with
particular Agent Classes to construct controls and indicators.  The
prototype for such constructor functions is.



@param {Object} p : Jquery element (probably a $('')) where
the UI should be inserted.
@param {string} base_id : Unique string to use as a base for all
element ids generated for the UI.
@param {Object} args : Additional parameters for creating the UI and
making any necessary Agent/other connections.

The content of args is specific to the constructor but at a
minimum you should expect 'address'.

The constructor should return a destructor function that will be
called after removing the UI elements.
The TabManager class is used to create new tabs with Agent UIs for a particular Agent instance . The UIs themselves can be implemented " manually " or by using OcsUiHelper objects to generate simple controls and indicators . The TabManager calls constructor functions associated with particular Agent Classes to construct controls and indicators . The prototype for such constructor functions is . @param { Object } p : Jquery element ( probably a $ ( ' ' )) where the UI should be inserted . @param { string } base_id : Unique string to use as a base for all element ids generated for the UI . @param { Object } args : Additional parameters for creating the UI and making any necessary Agent / other connections . The content of args is specific to the constructor but at a minimum you should expect ' address ' . The constructor should return a destructor function that will be called after removing the UI elements .
function TabManager() {
    this.ui_reg = {};
    this.constructors = {};
    this._next_id = 1000000;
    this.add = function(name, agent_class, args) {
        if (this.ui_reg[name])
            return this.ui_reg[name];
        if (agent_class instanceof Function) {
            pop_func = agent_class;
        } else if (!agent_class || !this.constructors[agent_class]) {
            var ap = $('#agent_class_picker');
            var list = $('<ul>');
            var tabman = this;
            $.each(this.constructors, function(key, val) {
                var item = $(`<span>`).append(`${key}`).addClass('clickable').on('click', function () {
                    if (val)
                        tabman.add(name, val, args);
                    $.modal.close();
                });
                list.append($(`<li>`).append(item));
            });
            ap.html(`<p>Select the agent class appropriate for use with ` +
                    `<tt>${name}</tt> which is of class <tt>${agent_class}</tt>:</p>`);
            ap.append(list);
            ap.append(`<p>If there is no class-specific handler, you might try GenericAgent.`);
            ap.modal();
            return;
        } else {
            pop_func = this.constructors[agent_class];
        }
        var uid = 'ocs'+(this._next_id++);
        dest_div_id = `${uid}-tab`;
        var div = $('#tabs').append(`<div id="${dest_div_id}" class="ocs_tab">`);
        var destructor = pop_func($('#'+dest_div_id), uid, args);
        $(`<li><a id="${uid}-tab-link" href='#${dest_div_id}'>${name}</a><span id="${uid}-closer">` +
          `<i class="fa fa-window-close"></i></span></li>`)
            .appendTo("#tabs .ui-tabs-nav");
        $('#tabs').tabs( "refresh" );
        ocs_connection.agent_list.subscribe(uid + '-tab', args.address, function (addr, ok) {
            var el = $('#' + uid + '-tab-link');
            if (ok)
                el.removeClass('ocs_missing_agent');
            else
                el.addClass('ocs_missing_agent');
        });
        var tabman = this;
        $('#' + uid + '-closer')
            .on('click', () => tabman.del(uid))
            .addClass('obviously_clickable');
        this.ui_reg[name] = {'base_id': uid, 'div_id': dest_div_id,
                          'destructor': destructor};
        return this.ui_reg[name];
    }
    this.activate = function(base_id) {
        $('#' + base_id + '-tab-link').trigger('click');
    }
    this.del = function(base_id) {
        ocs_connection.agent_list.unsubscribe(base_id + '-tab');
        var tabIdStr = '#'+base_id+'-tab';
        $(tabIdStr).remove();
        var hrefStr = "a[href='" + tabIdStr + "']"
        $(hrefStr).closest("li").remove()
        $('#tabs').tabs("refresh");
        var reg = this.ui_reg;
        $.each(reg, function (key, val) {
            if (val.base_id == base_id) {
                delete reg[key];
                if (val.destructor)
                    val.destructor();
            }
        });
    }
}
function TabManager ( ) { this . ui_reg = { } ; this . constructors = { } ; this . _next_id = 1000000 ; this . add = function ( name , agent_class , args ) { if ( this . ui_reg [ name ] ) return this . ui_reg [ name ] ; if ( agent_class instanceof Function ) { pop_func = agent_class ; } else if ( ! agent_class || ! this . constructors [ agent_class ] ) { var ap = $ ( '#agent_class_picker' ) ; var list = $ ( '<ul>' ) ; var tabman = this ; $ . each ( this . constructors , function ( key , val ) { var item = $ ( ` ` ) . append ( ` ${ key } ` ) . addClass ( 'clickable' ) . on ( 'click' , function ( ) { if ( val ) tabman . add ( name , val , args ) ; $ . modal . close ( ) ; } ) ; list . append ( $ ( ` ` ) . append ( item ) ) ; } ) ; ap . html ( ` ` + ` ${ name } ${ agent_class } ` ) ; ap . append ( list ) ; ap . append ( ` ` ) ; ap . modal ( ) ; return ; } else { pop_func = this . constructors [ agent_class ] ; } var uid = 'ocs' + ( this . _next_id ++ ) ; dest_div_id = ` ${ uid } ` ; var div = $ ( '#tabs' ) . append ( ` ${ dest_div_id } ` ) ; var destructor = pop_func ( $ ( '#' + dest_div_id ) , uid , args ) ; $ ( ` ${ uid } ${ dest_div_id } ${ name } ${ uid } ` + ` ` ) . appendTo ( "#tabs .ui-tabs-nav" ) ; $ ( '#tabs' ) . tabs ( "refresh" ) ; ocs_connection . agent_list . subscribe ( uid + '-tab' , args . address , function ( addr , ok ) { var el = $ ( '#' + uid + '-tab-link' ) ; if ( ok ) el . removeClass ( 'ocs_missing_agent' ) ; else el . addClass ( 'ocs_missing_agent' ) ; } ) ; var tabman = this ; $ ( '#' + uid + '-closer' ) . on ( 'click' , ( ) => tabman . del ( uid ) ) . addClass ( 'obviously_clickable' ) ; this . ui_reg [ name ] = { 'base_id' : uid , 'div_id' : dest_div_id , 'destructor' : destructor } ; return this . ui_reg [ name ] ; } this . activate = function ( base_id ) { $ ( '#' + base_id + '-tab-link' ) . trigger ( 'click' ) ; } this . del = function ( base_id ) { ocs_connection . agent_list . unsubscribe ( base_id + '-tab' ) ; var tabIdStr = '#' + base_id + '-tab' ; $ ( tabIdStr ) . remove ( ) ; var hrefStr = "a[href='" + tabIdStr + "']" $ ( hrefStr ) . closest ( "li" ) . remove ( ) $ ( '#tabs' ) . tabs ( "refresh" ) ; var reg = this . ui_reg ; $ . each ( reg , function ( key , val ) { if ( val . base_id == base_id ) { delete reg [ key ] ; if ( val . destructor ) val . destructor ( ) ; } } ) ; } }
monitor_ui.js
Support classes for monitor.js.
monitor_ui . js Support classes for monitor . js .
/* reg: a map from UI name (probably the Agent address) to an
     * object with some UI info (such as the id of the div for the
     * UI); see add function.
     */ /* constructors: a map from AgentClass name (string) to
     * constructor function.  Specific Agent UIs can add themselves to
     * this registry.
     */ // Internal counter for generating ~unique ids for each UI panel. /* We haven't bothered with putting the methods into .prototype,
       since this is likely to be a singleton class. */ /* Create an Agent-specific UI tab.
         *
         * @param {string} name: the identifier that will be used for
         *     the UI.  This is likely to simply be the Agent address.
         * @param {string or function} agent_class: If this is a
         *     function, it is used to construct UI Tab.  If it is a
         *     string, it is used as a key to find a constructor
         *     function in this.constructors.  If it is null, the user
         *     will be presented with a modal dialog to select a
         *     constructor function (from this.constructors).
         * @param {Object} args: additional arguments needed to
         *     construct the UI.  For Agent UIs, the OCS address of
         *     the Agent must be present in member 'address'.
         *
         * @returns {Object} The UI info from this.ui_reg.
         *
         * Note that if the 'name' already has an associated UI tab, a
         * new tab is not created but the info block is still
         * returned.
         */ /* If the agent_class is null or does not match any
             * registered constructor, allow the user to select a
             * constructor from a modal dialog. */ // Call self with the agent_class now populated. // Get a unique id - this will be the base ID for all the // controls in the tab. // Create new div and add it to the tab registry. // Mark the tab clearly if agent connection drops. // This seems to be the most reliable. /* Tear down the tab specified by base_id.  This will remove
         * the tab from the document tree and also call the destructor
         * function for that UI. */ // Un-tab... // Pop from registry and call destructors.