Setup (divs and tables on page load)

Setup

Setup()

function Setup()
{
	Setup.Elements( document.getElementsByTagName( "DIV"   ) );
	Setup.Elements( document.getElementsByTagName( "FORM"  ) );
	Setup.Elements( document.getElementsByTagName( "TBODY" ) );
}
Setup.All
=
function()
{
    Selects.CallSetup = true;
    Selects( APIServer );
}
Setup.CreateTableSetupFn
=
function( id, nr_columns, clear )
{
    var path   = "";
    var search = "";

    if ( undefined === clear ) clear = false;

    return Setup.CreateTableSetupWithDownFn( id, nr_columns, path, search, clear );
}
Setup.CreateTableSetupWithDownFn
=
function( id, nr_columns, path, search, clear )
{
    if ( undefined === clear ) clear = false;

    /*
     *  The returned function parses the JSON formatted response text and creates a table row template for each result tuple.
     *  These are added to the tbody corresponding to 'id' - 'nr_of_columns' is used if no result tuples are returned.
     */

    var fn
    =
    function( responseText )
    {
        var json  = typeof responseText === 'string' || responseText instanceof String ? JSON.parse( responseText ) : responseText;
        var tbody = document.getElementById( id );

        if ( tbody && ("OK" == json.status) )
        {
            var htm  = Setup.CreateTableSetupFn.RetrieveTemplate( id );
            var htm2 = Setup.CreateTableSetupFn.RetrieveTemplate( id + "-tally" );
            var htm3 = Setup.CreateTableSetupFn.RetrieveTemplate( id + "-summary" );

            if ( ! htm )
            {
                alert( "Table '" + id + "' is missing a row template with the id: '" + id + "-template'" );
            }
            else
            {
                var n      = json.results.length;
                var offset = parseInt( tbody.getAttribute( "data-offset" ) );
                    offset = isNaN( offset ) ? 0 : offset;

                var limit  = parseInt( tbody.getAttribute( "data-limit"  ) );
                    limit  = isNaN( limit ) ? 0 : limit;

                if ( (0 == n) && (0 == offset) )
                {
                    var tr = document.createElement( "TR" );
                        tr.innerHTML = "<td colspan='" + nr_columns + "'>No entries added.</td>";

                    Setup.Clear( tbody );

                    tbody.appendChild( tr );
                }
                else
                {
                    if ( clear ) Setup.Clear( tbody );

                    if ( n != limit )
                    {
                        var load_more = document.getElementById( "tbody-load-more" );
                        if ( load_more ) load_more.style.display = "none";
                    }

                    var T = { t: null };

                    var loading = tbody.querySelector( "TR.loading" );
                    if ( loading )
                    {
                        tbody.removeChild( loading )
                    }
                    else
                    {
                        var rows = tbody.querySelectorAll( "TR" );

                        if ( 0 < rows.length )
                        {
                            var tds = rows[0].querySelectorAll( "TD" );

                            if ( (0 < tds.length) && (-1 != tds[0].innerHTML.toLowerCase().indexOf( "loading" ) ) )
                            {
                                tbody.removeChild( rows[0] );
                            }
                        } 
                    }
                    
                    for ( var i=0; i < n; i++ )
                    {
                        var e = document.createElement( "TR" );
                        var t = json.results[i];
                            t['i'] = i + 1;

                        Setup.CreateTableSetupFn.AddT( T, t );

                        if ( t.hasOwnProperty( "results_set_type" ) && ("summary" == t.results_set_type) )
                        {
                            e.innerHTML = Replace( htm3, t );
                        }
                        else
                        {
                            e.innerHTML = Replace( htm, t );

                            if ( path )
                            {
                                e.style.cursor = "pointer";
                                e.onclick      = Locations.CreateDownFn( Replace( path, t, true ), Replace( search, t, true ) );
                                e.className    = "clickable";
                            }
                        }
                        
                        if ( 0 == (i % 2) )
                        {
                            e.className += " alternate"
                        }

                        if ( "css_class" in t )
                        {
                            e.className += " " + t['css_class'];
                        }

                        Setup.URIEncodeContainedLinks( e );

                        tbody.appendChild( e );
                    }

                    if ( htm2 && T.t )
                    {
                        var e = document.createElement( "TR" );
                            e.innerHTML = Replace( htm2, T.t );

                        tbody.appendChild( e );
                    }
                }
            }
        }
    }
    
    return fn;
}

Setup.CreateTableSetupFn.RetrieveTemplate
=
function( id )
{
    var htm             = "";
    var row_template_id = id + "-template";
    var template_tr     = document.getElementById( row_template_id );

    if ( template_tr )
    {
        htm = template_tr.innerHTML;
    }
    
    return htm;
}

Setup.CreateTableSetupFn.RetrieveTemplateParameters
=
function( id )
{
    var parameters      = "";
    var row_template_id = id + "-template";
    var template_tr     = document.getElementById( row_template_id );

    if ( template_tr.hasAttribute( "data-parameters" ) )
    {
        parameters = template_tr.getAttribute( "data-parameters" );
    }
    
    return parameters;
}
Setup.CreateTableSetupFn.AddT
=
function( T, t )
{
    if ( null == T.t )
    {
        T.t = Setup.CreateTableSetupFn.AddT.Clone( t );

        for ( var key in T.t )
        {
            if ( isNaN( T.t[key] ) ) T.t[key] = "";
        }
    }
    else
    {
        for ( var key in T.t )
        {
            if ( !isNaN( T.t[key] ) && !isNaN( t[key] ) )
            {
                T.t[key] = parseInt( T.t[key] ) + parseInt( t[key] );
            }
        }
    }
}

Setup.CreateTableSetupFn.AddT.Clone
=
function( obj )
{
    var ret = {};

    for ( var name in obj )
    {
        var value = obj[name];
        ret[name] = obj[name];
    }

    return ret;
}
Setup.CreateTableSetupWithClickFn
=
function( id, nr_columns, click_fn )
{
    /*
     *  The returned function parses the JSON formatted response text and creates a table row template for each result tuple.
     *  These are added to the tbody corresponding to 'id' - 'nr_of_columns' is used if no result tuples are returned.
     */

    var fn
    =
    function( responseText )
    {
        var json  = JSON.parse( responseText );
        var tbody = document.getElementById( id );
        var more  = tbody.getAttribute( "data-more" );

        if ( tbody && ("OK" == json.status) )
        {
            var htm  = Setup.CreateTableSetupFn.RetrieveTemplate( id );
            var htm2 = Setup.CreateTableSetupFn.RetrieveTemplate( id + "-tally" );
            var htm3 = Setup.CreateTableSetupFn.RetrieveTemplate( id + "-summary" );

            var parameters = Setup.CreateTableSetupFn.RetrieveTemplateParameters( id );

            if ( ! htm )
            {
                alert( "Table '" + id + "' is missing a row template with the id: '" + id + "-template'" );
            }
            else
            {
                var n      = json.results.length;
                var offset = parseInt( json.offset );
                    offset = isNaN( offset ) ? 0 : offset;

                var limit  = parseInt( json.limit );
                    limit  = isNaN( limit ) ? 0 : limit;

                if ( (0 == n) && (0 == offset) )
                {
                    var tr = document.createElement( "TR" );
                        tr.innerHTML = "<td colspan='" + nr_columns + "'>No entries added.</td>";

                    Setup.Clear( tbody );

                    tbody.appendChild( tr );
                }
                else
                {
                    if ( more )
                    {
                        var count  = json.results[0].count;
                        var button = document.getElementById( more );

                        if ( button )
                        {
                            button.setAttribute( "data-limit", limit );
                            button.setAttribute( "data-offset", offset + limit );

                            if ( offset + n < count )
                            {
                                Class.RemoveClass( button, "hidden" );
                            }
                            else
                            {
                                Class.AddClass( button, "hidden" );
                            }
                        }
                    }

                    var T = { t: null };

                    var loading = tbody.querySelector( "TR.loading" );
                    if ( loading )
                    {
                        tbody.removeChild( loading )
                    }
                    else
                    {
                        var rows = tbody.querySelectorAll( "TR" );

                        if ( 0 < rows.length )
                        {
                            var tds = rows[0].querySelectorAll( "TD" );

                            if ( (0 < tds.length) && (-1 != tds[0].innerHTML.toLowerCase().indexOf( "loading" ) ) )
                            {
                                tbody.removeChild( rows[0] );
                            }
                        } 
                    }
                    
                    for ( var i=0; i < n; i++ )
                    {
                        var e = document.createElement( "TR" );
                        var t = json.results[i];
                            t['i'] = i + 1;

                        Setup.CreateTableSetupFn.AddT( T, t );

                        if ( parameters )
                        {
                            e.setAttribute( "data-parameters", Replace( parameters, t ) );
                        }

                        if ( t.hasOwnProperty( "results_set_type" ) && ("summary" == t.results_set_type) )
                        {
                            e.innerHTML = Replace( htm3, t );
                        }
                        else
                        {
                            e.innerHTML = Replace( htm, t );

                            if ( click_fn )
                            {
                                e.style.cursor = "pointer";
                                e.onclick      =  click_fn;
                                e.className    = "clickable";
                            }
                        }
                        
                        if ( 0 == (i % 2) )
                        {
                            e.className += " alternate"
                        }

                        if ( "css_class" in t )
                        {
                            e.className += " " + t['css_class'];
                        }

                        Setup.URIEncodeContainedLinks( e );

                        tbody.appendChild( e );
                    }

                    if ( htm2 && T.t )
                    {
                        var e = document.createElement( "TR" );
                            e.innerHTML = Replace( htm2, T.t );

                        tbody.appendChild( e );
                    }
                }
            }
        }
    }
    
    return fn;
}
Setup.CreateFormSetupFn
=
function( id, key_field )
{
    var fn
    =
    function( responseText )
    {
        InsertResponseValues( id, key_field, responseText )
    }

    return fn;
}
Setup.CreateDivSetupFn
=
function( id )
{
    var fn
    =
    function( responseText )
    {
        var div = document.getElementById( id )
        
        if ( div )
        {
            var json = typeof responseText === 'string' || responseText instanceof String ? JSON.parse( responseText ) : responseText;
            
            if ( ("OK" == json.status) && (1 == json.results.length) )
            {
                div.innerHTML = Replace( div.innerHTML, json.results[0] )
                div.style.opacity = "1.0"

                Setup.URIEncodeContainedLinks( div );
            }
            else
            {
                alert( "An unexpected error occurred while retrieving data" )
            }
        }
    }
    
    return fn;
}
Setup.CreateMultiDivSetupFn
=
function( id, click_fn )
{
    var fn
    =
    function( responseText )
    {
        var div  = document.getElementById( id                     );
        var t    = document.getElementById( id + "-template"       );
        var et   = document.getElementById( id + "-empty-template" );

        var more = div.getAttribute( "data-more" );
        var type =   t.getAttribute( "data-type" );

        if ( div && t )
        {
            var json = JSON.parse( responseText )

            if ( "ERROR" == json.status )
            {
                alert( "An unexpected error occurred while retrieving data" );
            }
            else
            {
                var cls    = t.className; 
                var n      = json.results.length;
                var offset = parseInt( json.offset );
                    offset = isNaN( offset ) ? 0 : offset;

                var limit  = parseInt( json.limit );
                    limit  = isNaN( limit ) ? 0 : limit;

                if ( 0 == n )
                {
                    if ( et )
                    {
                        var type            = type ? type : "DIV";
                        var child           = document.createElement( type );
                            child.className = cls;
                            child.innerHTML = et.innerHTML;

                        div.appendChild( child );
                    }
                }
                else
                if ( (0 == n) && (0 == offset) )
                {
                    // Ignore for now.
                }
                else
                {
                    if ( more )
                    {
                        var count  = json.results[0].count;
                        var button = document.getElementById( more );

                        if ( button )
                        {
                            button.setAttribute( "data-limit", limit );
                            button.setAttribute( "data-offset", offset + limit );

                            if ( offset + n < count )
                            {
                                Class.RemoveClass( button, "hidden" );
                            }
                            else
                            {
                                Class.AddClass( button, "hidden" );
                            }
                        }
                    }

                    for ( var i=0; i < n; i++ )
                    {
                        var type            = type ? type : "DIV";
                        var child           = document.createElement( type );
                            child.className = cls;
                            child.innerHTML = Replace( t.innerHTML, json.results[i] );

                        if ( click_fn )
                        {
                            child.style.cursor = "pointer";
                            child.onclick      = click_fn;
                        }

                        Setup.URIEncodeContainedLinks( child );

                        div.appendChild( child );
                    }
                }
            }
        }
    }
    
    return fn;
}
Setup.CreateFormHandlerFn
=
function( id, handler, parameter )
{
    var fn
    =
    function( responseText )
    {
        if ( ! responseText )
        {
            setTimeout( function() { handler( parameter ) }, 1000 );
        }
        else
        {
            var response = JSON.parse( responseText );

            if ( response && ("ERROR" == response.status) )
            {
                alert( response.error );
            }
            else
            {
                if ( 1 == response.results.length )
                {
                    Setup.CreateFormHandlerFn.UpdateSearchVariables( response.results[0] );
                }

                handler( parameter );
            }
        }
    }
    
    return fn;
}
Setup.CreateFormHandlerFn.UpdateSearchVariables
=
function( tuple )
{
    var parameters = Locations.SearchValues();
    var n          = parameters.length;

    for (const [key, value] of Object.entries(parameters))
    {
        if ( tuple.hasOwnProperty( key ) )
        {
            parameters[key] = tuple[key];
        }
    }

    var search = "?" + Call.EncodeToString( parameters );

    window.history.replaceState( null, null, search );
}
Setup.CreateFormFinalFn
= function( form_id, key, handler )
{
    var fn
    =
    function( responseText )
    {
        var url      = document.getElementById( form_id ).getAttribute( "data-final-url" );
        var response = JSON.parse( responseText );

        if ( "ERROR" == response.status )
        {
            alert( response.error );
        }
        else
        if ( !url )
        {
            alert( "Missing --data-final-url for form with id: " + form_id );
        }
        else
        if ( response.results && response.results[0] )
        {
            var first = response.results[0];
            var parameters = {};
                parameters[key] = first[key];

            Call( url, parameters, null );

            window.setTimeout( handler, 1000 );
        }
    }
    return fn;
}
Setup.More
=
function( event )
{
    var button    = event.target;
    var target_id = button.getAttribute( "data-target" );
    var target    = document.getElementById( target_id );

    if ( !target )
    {
        alert( "Error, could not find target for Setup.More" );
    }
    else
    {
        var offset   = button.getAttribute( "data-offset"    );
        var endpoint = target.getAttribute( "data-setup-url" );

        var parameters        = Locations.SearchValues();
            parameters.offset = offset;

        Call( endpoint, parameters, target.setup );
    }

    return false;
}

Helper functions

Setup.Elements
=
function( elements )
{
	var n = elements.length;
	
	for ( var i=0; i < n; i++ )
	{
		var element    = elements[i];
		var parameters = GetSearchValues();
        var parameters = Setup.AddSelectCookies( parameters );

		Setup.Element( element, parameters );
	}
}

Setup.Element
=
function( element, parameters_orig )
{
	if ( element && element.hasAttribute( "data-setup-url" ) && !element.hasAttribute( "data-skip-setup" ) )
	{
		var url        = element.getAttribute( "data-setup-url" );
		var handler    = Setup.DefaultHandler;
        var parameters = { ...parameters_orig };
        
        if ( element.hasAttribute( "data-parameter-names" ) )
        {
            var names = element.getAttribute( "data-parameter-names" ).split( "," );
            var n     = names.length;
            
            for ( var i=0; i < n; i++ )
            {
                var name  = names[i];
                var value = DataStorage.Local.GetUserItem( name );
                
                if ( !value )
                {
                    value = DataStorage.Session.GetItem( name );
                }
                
                parameters[name] = value;
            }
        }

		if ( element.hasOwnProperty( "setup" ) )
		{
			handler = element.setup;

			handler = handler ? handler : element.handler;
		}

		if ( !parameters.target_id && element.hasAttribute( "id" ) )
		{
			parameters.target_id = element.getAttribute( "id" );
		}

		Call( url, parameters, handler );
	}
    else
    if ( element && ("FORM" == element.tagName) )
    {
        var fakeResponseText = '{"status":"OK","results":[{}]}';

        InsertResponseValues( element.id, null, fakeResponseText );
    }
}

Setup.DefaultHandler
=
function( responseText )
{
	var json = JSON.parse( responseText );
	
	if ( "OK" != json.status )
	{
		console.log( responseText );
	}
}

Setup.AddSelectCookies
=
function( parameters )
{
    var selects = document.getElementsByTagName( "SELECT" );
    var n       = selects.length;

    for ( var i=0; i < n; i++ )
    {
        var s = selects[i];

        if ( true == s.hasAttribute( "data-cookie" ) )
        {
            var name  = s.name;
            var value = GetCookie( name );

            parameters[name] = value;

            if ( ! s.hasAttribute( "data-value" ) )
            {
                s.setAttribute( "data-value", value );
            }
        }
        else
        if ( true == s.hasAttribute( "data-local" ) )
        {
            var select = s;
            var path   = location.pathname;
            var name   = select.name;
            var key    = name + "|" + path;

            if ( Session.user_hash )
            {
                key = Session.user_hash + "|" + name + "|" + path;
            }

            if ( DataStorage.Local.HasItem( key ) )
            {
                var value = DataStorage.Local.GetItem( key )

                parameters[name] = value;

                if ( ! s.hasAttribute( "data-value" ) )
                {
                    s.setAttribute( "data-value", value );
                }
            }
        }
    }

    return parameters;
}
Setup.Clear
=
function( container )
{
    //  Need to remove from back to maintain
    //  order of children.

    var n = container.children.length - 1;
    
    for ( var i = n; 0 <= i; i-- )
    {
        var child = container.children[i];

        switch ( child.tagName )
        {
        case "DIV":
        case "TR":
            if ( -1 == child.id.toLowerCase().indexOf( "template" ) )
            {
                container.removeChild( child );
            }
            break;
        }
    }
}
Setup.Download
=
function( event )
{
    var link         = event.target;
    var id           = link.id;
    var converter_fn = link.converter;
    var type         = link.getAttribute( "data-content-type"  );
    var url          = link.getAttribute( "data-download-url"  );
    var name         = link.getAttribute( "data-download-name" );
    var params       = link.hasAttribute( "data-download-parameters" )
                     ? GetSearchValues.CreateDictionary( link.getAttribute( "data-download-parameters" ) )
                     : Locations.SearchValues();

    event.preventDefault();

    params.submit    = name ? name : "";
    params.target_id = id;

    link.removeEventListener( 'click', Setup.Download );
    link.onclick = null;

    Call
    (
        url,
        params,
        Setup.CreateDownloadLinkFn( type, converter_fn )
    );
}
Setup.CreateDownloadLinkFn
=
function( type, converter_fn )
{
    var fn
    =
    function( responseText )
    {
        var response = JSON.parse( responseText );

        if ( "ERROR" == response.status )
        {
            alert( response.error );
        }
        else
        {
            var target_id = response.target_id;
            var name      = response.submit;

            const blob = new Blob
            (
                converter_fn( type, response ),
                { type: type }
            );

            const url = URL.createObjectURL(blob);

            var a      = document.getElementById( target_id );
                a.href = url;

            if ( name )
            {
                a.download = name;
            }

            a.click();
        }
    }
    return fn;
}
Setup.URIEncodeContainedLinks
=
function( e )
{
    var links = e.getElementsByTagName( "A" );
    var n     = links.length;

    for ( var i=0; i < n; i++ )
    {
        var bits = links[i].href.split( "?" );

        if ( 2 == bits.length )
        {
            var href       = bits[0] + "?";
            var parameters = GetSearchValues.CreateDictionary( bits[1] );
            var sep        = "";

            for ( key in parameters )
            {
                href += sep + key + "=" + encodeURIComponent( parameters[key] );
                sep = "&";
            }
            links[i].href = href;
        }
    }
}
Setup.CreateMultiFormSetupFn
=
function( form_id, primary_key )
{
    var fn
    =
    function( responseText )
    {
        Setup.CreateFormSetupFn( form_id, primary_key )( responseText );

        var response = JSON.parse( responseText );

        if ( "OK" == response.status )
        {
            var form = document.getElementById( form_id );
            var divs = form.querySelectorAll( "DIV.multiselect" );
            var n    = divs.length;

            for ( var i=0; i < n; i++ )
            {
                Setup.CreateMultiFormSetupFn.Populate( form, divs[i] );
            }
        }
    }

    return fn;
}

Setup.CreateMultiFormSetupFn.Populate
=
function( form, div )
{
    var hidden_input_id = div.getAttribute  ( "data-hidden-input-id" );
    var hidden_input    = form.querySelector( "INPUT#" + hidden_input_id  );
    var div_id          = div.id;
    var values_string   = hidden_input.value.substring( 1, hidden_input.value.length - 1 );
    var list            = values_string.split( "][" );
    var n               = list.length;

    for ( var i=0; i < n; i++ )
    {
        var key_value = list[i];
        var bits      = key_value.split( "=" );

        if ( 2 == bits.length )
        {
            var value = bits[0];
            var text  = bits[1];
            var span  = div.querySelector( "SPAN[data-value='" + value + "']" );

            if ( !span )
            {
                var span           = document.createElement( "SPAN" );
                    span.innerText = text;
                    span.onclick   = AddToMulti.Remove;
                    span.setAttribute( "data-value", value );

                div.appendChild( span );
            }
        }
    }
}