Scouts Membership System (UK) Tweaks

Provide a few tweaks including a role compliance report to the Scouts Website

// ==UserScript==
// @name         Scouts Membership System (UK) Tweaks
// @description  Provide a few tweaks including a role compliance report to the Scouts Website
// @namespace    http://tampermonkey.net/
// @version      100000041
// @author       David Breakwell
// @match        https://membership.scouts.org.uk/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=scouts.org.uk
// @grant  GM_xmlhttpRequest
// @grant GM_getResourceURL
// @grant GM_getResourceText
// @grant GM_addStyle
// @grant GM_notification
// @grant GM_log
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_getResourceURL
// @run-at      document-idle
// @require     http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// @require     http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js
// @require     https://cdnjs.cloudflare.com/ajax/libs/gojs/3.0.17/go.js
// @require     https://unpkg.com/[email protected]/dist/leaflet.js
// @require     https://cdn.jsdelivr.net/npm/[email protected]/dist/xlsx.min.js

// @require     https://unpkg.com/[email protected]/dist/js/tabulator.min.js
// @require     https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js
// @resource    jqUI_CSS  http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css
// @resource    leaf https://unpkg.com/[email protected]/dist/leaflet.css
// @resource    IconSet1  http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/images/ui-icons_222222_256x240.png
// @resource    IconSet2  http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/images/ui-icons_454545_256x240.png
// @resource    LeafletPin  https://unpkg.com/[email protected]/dist/images/marker-icon.png
// @resource    LeafletShadow   https://unpkg.com/[email protected]/dist/images/marker-shadow.png
// @resource    tabulator https://unpkg.com/[email protected]/dist/css/tabulator.min.css
// @connect tsa-memportal-prod-fun01.azurewebsites.net
// @connect tsauksprodasa001.blob.core.windows.net
// @connect tsauksprodasa001.table.core.windows.net
// @connect api.postcodes.io
// @connect learn.scouts.org.uk
// @license MIT
// ==/UserScript==
//     https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js
var code_version = "100000041";
var mem = [{}];
var mems= [];
var mapmems= [];
var memsunique = [];
var roles = [];
var role_detail = [];
var dbs_detail = [];
var mem_learning= [];
var learn = [];
var mem_lcount;
var role_max_count;
var respond = false;
var end = false;
var un="";
var debug = false;
var keepalive = null;
var orgcounter = 0;
var myDiagram;
var postcode_regex = new RegExp("[A-Za-z]{1,2}[0-9Rr][0-9A-Za-z]? ?[0-9][ABD-HJLNP-UW-Zabd-hjlnp-uw-z]{2}")
var mappin = {};
var map = {};
var mapunits = [];
var postcodes = [];
var defcolors=['#fafad2','#ffe4b5','#ffa07a','#ff6347'];
var defcolorsT=['#000000','#000000','#000000','#000000'];
var colors=['#fafad2','#ffe4b5','#ffa07a','#ff6347'];
var colorsT=['#000000','#000000','#000000','#000000'];
var colormeanings = ["90 Days","60 Days","30 Days","Expired"];
var daylimits = [90,60,30,0,-9999999];
var tabletabulator
var added_css = false;
var added_default = false;
var roles_count = 0;

(function() {
    'use strict';
    function do_move() {
        console.log(document.getElementById("member-nav").value);
    }
    function dopage() {
        //    if(window.location.href.indexOf("subunitsau")!=-1) {

        setTimeout(function() {
            console.log("After Wait");
            console.log(window.location.href );
            if ((window.location.href == "https://membership.scouts.org.uk/")||(window.location.href == "https://membership.scouts.org.uk/#/")) {
                console.log("Homepage");
                if (document.getElementById("member-nav-list")==null){
                    var gotolist="<datalist id='member-nav-list'><option value='Add a New Member'></option>";
                    gotolist+="<option value='My Units'></option>";
                    gotolist+="</datalist>";

                    gotolist ='<label for="member-nav" style="font-size: 1.5em;font-weight: 600">What do you want to do?&nbsp;</label>' + gotolist;
                    gotolist += '<input style="width: 50%; font-size: 1.5em "list="member-nav-list" id="member-nav" name="member-nav-choice" onchange="do_move"/>';
                    if( document.getElementById("title")!=null) {

                        if (document.getElementById("quick_nav")==null) {
                            gotolist = '<div id="quick_nav">'+gotolist+'</div>';
                            //      document.getElementById("title").outerHTML += (gotolist);

                        } else
                        {
                            //      document.getElementById("quick_nav").innerHTML = (gotolist);
                        }
                    } // title

                } // already dispalyed
            }
            if (window.location.href.indexOf("subunitsau") != -1) {
                //document.body.appendChild(button);
                var htmlToInsert;
                htmlToInsert = '<span id="DavesnewText"><p>There are <a href="';
                htmlToInsert += window.location.href.substring(0, window.location.href.indexOf("subunitsau"));
                htmlToInsert += 'teamsau"><span id="teamcounter"></span></a> Teams at this level</p><span><span id="DavesList"><span>';
                //console.log(document.getElementById("header-main-title-all-units-grid"));

                GM_xmlhttpRequest({
                    method: "POST",
                    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/UnitTeamsAndRolesListingAsync",
                    data: '{"unitId" : "' + window.location.href.substring(window.location.href.indexOf("allunits/") + 9, window.location.href.indexOf("subunitsau") - 1) + '", "type" : "team"}',
                    headers: {
                        "Content-Type": "application/json",
                        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                        "Accept": "application/json, text/plain, */*"
                    },
                    onload: function(response) {
                        //    console.log("gut response");
                        //    console.log(response.responseText);
                        //    console.log(JSON.parse(response.responseText));
                        if (JSON.parse(response.responseText).teams.length > 1) {
                            document.getElementById("teamcounter").innerHTML = JSON.parse(response.responseText).teams.length;
                        } else {
                            htmlToInsert = '<p>There is <a href="';
                            htmlToInsert += window.location.href.substring(0, window.location.href.indexOf("subunitsau"));
                            htmlToInsert += 'teamsau/' + JSON.parse(response.responseText).teams[0].teamId + "/teamdashboardaut";
                            htmlToInsert += '"><span id="teamcounter">1</span></a> Team at this level</p>';
                            document.getElementById("DavesnewText").innerHTML = (htmlToInsert);
                        }
                        var elements = document.querySelectorAll('[id^=grid-unit]');
                        var elements2 = document.querySelectorAll('[id^=grid-parentUnit]');
                        for (var loop = 0; loop < elements.length; loop++) {
                            var htmlToInsert2 = '<a href="';
                            htmlToInsert2 += elements[loop].href.substring(0, elements[loop].href.indexOf("subunitsau"));
                            htmlToInsert2 += 'teamsau">&nbsp;&nbsp;&nbsp;<strong style=" display: inline-block;  width: 40px; height: 40px;line-height: 40px;text-align: center; background-color: #007bff;  cursor:pointer; color: white; border-color: #007bff; border-radius: 50%;font-size: 20px;font-weight: bold;">T</strong></a>';
                            elements2[loop].outerHTML += htmlToInsert2;
                        }
                    }
                }); // End of request
                document.getElementById("header-main-title-all-units-grid").innerHTML += (htmlToInsert);
                var button = document.createElement("Button");
                button.innerHTML = "Add a Member";
                button.onclick = () => {

                    window.open(window.location.href.substring(0, window.location.href.indexOf("subunitsau")) + "unitdetailsau/addmemberau", "_self");
                };
                button.style = "background: rgb(0, 97, 205);font-weight: 700;font-family: 'Nunito Sans';font-size: 20px;line-height: 1.5;color: white;min-height: 46px;max-width: 215px; cursor:pointer;vertical-align: middle;padding: 8px 24px;border-color: #007bff;";
                document.getElementById("header-main-title-all-units-grid").appendChild(button);

            }
        })

        if (window.location.href.indexOf("teamdashboardaut") != -1) {
            setTimeout(function() {
                var button = document.createElement("Button");
                button.innerHTML = "Add a Member";
                button.onclick = () => {

                    window.open(window.location.href.substring(0, window.location.href.indexOf("teamsau")) + "unitdetailsau/addmemberau", "_self");
                };
                button.style = "background: rgb(0, 97, 205);font-weight: 700;font-family: 'Nunito Sans';font-size: 20px;line-height: 1.5;color: white;min-height: 46px;max-width: 215px;vertical-align: middle;padding: 8px 24px;border: none; cursor:pointer;";
                document.getElementById("header-button-wrapper-action-list").appendChild(button);
            }, 1000);
        }

    } // dopage

    function addButton(text, onclick, cssObj,pos) {
        if (pos=="T") {
            cssObj = cssObj || {position: 'absolute', top: '63px', left:'50%', 'z-index': 3}
        }
        if (pos=="B") {
            cssObj = cssObj || {position: 'absolute', top: '30px', left:'50%', 'z-index': 3}
        }
        if (pos=="C") {
            cssObj = cssObj || {position: 'absolute', top: '30px', left:'40%', 'z-index': 3}
        }
        if (pos=="D") {
            cssObj = cssObj || {position: 'absolute', top: '63px', left:'40%', 'z-index': 3}
        }
        let button = document.createElement('button'), btnStyle = button.style
        document.body.appendChild(button)
        button.innerHTML = text
        button.onclick = onclick
        btnStyle.position = 'absolute'
        Object.keys(cssObj).forEach(key => btnStyle[key] = cssObj[key])
        return button
    }
    function get_data(count){
        var datapass;
        var max = 50;
        un = document.getElementById("gmunitname").value;
        document.getElementById("gmUnit").innerHTML = un;
        $("#gmState")[0].innerText = "Requesting Members for "+un+" ("+count+")"+"Total read "+mems.length;
        if (debug) {GM_log("get_data :Version "+code_version);}
        if (debug) {GM_log("get_data : Collection for Report");}
        if (debug) {GM_log("Unit Request for "+un+" Count "+count);}
        if (debug) {
            GM_log("Unit Request for "+un+" :"+ JSON.parse(localStorage.getItem("authInfo")).idToken.substring(1,50));
            var tokexpDate = new Date( JSON.parse(localStorage.getItem("authInfo")).account.idTokenClaims.exp *1000);
            GM_log("Token expires :"+ tokexpDate.toGMTString());
        }
        //  document.getElementById("gmState").innerText = "Hello World";
        datapass = '{"pagesize":'+max+',"nexttoken":'+count+',"filter":{"global":"","globaland":false,"fieldand":true,"filterfields":[{"field":"unitName","value":"'+un+'"}]},"isSuspended":false}';
        //       console.log(datapass);
        if (debug) {GM_log("Counter for get_data "+count);}
        GM_xmlhttpRequest({
            method: "POST",
            context: count,
            url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/MemberListingAsync",
            data: datapass,
            //            synchronous:    true,
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                "Accept": "application/json, text/plain, */*"
            },

            onload: function(response) { //  console.log(response);console.log(count);
                if (debug) {GM_log("Counter Recieved "+response.context)}
                // console.log(JSON.parse(response.responseText).data[1]);
                var mem = null;
                try { mem=JSON.parse(response.responseText).data} catch {console.log("JSON Error "+response.responseText);}
                //   mem =(JSON.parse(response.responseText).data);
                if (mem!=null) {
                    if (debug) {
                        for (var debugloop=0;debugloop<mem.length;debugloop++){
                            GM_log("Member "+mem[debugloop].firstname+" "+mem[debugloop].unitName+mem[debugloop].Role);
                        }
                    }
                }
                if ((mem==null)&&(debug)) {
                    GM_log("GetData: We got an error collecting data for mems page"+response.context);
                }
                if (mem!=null) {
                    mems = mems.concat(mem);
                }
                if (debug) {GM_log("mems now has length "+mems.length);}

                //      console.log(mem.length);
                //      console.log(JSON.parse(response.responseText).data);
                if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}
                //     console.log(mem);
                //    console.log(JSON.parse(response.responseText).data.length);
                $("#gmState")[0].innerText = "Reading Members for "+un+" ("+response.context+") "+"Total read "+mems.length+"("+mem.length+")";
                if ( JSON.parse(response.responseText).data.length ==50) {get_data(count+1); } else {roles_count = 0;report2()};},
            onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
            onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
        });

    }

    function clear_mems(){
        //if (1!=1){
        if (debug) {GM_log("Clear Mems entry: mems now has length "+mems.length);}
        if(document.getElementById("gmmissing").checked) {
            var onlymems = [];
            var onlymemsu = [];
            var act = ['managerDisclosureCheck','dataProtectionTrainingComplete','signDeclaration','updateMemberProfile','referenceRequest','safeguardconfidentialEnquiryCheck','managerWelcomeConversation','coreLearning','managerTrusteeCheck'];

            for (var i = 0; i < role_detail.length; i++) {
                var displine=true;

                if (document.getElementById("gmmissing").checked) {
                    var any_missing = false;
                    for (var j = 0; j < act.length; j++) {
                        if (role_detail[i].actions.find((element)=>element.typeid==act[j])) {
                            var value = role_detail[i].actions.find((element)=>element.typeid==act[j]).status;
                            if ((value!="Satisfactory")&&(value!="Completed")&&(value!="Held - Satisfactory")){ any_missing=true;}
                        }
                    }
                    if (any_missing==false) {displine=false}
                }
                if (displine) {
                    var memobj = new Object();
                    memobj = mems.find((element) => element.id == roles.find((element) => element.Id==role_detail[i].roleApprovalMemberShipId).ContactId);
                    onlymems.push(memobj);
                }
            }
            //roles.find((element) => element.Id==role_detail[i].roleApprovalMemberShipId).RoleName)
            // mems = mems.filter( function(v) {return v.id == onlymems.find((element)=>element.id== v.id)})
            mems = onlymems;
            for(i=0;i<memsunique.length;i++) {
                if (mems.findIndex(e=>e.id==memsunique[i].id)>-1) {
                    onlymemsu.push(memsunique[i]);
                }
            }
            memsunique = onlymemsu;
            if (debug) {GM_log("Clear Mems exit: mems now has length "+mems.length);}
        }
        //       }
        if (mems.length==0) { alert('The report will not be generated as your filters have resulted in no members being selected')}
    }


    function report2() {
        //  console.log(mem[1]);
        //      console.log(mems);

        if (debug) {GM_log("report2 : Collection for Report");}
        if (debug) {GM_log("report2 : Number of Members"+mems.length);}
        if (document.getElementById("gmwelcome").checked) {
            if (document.getElementById("gmhelper").checked) {
                mems = mems.filter( function(v) {return v.Role!="Non Member - Needs disclosure";})
                mems = mems.filter( function(v) {return v.Role!="Holding";})
                if (debug) {GM_log("Report 2: Remove Helpers roles now has length "+roles.length);}
            }

        }
        if (debug) {GM_log("mems now has length "+mems.length);}
        if (document.getElementById("gmteamname").value !=""){
            if (debug) {GM_log("Match team "+document.getElementById("gmteamname").value);}
            mems = mems.filter( function(v) {return v.Team.includes(document.getElementById("gmteamname").value);})
            if (debug) {GM_log("mems now has length "+mems.length);}

        }
        if(document.getElementById("gmexact").checked) { //gmexact
            if (debug) {GM_log("Match team "+document.getElementById("gmteamname").value);}
            mems = mems.filter( function(v) {return v.unitName == document.getElementById("gmunitname").value;})
            if (debug) {GM_log("mems now has length "+mems.length);}
        }
        if (document.getElementById("gmpersonname").value !=""){
            if (debug) {GM_log("Match Names "+document.getElementById("gmpersonname").value);}
            mems = mems.filter( function(v) {return v.fullname.toLowerCase().includes(document.getElementById("gmpersonname").value.toLowerCase());})
            if (debug) {GM_log("mems now has length "+mems.length);}

        }
        if (mems.length==0) { alert('The report will not be generated as your filters have resulted in no members being selected')}
        if (debug) {GM_log("Check for Duplicates ");}
        var seen = {};
        var out = [];
        var len = mems.length;
        var j = 0;
        for(var i = 0; i < len; i++) {
            var item = mems[i];
            if(seen[item.id] !== 1) {
                seen[item.id] = 1;
                out[j++] = item;
            }
        }
      //  if (!document.getElementById("gmwelcome").checked) {
            mems = out;
     //   }
        memsunique = out;
        if (document.getElementById("gmwelcome").checked) {
            if (document.getElementById("gmhelper").checked) {
                mems = mems.filter( function(v) {return v.Role!="Non Member - Needs disclosure";})
                mems = mems.filter( function(v) {return v.Role!="Holding";})
                if (debug) {GM_log("Report 2: Remove Helpers roles now has length "+roles.length);}
            }
            mem_lcount = 0;
            clear_mems();
            report5()
        } else {



            if (mems.length==0) { alert('The report will not be generated as your filters have resulted in no members being selected')}
            $("#gmState")[0].innerText = "Number of Members "+mems.length;
            GM_log("Number of Members "+mems.length);
            GM_log("Number of Unique Members "+memsunique.length);
            for(var loop = 0;loop<memsunique.length; loop++) {
                if (debug) {GM_log("Request Roles for "+loop+" "+memsunique[loop].firstname+" "+memsunique[loop].unitName);}

                GM_xmlhttpRequest({
                    method: "POST",
                    context: loop+1,
                    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/GetMemberRolesAsync",
                    data: '{"contactId": "'+memsunique[loop].id+'"}',
                    headers: {
                        "Content-Type": "application/json",
                        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                        "Accept": "application/json, text/plain, */*"
                    },
                    onload: function(response) {//console.log(response.context);
                        roles_count +=1;
                        $("#gmState")[0].innerText = "Number of Members "+memsunique.length+" reading roles for number "+response.context;
                        //     console.log(mems.length);
                        if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}
                        if (debug) {GM_log("Request Roles received for "+response.context+" "+memsunique[response.context-1].firstname+" "+memsunique[response.context-1].unitName);}
                        var jrole= null;
                        try { jrole=JSON.parse(response.responseText).data} catch {console.log("JSON Error "+response.responseText);}
                        if (jrole!=null) {
                            roles=roles.concat(jrole);
                        }
                        if ((jrole==null)&&(debug)) {
                            GM_log("GetData: We got an error collecting roles for member "+response.context+mems[response.context-1].firstname);
                        }
                        if (debug) {GM_log("Context "+response.context+" mems length "+memsunique.length+" "+response.context+" "+mems[response.context-1].firstname+" "+(response.context-1==memsunique.length-1)+" Roles Count "+(roles_count))}

                        // console.log(JSON.parse(response.responseText));
                  //      GM_log("Context "+response.context+ " "+ response.context-1 + "Unique "+memsunique.length
                        if (roles_count==memsunique.length){console.log("Exit");console.log(roles);console.log(mems);report3();} },
                    onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
                    onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
                });
            };
        }
    }


    function report3() {
        console.log(roles);
        if (debug) {GM_log("Report 3: mems now has length "+mems.length);}
        if (debug) {GM_log("Report 3: roles now has length "+roles.length);}
        if (document.getElementById("gmhelper").checked) {

            roles = roles.filter( function(v) {return !v.RoleName.includes("Non Member - Needs disclosure");})
            roles = roles.filter( function(v) {return !v.RoleName.includes("Holding");})
            if (debug) {GM_log("Report 3: Remove Helpers roles now has length "+roles.length);}
        }
        if (document.getElementById("gmclosed").checked) {
            roles = roles.filter( function(v) {return v.Closed});
            roles = roles.filter( function(v) {return v.EndDate==null});
            roles = roles.filter( function(v) {return !v.IsSecondaryLevel});
            if (debug) {GM_log("Report 3: Remove Closed roles now has length "+roles.length);}
            console.log(roles);

        }
        if (document.getElementById("gmteamname").value !=""){
            roles = roles.filter( function(v) {if( v.Team==null){return false} else {return v.Team.includes(document.getElementById("gmteamname").value);}})
            if (roles.length==0) { alert('There were no roles in Units matching the Unit Name maybe check you have a % at the end to get all the sub Units')}
            if (debug) {GM_log("Report 3: Filter by Team roles now has length "+roles.length);}
        }
        if (document.getElementById("gmteamonly").checked){
            var  filter = document.getElementById("gmunitname").value.replaceAll("%", "*");
            let w = filter.replace(/[.+^${}()|[\]\\]/g, '\\$&'); // regexp escape
            const re = new RegExp(`^${w.replace(/\*/g,'.*').replace(/\?/g,'.')}$`,'i');
            roles = roles.filter( function(v) {return re.test(v.UnitId)});
            if (debug) {GM_log("Report 3: Filter by for Unit only roles now has length "+roles.length);}
        }

        var xml = 0;
        var nullc=0;

        if (roles.length==0) { alert('The report will not be generated as your filters have resulted in no roles being selected')}

        for(var loop=0;loop<roles.length;loop++) {
            if (debug) {GM_log("Report 3: Roles Loop request for "+roles[loop].RoleName+" "+roles[loop].Team+" "+loop);}
            //  console.log(roles[loop].EndDate);
            $("#gmState")[0].innerText = "Checking Role "+loop+" of "+roles.length;
            if ((roles[loop].EndDate==null)||(roles[loop].Status!=null)) {
                //   console.log("OK");
                role_max_count = loop;
                GM_xmlhttpRequest({
                    method: "POST",

                    context: loop+1,
                    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/GenerateSASTokenAsync",
                    data: '{container: "contacts", permissions: "R", file: "'+roles[loop].ContactId+'/membership/'+roles[loop].Id+'/audit/data.json"}',
                    //                  synchronous:    true,
                    headers: {
                        "Content-Type": "application/json",
                        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                        "Accept": "application/json, text/plain, */*"
                    },
                    onload: function(response) {
                        if (debug) {GM_log("Report 3: Roles Blob request for "+response.context+" ")}
                                     if (debug) {GM_log("Report 3: Roles Blob request for "+(response.context-1)+" "+roles[response.context-1].RoleName+" "+roles[response.context-1].Team);}
                        $("#gmState")[0].innerText = "Fetching Role "+response.context+" of "+roles.length;
                        GM_xmlhttpRequest({
                            method: "GET",
                            Accept: "application/json;odata=minimalmetadata",
                            context: response.context+1,
                            url: JSON.parse(response.responseText).token,

                            onload: function(response) { // console.log(response.responseText);
                                // console.log(JSON.parse(response.responseText));
                                // console.log("Total"+(role_detail.length + nullc + xml));
                                //  console.log("Aim"+(roles.length-1));
                                if (debug) {GM_log("Report 3: Roles Loop received for "+(response.context-2)+" "+roles[response.context-2].RoleName+" "+roles[response.context-2].Team);}
                                if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}
                                $("#gmState")[0].innerText = "Checking Role "+response.context;
                                if(!response.responseText.includes("xml")) { role_detail.push(JSON.parse(response.responseText));} else {xml++; console.log("XML for "+response.context)};
                                if (debug) {GM_log("Report 3: role_detail.length : " +role_detail.length+" XML "+xml+" nullc "+nullc + " roles "+roles.length);}
                                if ((role_detail.length+nullc+xml)>=roles.length){console.log("Finish REport 3");console.log(role_detail); mem_lcount = 0;clear_mems();report5()}},
                            onerror:   function(response) { GM_log("Report 3 ON Error");GM_log(JSON.stringify(response))},
                            onabort:  function(response) { GM_log("Report ON Abort");GM_log(JSON.stringify(response))}

                        })

                    },
                    onerror:   function(response) { GM_log("Report 3 Outer ON Error");GM_log(JSON.stringify(response))},
                    onabort:  function(response) { GM_log("Report 3 Outer ON Abort");GM_log(JSON.stringify(response))}
                });
            } else { nullc++}
        }
    }



    function report5() {

        if (document.getElementById("gmlearning").checked) {

            if (debug) {GM_log("Report 5: Get Learning for "+memsunique[mem_lcount].firstname+" "+memsunique[mem_lcount].Team)+" "+mem_lcount;}
            GM_xmlhttpRequest({
                method: "POST",

                url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/GetLmsDetailsAsync",
                data: '{"contactId": "'+memsunique[mem_lcount].id+'"}',
                context:  mem_lcount+1,
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                    "Accept": "application/json, text/plain, */*"
                },
                onload: function(response) {
                    $("#gmState")[0].innerText = "Number of Members "+memsunique.length+" reading learning for number "+mem_lcount;
                    if (debug) {GM_log("Report 5: Learning received for "+(response.context-1));}
                    var l = new Object;
                    l.id = memsunique[ mem_lcount].id;
                    l.learn = [];
                    try { l.learn=JSON.parse(response.responseText)} catch {console.log("JSON Error "+response.responseText);}
                    mem_learning[mem_lcount] = l;
                    // console.log(JSON.parse(response.responseText));
                    if ( mem_lcount==memsunique.length-1){console.log(mem_learning);report6();}else{ mem_lcount++;report5()}},
                onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
                onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
            });

        } else {report6() }
    }

    function xmlToJson(xml) {
        // Create the return object
        var obj = {};

        if (xml.nodeType == 1) {
            // element
            // do attributes
            if (xml.attributes.length > 0) {
                obj["@attributes"] = {};
                for (var j = 0; j < xml.attributes.length; j++) {
                    var attribute = xml.attributes.item(j);
                    obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
                }
            }
        } else if (xml.nodeType == 3) {
            // text
            obj = xml.nodeValue;
        }

        // do children
        // If all text nodes inside, get concatenated text from them.
        var textNodes = [].slice.call(xml.childNodes).filter(function(node) {
            return node.nodeType === 3;
        });
        if (xml.hasChildNodes() && xml.childNodes.length === textNodes.length) {
            obj = [].slice.call(xml.childNodes).reduce(function(text, node) {
                return text + node.nodeValue;
            }, "");
        } else if (xml.hasChildNodes()) {
            for (var i = 0; i < xml.childNodes.length; i++) {
                var item = xml.childNodes.item(i);
                var nodeName = item.nodeName;
                //  if (nodeName=="m:properties") {nodeName = "m_properties"}
                if (nodeName[1]==":") {nodeName = nodeName.replace(":", "_")};
                if (typeof obj[nodeName] == "undefined") {
                    obj[nodeName] = xmlToJson(item);
                } else {
                    if (typeof obj[nodeName].push == "undefined") {
                        var old = obj[nodeName];
                        obj[nodeName] = [];
                        obj[nodeName].push(old);
                    }
                    obj[nodeName].push(xmlToJson(item));
                }
            }
        }
        return obj;
    }

    function report6() {
        var xmlURL ="";
        var xml = 0; var nullc=0;

        if (document.getElementById("gmdbs").checked) {

            for(var loop=0;loop<memsunique.length;loop++) {
                if (debug) {GM_log("Report 6: DBS requested for "+loop+" "+memsunique[loop].firstname);}
                //  console.log(roles[loop].EndDate);
                $("#gmState")[0].innerText = "Checking Disclosure "+loop+" of "+memsunique.length;
                GM_xmlhttpRequest({
                    method: "POST",
                    context: loop+1,
                    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/GenerateSASTokenAsync",
                    data: '{"table": "Disclosures", "permissions": "R", "partitionkey" : "'+memsunique[loop].id+'"}',
                    synchronous:    true,
                    headers: {
                        "Content-Type": "application/json",
                        "Type" : "table",
                        "accept-language" : "en-GB,en;q=0.9",
                        "sec-fetch-site" : "cross-site",
                        "Origin" : "https://membership.scouts.org.uk",
                        "content-encoding" : "gzip",
                        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                        "Accept": "application/json, text/plain, */*"
                    },
                    onload: function(response) {
                        xmlURL = "";
                        if (debug) {GM_log("Report 6: DBS Blob URL Received for "+(response.context-1)+" "+memsunique[response.context-1].firstname);}
                        try { xmlURL =  JSON.parse(response.responseText) } catch {console.log(response.responseText)};
                        if (xmlURL=="") { if (debug) {GM_log("Report 6: DBS Blob URL JSON Error " +response.responseText);} }
                        if (xmlURL!="") {
                            $("#gmState")[0].innerText = "Fetching DBS "+response.context+" of "+memsunique.length;
                            GM_xmlhttpRequest({
                                method: "GET",
                                context: response.context,
                                url: JSON.parse(response.responseText).token,
                                onload: function(response) {
                                    if (debug) {GM_log("Report 6: DBS received for "+(response.context-1)+" "+memsunique[response.context-1].firstname);}
                                    if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}
                                    $("#gmState")[0].innerText = "Checking DBS "+response.context;
                                    if(response.responseText.includes("xml")) { var XmlNode = new DOMParser().parseFromString(response.responseText, 'text/xml');
                                                                               var dbsxml = xmlToJson(XmlNode); dbsxml.id = memsunique[response.context-1].id;
                                                                               dbsxml.firstname = memsunique[response.context-1].firstname;
                                                                               dbs_detail.push(dbsxml);} else {xml++};
                                    if (dbs_detail.length+xml>=memsunique.length){console.log(dbs_detail); report4()}},
                                onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
                                onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}

                            }) } else {xml++; if (dbs_detail.length+xml>=memsunique.length){console.log(dbs_detail); report4()}}

                    }
                });

            }
        } else {report4()}

    }

    function report_headers(table) {
        var expirytext = "Expiry";
        var expirytextdays = "Expiry Days";
        var act = ['managerDisclosureCheck','dataProtectionTrainingComplete','signDeclaration','updateMemberProfile','referenceRequest','safeguardconfidentialEnquiryCheck','managerWelcomeConversation','coreLearning','managerTrusteeCheck'];
        var title = ['Role','Unit','Team','Name','Number','Status','Start Date','Days','Disclosure','GDPR','Declaration','Profile','References','CE Check','Welcome','Learning','Trustee'];
        var learn = ['Creating Inclusion','Data Protection in Scouts','Who We Are and What We Do','Safeguarding','Safety','First Response','Delivering a Great Programme','Being a Trustee in Scouts','Leading Scout Volunteers'];
        var tr = document.createElement('TR');
        table.appendChild(tr);

        for (var i=0; i<title.length;i++){
            var td = document.createElement('TH');

            if (i>7) { td.style.writingMode = "vertical-lr"; td.style.backgroundColor='lightcyan';}
            //  td.style.writingmode = 'vertical-lr';
            td.appendChild(document.createTextNode(title[i]));
            var visible = true;
            if ((i>4)&&(document.getElementById("gmwelcome").checked)){visible=false};
            if ((i==4)&&(!document.getElementById("gmnumber").checked)){visible=false};
            if (visible) { tr.appendChild(td); }
        }

        if (document.getElementById("gmlearning").checked) {
            for ( i=0; i<learn.length;i++){
                td = document.createElement('TH');
                td.style.writingMode = "vertical-lr";
                td.style.backgroundColor = 'LightGray';
                td.appendChild(document.createTextNode(learn[i]));
                tr.appendChild(td);
                if (((i==3)||(i==4)||(i==5))&&(document.getElementById("gmdates").checked)) {
                    td = document.createElement('TH');
                    td.style.writingMode = "vertical-lr";
                    td.style.backgroundColor = 'LightGray';
                    td.appendChild(document.createTextNode(learn[i]+" "+expirytext));
                    tr.appendChild(td);
                }
                if (((i==3)||(i==4)||(i==5))&&(document.getElementById("gmdays").checked)) {
                    td = document.createElement('TH');
                    td.style.writingMode = "vertical-lr";
                    td.style.backgroundColor = 'LightGray';
                    td.appendChild(document.createTextNode(learn[i]+" "+expirytextdays));
                    tr.appendChild(td);
                }
            }
        }
        if (document.getElementById("gmdbs").checked) {
            td = document.createElement('TH');
            td.style.backgroundColor = 'LightGray';
            td.appendChild(document.createTextNode("DBS Expiry"));

            tr.appendChild(td);
            td = document.createElement('TH');
            td.style.writingMode = "vertical-lr";
            td.style.backgroundColor = 'LightGray';
            td.appendChild(document.createTextNode("DBS Days Remaining"));

            tr.appendChild(td);
            if (document.getElementById("gmdbsip").checked){
                td = document.createElement('TH');
                td.style.backgroundColor = 'LightGray';
                td.appendChild(document.createTextNode("DBS Status"));

                tr.appendChild(td);
            }
        }
    }

    function tabulator_id_url() {
    }

    var headerMenu = function(){
        var menu = [];
        var columns = this.getColumns();

        for(let column of columns){

            //create checkbox element using font awesome icons
            let icon = document.createElement("span");
            icon.classList.add("fas");
            icon.classList.add(column.isVisible() ? "fa-check-square" : "fa-square");

            //build label
            let label = document.createElement("span");
            let title = document.createElement("span");

            title.textContent = " " + column.getDefinition().title;

            label.appendChild(icon);
            label.appendChild(title);

            //create menu item
            menu.push({
                label:label,
                action:function(e){
                    //prevent menu closing
                    e.stopPropagation();

                    //toggle current column visibility
                    column.toggle();

                    //change menu item icon
                    if(column.isVisible()){
                        icon.classList.remove("fa-square");
                        icon.classList.add("fa-check-square");
                    }else{
                        icon.classList.remove("fa-check-square");
                        icon.classList.add("fa-square");
                    }
                }
            });
        }

        return menu;
    };

    function report_headers_tabulator() {
        var expirytext = "Expiry";
        var expirytextdays = "Expiry Days";
        var act = ['managerDisclosureCheck','dataProtectionTrainingComplete','signDeclaration','updateMemberProfile','referenceRequest','safeguardconfidentialEnquiryCheck','managerWelcomeConversation','coreLearning','managerTrusteeCheck'];
        var title = ['Name','Role','Unit','Team','Number','Status','Start Date','Days','Disclosure','GDPR','Declaration','Profile','References','CE Check','Welcome','Growing Roots Learning','Trustee Check'];
        var learn = ['Creating Inclusion','Data Protection in Scouts','Who We Are and What We Do','Safeguarding','Safety','First Response','Delivering a Great Programme','Being a Trustee in Scouts','Leading Scout Volunteers'];
        var titlefieldnames = ['name','role','unit','team','number','status','start','days','disclosure','gdpr','declaration','profile','references','ce','welcome','learning','trustee'];
        var learnfieldnames = ['inclusion','datap','who','safeguarding','safety','firstresponse','programme','trustee2','leading'];
        var dbs = ['DBS','DBS Expiry','DBS Days','DBS Status'];
        var dbsfieldnames = ['dbs','dbs_expiry','dbs_expirydays','dbsstatus'];
        var headers = [];

        var color_formatter = function testFormatter(cell, formatterParams) {    var days = cell.getValue();
                                                                             if(document.getElementById("gmcolor").checked) {
                                                                                 var fv =  cell.getData()[cell.getField().substr(0,cell.getField().indexOf("_"))];
                                                                                 if ("✅❌✔⚡".includes(fv)) {
                                                                                     for(cloop=0;cloop<4;cloop++) {
                                                                                         if ((days<=daylimits[cloop])&&(days>daylimits[cloop+1])) {
                                                                                             cell.getElement().style.backgroundColor = colors[cloop];
                                                                                             cell.getElement().style.color = colorsT[cloop];
                                                                                         }}}
                                                                             }
                                                                             return days}
        var color_formatter_date = function testFormatter(cell, formatterParams) {
            if(document.getElementById("gmcolor").checked) {
                var fv =  cell.getData()[cell.getField().substr(0,cell.getField().indexOf("_"))];
                if ("✅❌✔⚡".includes(fv)) {
                    var days  = cell.getData()[cell.getField()+"days"];
                    for(cloop=0;cloop<4;cloop++) {
                        if ((days<=daylimits[cloop])&&(days>daylimits[cloop+1])) {
                            cell.getElement().style.backgroundColor = colors[cloop];
                            cell.getElement().style.color = colorsT[cloop];
                        }}}}
            return cell.getValue()}
        //   for (var i=0; i<title.length;i++){
        var group = new Object;
        var visible = true;
         group.columns = [];
         group.title = "";
         group.frozen = true;

                var head = new Object;
                head.title = title[0];
                head.field = titlefieldnames[0];
                head.headerFilter = "input";

                head.formatterParams = new Object;
                head.formatter="link"; head.formatterParams.urlField = "memberURL";head.formatterParams.target = "_blank"
                head.headerMenu = headerMenu;
                group.columns.push(head);



          headers.push(group);
        group = new Object;
         group.columns = [];
         group.title = "";
        for (var i=1; i<8;i++){

             visible = true;
            if (((i>4))&&(document.getElementById("gmwelcome").checked)){visible=false};
            //    if ((i==4)&&(!document.getElementById("gmnumber").checked)){visible=false};
            if (visible) {
                head = new Object;
                head.title = title[i];
                head.field = titlefieldnames[i];
                head.headerFilter = "input";
                if (i==6) {    head.sorter = "date";}
                if (i==7) {    head.sorter = "number"; head.headerFilterPlaceholder = "less than" ; head.headerFilterFunc = "<";  head.headerFilter = "number";}
                if (i==0) { head.frozen = true;}
                head.formatterParams = new Object;
                if (i==0){head.formatter="link"; head.formatterParams.urlField = "memberURL";head.formatterParams.target = "_blank"}

                if (i>7) { head.headerVertical = true;}
                head.headerMenu = headerMenu;
                group.columns.push(head);
            }

        }
          headers.push(group);
        group = new Object;
        if (!document.getElementById("gmwelcome").checked) {
            group.columns = [];
            group.title = "Welcome Activities";
            //    group.cssClass = "blue-background";
            for (i=8; i<title.length;i++){
                visible = true;
                if ((i>7)&&(document.getElementById("gmwelcome").checked)){visible=false};
                if (visible) {
                    head = new Object;
                    head.title = title[i];
                    head.field = titlefieldnames[i];
                    head.cssClass = "blue-background";

                    head.formatterParams = new Object;
                    if (i>7) { head.headerVertical = true;}

                    head.headerFilter = "list";
                    head.headerFilterParams = {    valuesLookup:"active" }
                    group.columns.push(head);
                }
            }
            headers.push(group);
        }
        group = new Object;
        if (document.getElementById("gmlearning").checked) {
            group.columns = [];
            group.title = "Learning";
            //      group.cssClass = "yellow-background";
            for ( i=0; i<learn.length;i++){
                head = new Object;
                head.title = learn[i];
                head.field = learnfieldnames[i];
                head.headerVertical = true;
                head.cssClass = "yellow-background";
                head.headerFilter = "list";
                head.headerFilterParams = {    valuesLookup:"active" }
                group.columns.push(head);
                if (((i==3)||(i==4)||(i==5))) {
                    head = new Object;
                    head.title = learn[i]+" "+expirytext;
                    head.field = learnfieldnames[i]+"_expiry";
                    head.sorter = "date";
                    head.headerVertical = true;
                    head.formatter = color_formatter_date;
                    head.cssClass = "yellow-background";
                    head.headerFilter = "input";
                    group.columns.push(head);
                }
                if (((i==3)||(i==4)||(i==5))) {
                    head = new Object;
                    head.title = learn[i]+" "+expirytextdays;
                    head.field = learnfieldnames[i]+"_expirydays";
                    head.sorter = "number";
                    head.headerVertical = true;
                    head.formatter = color_formatter;
                    head.cssClass = "yellow-background";
                    head.headerFilterPlaceholder = "less than",
                        head.headerFilterFunc = "<"
                    head.headerFilter = "number";
                    group.columns.push(head);
                }
            }
            headers.push(group);
        }
        group = new Object;
        if (document.getElementById("gmdbs").checked) {
            group.columns = [];
            group.title = "DBS";
            // group.cssClass = "orange-background";
            for ( i=0; i<dbs.length-1;i++){
                head = new Object;
                head.title = dbs[i];
                head.field = dbsfieldnames[i];
                head.cssClass = "orange-background";
                if (i==0) {
                    head.headerFilter = "list";
                    head.headerFilterParams = {    valuesLookup:"active" }
                } else {              head.headerFilter = "input"; }
                if (i==0) { head.headerVertical = true;}

                if (i==2) {     head.sorter = "number";   head.formatter = color_formatter;   head.headerFilterPlaceholder = "less than" ; head.headerFilterFunc = "<";  head.headerFilter = "number";}
                if (i==1) {     head.sorter = "date";   head.formatter = color_formatter_date;}
                group.columns.push(head);
            }
            //   if (document.getElementById("gmdbsip").checked){
            head = new Object;
            head.title = dbs[3];
            head.field = dbsfieldnames[3];
            head.cssClass = "orange-background";
            head.headerFilter = "input";
            group.columns.push(head);
            headers.push(group);
            //   }
        }
        return headers;
    }


    function report4() {
        if (debug) {GM_log("Report 4 : Data Collected - generate report ");}
        if (debug) {GM_log("Report 4 : Roles Detail "+role_detail.length);}
        if (debug) {GM_log("Report 4 : Mems  "+mems.length);}
        if (debug) {GM_log("Report 4 : Roles  "+roles.length);}
        if (debug) {GM_log("Report 4 : Learning  "+mem_learning.length);}
        if (debug) {GM_log("Report 4 : DBS  "+ dbs_detail.length);}
        var expirytext = "Expiry";
        var expirytextdays = "Expiry Days";
        var act = ['managerDisclosureCheck','dataProtectionTrainingComplete','signDeclaration','updateMemberProfile','referenceRequest','safeguardconfidentialEnquiryCheck','managerWelcomeConversation','coreLearning','managerTrusteeCheck'];
        var title = ['Role','Unit','Team','Name','Status','Start Date','Days','Disclosure','GDPR','Declaration','Profile','References','CE Check','Welcome','Learning','Trustee'];
        var learn = ['Creating Inclusion','Data Protection in Scouts','Who We Are and What We Do','Safeguarding','Safety','First Response','Delivering a Great Programme','Being a Trustee in Scouts','Leading Scout Volunteers'];

        $("#gmState")[0].innerText = "Data Collection Completed";
        var myTableDiv = document.getElementById("gmTable");
        var table = document.createElement('TABLE');
        table.border = '1';
        table.id = "GMourreport";

        var tableBody = document.createElement('THEAD');
        table.appendChild(tableBody);
        //    var tr = document.createElement('TR');
        //   tableBody.appendChild(tr);
        report_headers(tableBody);
        tableBody = document.createElement('TBODY');
        table.appendChild(tableBody);
        var tablines = [];
        var   tr = document.createElement('TR');
        if (!(document.getElementById("gmwelcome").checked)) {
            for (i = 0; i < role_detail.length; i++) {
                var displine=true;

                if (document.getElementById("gmmissing").checked) {
                    var any_missing = false;
                    for (var j = 0; j < act.length; j++) {
                        if (role_detail[i].actions.find((element)=>element.typeid==act[j])) {
                            var value = role_detail[i].actions.find((element)=>element.typeid==act[j]).status;
                            if ((value!="Satisfactory")&&(value!="Completed")&&(value!="Held - Satisfactory")){ any_missing=true;}
                        }
                    }
                    if (any_missing==false) {displine=false}
                }
                if (displine) {
                    //      tr = document.createElement('TR');
                    //      tableBody.appendChild(tr);

                    output_report_line(tableBody,i,mems.findIndex( (element) => element.id == roles.find( (element) => element.Id == role_detail[i].roleApprovalMemberShipId).ContactId));
                    tablines.push(report_line_tabulator(i,mems.findIndex( (element) => element.id == roles.find( (element) => element.Id == role_detail[i].roleApprovalMemberShipId).ContactId)));
                }
            }
        }
        else
        {
            for (var i = 0; i < mems.length; i++) {
                if (mems[i].Role!=null) {
                    output_report_line(tableBody,-1,i);
                    tablines.push( report_line_tabulator(-1,i))
                }
            }
        }
        table.style.fontSize = "10pt";
        document.getElementById("gmTable").innerHTML="";
        //  myTableDiv.appendChild(table);

        //   console.log(hasStyleRule("tabulator-col.tabulator-sortable.tabulator-col-sorter-element.orange-background"));
        if (!added_css) {
            added_css=true;
            GM_addStyle ('.fa-check-square:before { content: "✅";   }' );
            GM_addStyle ('.fa-square:before { content: "🟩";   }' );
            GM_addStyle('.tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-sorter-element.blue-background {background : #ADD8E6 }');
            GM_addStyle('.tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-sorter-element.yellow-background {background : #FFFFE0 }');
            GM_addStyle('.tabulator-col.tabulator-sortable.tabulator-col-sorter-element.orange-background {background : #FFDAB9 }');
            GM_addStyle('th.blue-background {    writing-mode: vertical-lr;    background-color: lightcyan; }');
            GM_addStyle('th.yellow-background {    writing-mode: vertical-lr;    background-color: lightgoldenrodyellow; }');
            GM_addStyle('th.orange-background {    writing-mode: vertical-lr;    background-color: salmon; }');
            GM_addStyle('table.tabulator-print-table, tr, th, td {    border: solid;}');
        }
        tabletabulator = new Tabulator("#gmTabulator", {data: tablines,  printAsHtml:true,  columnHeaderVertAlign :"bottom", columns: report_headers_tabulator(),    movableColumns: true,   dependencies:{  DateTime:luxon.DateTime, },
                                                        downloadEncoder:function(fileContents, mimeType){
                                                            var b = new Blob([fileContents], {type:mimeType});    var url = URL.createObjectURL(b);
                                                            url = window.URL.createObjectURL(b);
                                                            var filename = 'myBlobFile.xlsx';

                                                            var a = document.createElement('a');
                                                            a.style = 'display: none';
                                                            a.href = url;
                                                            a.download = filename;

                                                            // IE 11
                                                            if (window.navigator.msSaveBlob !== undefined) {
                                                                window.navigator.msSaveBlob(b, filename);
                                                                return;
                                                            }
                                                            document.body.appendChild(a);
                                                            var e = new MouseEvent ("click");
                                                            a.dispatchEvent (e);
                                                            window.URL.revokeObjectURL(url);
                                                            document.body.removeChild(a);
                                                            // });
                                                        }
                                                       }); // tabulator
        tabletabulator.on("tableBuilt", function(){
            if (!document.getElementById("gmnumber").checked) { tabletabulator.hideColumn("number") }
            if (!document.getElementById("gmdates").checked) { tabletabulator.hideColumn("safeguarding_expiry");tabletabulator.hideColumn("firstresponse_expiry");tabletabulator.hideColumn("safety_expiry") }
            if (!document.getElementById("gmdays").checked) { tabletabulator.hideColumn("safeguarding_expirydays");tabletabulator.hideColumn("firstresponse_expirydays");tabletabulator.hideColumn("safety_expirydays") }
            if (!document.getElementById("gmdbsip").checked) { tabletabulator.hideColumn("dbsstatus") }
        });
    }

    var trustee_roles = ['Lead Volunteer','Trustee','Treasurer','Youth Lead','Chair'];

    function is_trustee(rolename) {
        if (trustee_roles.findIndex(e=>e == rolename)>-1) {return true}
        if (rolename.includes(trustee_roles[0])) {return true}
        return false;
    }
    var no_dbs = ['President','Vice President','Holding','Retired Member','Nations Staff Member','Council Member','Network Member','Locally Employed Staff','National Staff member','National Staff team leader','Trustee Board Sub-Team Member'];

    function needs_dbs(rolename) {
        if (no_dbs.findIndex(e=>e == rolename)>-1) {return false} else {return true};
    }

    var no_training = ['President','Vice President','Holding','Retired Member','Non Member - Needs disclosure','Council Member','Network Member','Designated Carers'];

    function needs_training(rolename) {
        if (no_training.findIndex(e=>e == rolename)>-1) {return false} else {return true};
    }

    function is_section(unit_type) {
        if ((unit_type=='Group Section')||(unit_type=='District Section')||(unit_type=='County Section')) {return true} else {return false}
    }

    var mainteams = ['Leadership Team','Volunteering Development Team','Support Team','Programme Team','14-24 Team'];

    function is_mainteam(team_name) {
        if (mainteams.findIndex((element)=>element==team_name>-1)) {return true} else {return false}
    }

    function is_teamleader(rolename,unit_type,ParentTeamId,team_name) {
        //  if ((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName.includes('Lead Volunteer'))||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Team Leader')&&((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType!='Group Section')&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType!='District Section'))) {
        if (rolename.includes('Lead Volunteer')) { return true;}
        if (rolename.includes('Youth Lead')) { return true;}
        if (ParentTeamId!=null) {return false} // Is a Sub team
        if (!is_mainteam(team_name)) {return false}
        if ((rolename=='Team Leader')&&!is_section(unit_type)) {return true}
        return false;
    }
    function is_firstresponse(unit_type,role_name,team_name) {
        if (is_section(unit_type)) {return true}
        if (role_name=="Group Lead Volunteer") {return true}
        if ((team_name=='14-24 Team')&&(role_name=="Team Leader")) {return true}
        return false;
    }



    // *****************************************************************
    /// This is used for the refresh but no new data
    /// *****************************************************************
    function output_report_line(table,role_index,mem_index) {
        var act = ['managerDisclosureCheck','dataProtectionTrainingComplete','signDeclaration','updateMemberProfile','referenceRequest','safeguardconfidentialEnquiryCheck','managerWelcomeConversation','coreLearning','managerTrusteeCheck'];
        var title = ['Role','Unit','Team','Name','Status','Start Date','Days','Disclosure','GDPR','Declaration','Profile','References','CE Check','Welcome','Learning','Trustee'];
        var learn = ['Creating Inclusion','Data Protection in Scouts','Who We Are and What We Do','Safeguarding','Safety','First Response','Delivering a Great Programme','Being a Trustee in Scouts','Leading Scout Volunteers'];

        var tr = document.createElement('TR');
        table.appendChild(tr);
        // Name
        if (role_index>=0) {
            var td = document.createElement('TD');
            td.appendChild(document.createTextNode(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName));
            tr.appendChild(td);
            td = document.createElement('TD');
            td.appendChild(document.createTextNode(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitId));
            tr.appendChild(td);
            td = document.createElement('TD');
            td.appendChild(document.createTextNode(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).Team));
            tr.appendChild(td);
        } else
        {
            td = document.createElement('TD');
            td.appendChild(document.createTextNode(mems[mem_index].Role));
            tr.appendChild(td);
            td = document.createElement('TD');
            td.appendChild(document.createTextNode(mems[mem_index].unitName));
            tr.appendChild(td);
            td = document.createElement('TD');
            td.appendChild(document.createTextNode(mems[mem_index].Team));
            tr.appendChild(td);
        }
        td = document.createElement('TD');
        var fullname = mems[mem_index].fullname;
        //  var nameid = mems.find((element) => element.id == roles.find((element) => element.Id==role_detail[i].roleApprovalMemberShipId).ContactId).id;
        var nameid = mems[mem_index].id;
        var memhtml = '<a href="https://membership.scouts.org.uk/#/membersearch/'+nameid+'/viewmember" target="_blank">'+fullname+'</a>';
        tr.appendChild(td);
        td.innerHTML=(memhtml);
        if (document.getElementById("gmnumber").checked) {
            td = document.createElement('TD');
            td.appendChild(document.createTextNode(mems[mem_index].membershipnumber));
            tr.appendChild(td);
        };
        if (role_index>=0) {

            // Role
            td = document.createElement('TD');
            td.appendChild(document.createTextNode(role_detail[role_index].rolestatus));
            tr.appendChild(td);
            td = document.createElement('TD');
            var sdate = new Date(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).StartDate);
            td.appendChild(document.createTextNode(sdate.toLocaleDateString('en-GB')));
            var days  = Math.round((new Date() - sdate) / (1000 * 60 * 60 * 24));
            tr.appendChild(td);

            td = document.createElement('TD');
            td.appendChild(document.createTextNode(days));
            tr.appendChild(td);

            for (var j = 0; j < act.length; j++) {
                td = document.createElement('TD');
                //   td.width = '75';
                if (role_detail[role_index].actions.find((element)=>element.typeid==act[j])) {

                    var state= 0x274C;
                    var value = role_detail[role_index].actions.find((element)=>element.typeid==act[j]).status;
                    if ((value=="Satisfactory")||(value=="Completed")||(value=="Held - Satisfactory")){state= 0x2705;}

                    //  td.appendChild(document.createTextNode(role_detail[i].actions.find((element)=>element.typeid==act[j]).status+String.fromCodePoint(state)));
                    td.appendChild(document.createTextNode(String.fromCodePoint(state)));

                    //   td.appendChild(document.createTextNode(String.fromCodePoint(state)));
                } else {
                    // Deal with bug in API that means this can be missing.....
                    if(j==3) {
                        state= 0x274C;
                        td.appendChild(document.createTextNode(String.fromCodePoint(state)));
                    } else {
                        td.appendChild(document.createTextNode(" "))
                    }
                }
                td.style.backgroundColor='lightcyan';
                tr.appendChild(td);
            }
        } else {

        }
        // Learning

        if (document.getElementById("gmlearning").checked) {
            for ( j = 0; j < learn.length; j++) {
                td = document.createElement('TD');
                state= 0x274C;
                var lr = mem_learning.find((element) => element.id == mems[mem_index].id);
                if (lr.learn.find((object)=>object.title==learn[j])){
                    state= 0x2705;
                }
                // Check Expiry x26A1
                if (lr.learn.find((object)=>object.title==learn[j])){
                    if (lr.learn.find( (object) => object.title == learn[j]).expiryDate !=null) {
                        sdate = new Date(lr.learn.find((object)=>object.title==learn[j]).expiryDate);
                        days  = Math.round((sdate - new Date()) / (1000 * 60 * 60 * 24));
                        if (days<=90) {state = 0x26A1;
                                      }
                    } else {days=0;sdate = new Date("1977-01-01T00:00:01Z") ;}
                } else {days=0;sdate = new Date("1977-01-01T00:00:01Z") ;}
                if (role_index>=0) {
                    var role_find = {};
                    role_find = roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId);
                    if (j==7) {
                        // Trustee
                        //       if ((!roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName.includes('Lead Volunteer'))&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName!='Trustee')) {
                        if (!is_trustee(role_find.RoleName)) {
                            if(state==0x2705) {state=0x2714;}
                            else {
                                state = 0x0020;
                            }
                        }
                    }
                    if (j==8) {
                        // Team Lead is_teamleader(rolename,unit_type,ParentTeamId,team_name)
                        //   if ((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName.includes('Lead Volunteer'))||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Team Leader')&&((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType!='Group Section')&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType!='District Section'))) {
                        if (is_teamleader(role_find.RoleName,role_find.UnitType,role_find.ParentTeamId,role_find.Team)) {
                        } else {if(state==0x2705) {
                            state=0x2714;}
                                else {
                                    state = 0x0020;
                                }}
                    }
                    /*  if (j==8) {
                        // Team Lead of Subteam means no training... Parent being null means this isn't a subteam
                        if ((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).ParentTeamId==null)) {

                        } else {if((state==0x2705)||(state == 0x2714)) {
                            state=0x2714;}
                                else {
                                    state = 0x0020;
                                }}
                    }*/
                    if (j==6) {
                        // programme - only section roles
                        //    if ((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType!='Group Section')&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType!='District Section')) {
                        if (!is_section(role_find.UnitType) )
                            if(state==0x2705) {
                                state=0x2714;}
                            else {
                                state = 0x0020;
                            }
                    }

                    if (j==5) {
                        // First Response
                        //    if ((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType=='Group Section')||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType=='District Section')||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Group Lead Volunteer')||((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Team Leader'))&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).Team=='14-24 Team')) {
                        if (is_firstresponse(role_find.UnitType,role_find.RoleName,role_find.Team)) {
                        } else { if(state==0x2705) {
                            state=0x2714;}
                                else {
                                    state = 0x0020;
                                }
                               }
                    }
                    // Check for those not needing training
                    //   var rn = roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName;
                    //   if ((rn=='Non Member - Needs disclosure')||(rn=='President')||(rn=='Vice President')||(rn=='Holding')||(rn=='Retired Member')) {
                    if (!needs_training(role_find.RoleName)) {
                        if ((state == 0x26A1)||(state == 0x2705)||(state == 0x2714)) {
                            state=0x2714;}
                        else {
                            state = 0x0020;
                        }
                    }

                } else {

                    if ((j==7)&&(mems[mem_index].Role!=null)) {
                        // Trustee
                        if (!is_trustee(mems[mem_index].Role)) {
                            //  if ((!mems[mem_index].Role.includes('Lead Volunteer'))&&(mems[mem_index].RoleName!='Trustee')) {

                            if(state==0x2705) {
                                state=0x2714;}
                            else {
                                state = 0x0020;
                            }
                        }
                    }
                    if ((j==8)&&(mems[mem_index].Role!=null)) {
                        // Team Lead
                        if (is_teamleader(mems[mem_index].Role,mems[mem_index].unittype,null,mems[mem_index].Team)) {

                            //         if ((mems[mem_index].Role.includes('Lead Volunteer'))||(mems[mem_index].Role=='Team Leader')&&((mems[mem_index].unittype!='Group Section')&&(mems[mem_index].unittype!='District Section'))) {

                        } else {if(state==0x2705) {
                            state=0x2714;}
                                else {
                                    state = 0x0020;
                                }}
                    }
                    /*       if (j==8) {
                        var mainteams = ['Leadership Team','Volunteering Development Team','Support Team','Programme Team','14-24 Team'];
                        // Team Lead for Sub teams
                        if (mainteams.find((element)=>element==mems[mem_index].Team!=undefined)
                           ) {

                        } else {if((state==0x2705)||(state == 0x2714)) {
                            state=0x2714;}
                                else {
                                    state = 0x0020;
                                }}
                    }*/
                    if (j==6) {
                        // programme - only section roles
                        if (!is_section(mems[mem_index].unittype) ){
                            //       if ((mems[mem_index].unittype!='Group Section')&&(mems[mem_index].unittype!='District Section')) {
                            if(state==0x2705) {
                                state=0x2714;}
                            else {
                                state = 0x0020;
                            }
                        }
                    }
                    if ((j==5)&&(mems[mem_index].Role!=null)) {
                        // First Response
                        //     if ((mems[mem_index].unittype=='Group Section')||(mems[mem_index].unittype=='District Section')||(mems[mem_index].Role=='Group Lead Volunteer')||((mems[mem_index].Role=='Team Leader'))&&(mems[mem_index].Team=='14-24 Team')) {
                        if (is_firstresponse(mems[mem_index].unittype,mems[mem_index].Role,mems[mem_index].Team)) {

                        } else { if(state==0x2705) {
                            state=0x2714;}
                                else {
                                    state = 0x0020;
                                } }
                    }
                    // Check for those not needing training
                    var rn = mems[mem_index].Role;
                    if (rn!=null) {
                        if (!needs_training(mems[mem_index].Role)) {
                            if ((state == 0x26A1)||(state == 0x2705)||(state == 0x2714)) {
                                state=0x2714;}
                            else {
                                state = 0x0020;
                            }
                        }
                    }

                }
                td.appendChild(document.createTextNode(String.fromCodePoint(state)));
                td.style.backgroundColor = 'LightGray';
                tr.appendChild(td);
                var blankdate =  new Date("1977-01-01T00:00:01Z");
                if ((document.getElementById("gmdates").checked)&&((j==3)||(j==4)||(j==5))) {
                    td = document.createElement('TD');
                    if (lr.learn.find( (object) => object.title == learn[j])) {
                        if (sdate.getTime()!= blankdate.getTime()) {
                            td.style.backgroundColor = 'LightGray';
                            if (( state != 0x0020)&&(document.getElementById("gmcolor").checked)){
                                for(var cloop=0;cloop<4;cloop++) {
                                    if ((days<=daylimits[cloop])&&(days>daylimits[cloop+1])) {
                                        td.style.backgroundColor = colors[cloop];
                                        td.style.textColor = colorsT[cloop];
                                    }}}
                            td.appendChild(document.createTextNode(sdate.toLocaleDateString('en-GB')));
                        }
                    } else {
                        if (( state != 0x0020)&&(document.getElementById("gmcolor").checked)){
                            for(cloop=0;cloop<4;cloop++) {
                                if ((days<=daylimits[cloop])&&(days>daylimits[cloop+1])) {
                                    td.style.backgroundColor = colors[cloop];
                                    td.style.textColor = colorsT[cloop];
                                }}}
                    }

                    tr.appendChild(td);
                }
                if (((j==3)||(j==4)||(j==5))&&(document.getElementById("gmdays").checked)) {
                    td = document.createElement('TD');
                    td.style.backgroundColor = 'LightGray';
                    if (!document.getElementById("gmwelcome").checked) {
                        if ((j!=3)||(((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType=='Group Section')||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType=='District Section')||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Group Lead Volunteer')||((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Team Leader'))&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).Team=='14-24 Team'))&&j==3)) {

                            if (sdate!= new Date("1000-01-01T00:00:01Z")) {
                                td.appendChild(document.createTextNode(days));

                            }
                        }
                    } else {  if (sdate!= new Date("1000-01-01T00:00:01Z")) {
                        td.appendChild(document.createTextNode(days));

                    }}
                    if (( state != 0x0020)&&(document.getElementById("gmcolor").checked)){
                        for(cloop=0;cloop<4;cloop++) {
                            if ((days<=daylimits[cloop])&&(days>daylimits[cloop+1])) {
                                td.style.backgroundColor = colors[cloop];
                                td.style.textColor = colorsT[cloop];
                            }}}
                    tr.appendChild(td);
                }

            }
        }
        if (document.getElementById("gmdbs").checked) {
            if (role_index>=0) {
                rn = roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName;
            } else { rn=mems[mem_index].Role;}
            //    if ((rn=='President')||(rn=='Vice President')||(rn=='Holding')||(rn=='Retired Member')) {
            if (!needs_dbs(rn)) {
                // No DBS Needed
                td = document.createElement('TD');
                tr.appendChild(td);
                td = document.createElement('TD');
                tr.appendChild(td);

            } else {
                var mindex = dbs_detail.findIndex((element)=> element.id == mems[mem_index].id)
                if (mindex>=0) {
                    var time = new Date("1000-01-01T00:00:01Z") ;
                    var timel = new Date("1000-01-01T00:00:01Z") ;
                    var dbs_status = "";
                    if (dbs_detail[mindex].feed.entry != undefined) {
                        if ( dbs_detail[mindex].feed.entry.length != undefined ) {
                            for(var dbsloop=0;dbsloop<dbs_detail[mindex].feed.entry.length;dbsloop++) {
                                sdate = new Date(dbs_detail[mindex].feed.entry[dbsloop].content.m_properties.d_ExpiryDate);
                                if (sdate>time) { time = sdate;}
                                sdate = new Date(dbs_detail[mindex].feed.entry[dbsloop].content.m_properties.d_IssuedDate);
                                if (sdate>timel) { timel = sdate;dbs_status=dbs_detail[mindex].feed.entry[dbsloop].content.m_properties.d_Status; if (dbs_status!="Disclosure issued"){dbs_status+=" "+timel.toLocaleDateString('en-GB')}}
                            }
                        }
                        if (dbs_detail[mindex].feed.entry.length == undefined) {
                            // Single DBS
                            sdate = new Date(dbs_detail[mindex].feed.entry.content.m_properties.d_ExpiryDate);
                            if (sdate>time) { time = sdate;}
                            sdate = new Date(dbs_detail[mindex].feed.entry.content.m_properties.d_DateModified);
                            dbs_status=dbs_detail[mindex].feed.entry.content.m_properties.d_Status;
                            if (dbs_status!="Disclosure issued"){dbs_status+=" "+sdate.toLocaleDateString('en-GB')}
                        }
                    }
                    td = document.createElement('TD');
                    var dbsdatedisp = time.toLocaleDateString('en-GB');
                    if (time<new Date()) {dbsdatedisp=String.fromCodePoint(0x274C)};
                    var expdays  = Math.round((time-new Date()) / (1000 * 60 * 60 * 24));
                    if ((expdays<=90)&&(expdays>0)) {dbsdatedisp+=String.fromCodePoint(0x26A1)}
                    if ((document.getElementById("gmcolor").checked)){
                        for(cloop=0;cloop<4;cloop++) {
                            if ((expdays<=daylimits[cloop])&&(expdays>daylimits[cloop+1])) {
                                td.style.backgroundColor = colors[cloop];
                                td.style.textColor = colorsT[cloop];
                            }}}
                    td.appendChild(document.createTextNode(dbsdatedisp));

                    tr.appendChild(td);
                    td = document.createElement('TD');
                    if ((document.getElementById("gmcolor").checked)){
                        for(cloop=0;cloop<4;cloop++) {
                            if ((expdays<=daylimits[cloop])&&(expdays>daylimits[cloop+1])) {
                                td.style.backgroundColor = colors[cloop];
                                td.style.textColor = colorsT[cloop];
                            }}}
                    if(expdays>0) {
                        td.appendChild(document.createTextNode(expdays));
                    }
                    tr.appendChild(td);
                    if (document.getElementById("gmdbsip").checked){
                        td = document.createElement('TD');
                        td.appendChild(document.createTextNode(dbs_status));
                        tr.appendChild(td);
                    }
                } else {td = document.createElement('TD');td.appendChild(document.createTextNode(String.fromCodePoint(0x274C))); tr.appendChild(td);td = document.createElement('TD');tr.appendChild(td);
                        if (document.getElementById("gmdbsip").checked) { td = document.createElement('TD');tr.appendChild(td); }
                       }
            }
        }
    }


    function report_line_tabulator(role_index,mem_index) {
        var act = ['managerDisclosureCheck','dataProtectionTrainingComplete','signDeclaration','updateMemberProfile','referenceRequest','safeguardconfidentialEnquiryCheck','managerWelcomeConversation','coreLearning','managerTrusteeCheck'];
        var title = ['Role','Unit','Team','Name','Number','Status','Start Date','Days','Disclosure','GDPR','Declaration','Profile','References','CE Check','Welcome','Learning','Trustee'];
        var learn = ['Creating Inclusion','Data Protection in Scouts','Who We Are and What We Do','Safeguarding','Safety','First Response','Delivering a Great Programme','Being a Trustee in Scouts','Leading Scout Volunteers'];
        var titlefieldnames = ['role','unit','team','name','number','status','start','days','disclosure','gdpr','declaration','profile','references','ce','welcome','learning','trustee'];
        var learnfieldnames = ['inclusion','datap','who','safeguarding','safety','firstresponse','programme','trustee2','leading'];
        var dbs = ['DBS Expiry','DBS Days','DBS Status'];
        var dbsfieldnames = ['dbs','dbs_expiry','dbs_expirydays','dbsstatus'];
        var line = new Object;

        // Name
        if (role_index>=0) {
            line.role = roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName;
            line.unit = roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitId;
            line.team = roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).Team;
            line.memberURL  = 'https://membership.scouts.org.uk/#/membersearch/'+roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).ContactId+"/viewmember";
        } else
        {
            line.role = mems[mem_index].Role;
            line.unit = mems[mem_index].unitName;
            line.team = mems[mem_index].Team;
            line.memberUrl = 'https://membership.scouts.org.uk/#/membersearch/'+mems[mem_index].id+"/viewmember";
        }
        line.name = mems[mem_index].fullname;

        //  var nameid = mems.find((element) => element.id == roles.find((element) => element.Id==role_detail[i].roleApprovalMemberShipId).ContactId).id;
        var nameid = mems[mem_index].id;
        // line.memberURL  = 'https://membership.scouts.org.uk/#/membersearch/'+mems.find((element) => element.id == roles.find((element) => element.Id==role_detail[i].roleApprovalMemberShipId).ContactId).id+"/viewmember";
        //  var memhtml = '<a href="https://membership.scouts.org.uk/#/membersearch/'+nameid+'/viewmember" target="_blank">'+fullname+'</a>';
        //     if (document.getElementById("gmnumber").checked) {
        line.number = mems[mem_index].membershipnumber;
        //   };
        if (role_index>=0) {
            line.status = role_detail[role_index].rolestatus;

            var sdate = new Date(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).StartDate);
            line.start = sdate.toLocaleDateString('en-GB').toString();
            var days  = Math.round((new Date() - sdate) / (1000 * 60 * 60 * 24));
            line.days = days;
            for (var j = 0; j < act.length; j++) {

                if (role_detail[role_index].actions.find((element)=>element.typeid==act[j])) {

                    var state= 0x274C;
                    var value = role_detail[role_index].actions.find((element)=>element.typeid==act[j]).status;
                    if ((value=="Satisfactory")||(value=="Completed")||(value=="Held - Satisfactory")){state= 0x2705;}

                    line[titlefieldnames[j+8]] = String.fromCodePoint(state);

                } else {
                    // Deal with bug in API that means this can be missing.....
                    if(j==3) {
                        state= 0x274C;
                        line[titlefieldnames[j+8]] = String.fromCodePoint(state);
                    } else {
                        line[titlefieldnames[j+8]] = "";
                    }
                }
            }
        } else { }


        // Learning

        if (document.getElementById("gmlearning").checked) {
            for ( j = 0; j < learn.length; j++) {

                state= 0x274C; // Red Cross 10060
                var lr = mem_learning.find((element) => element.id == mems[mem_index].id);
                if (lr.learn.find((object)=>object.title==learn[j])){
                    state= 0x2705; // Green Tick 9989
                }
                // Check Expiry
                var has_expiry = false;
                if (lr.learn.find((object)=>object.title==learn[j])){
                    if (lr.learn.find( (object) => object.title == learn[j]).expiryDate !=null) {
                        sdate = new Date(lr.learn.find((object)=>object.title==learn[j]).expiryDate);
                        days  = Math.round((sdate - new Date()) / (1000 * 60 * 60 * 24));
                        has_expiry = true;
                        if (days<=90) {state = 0x26A1; } // Flash 9889
                        if (days<0) {state = 0x274C;}   // Red Cross 10060
                    } else {days=0;sdate = new Date("1977-01-01T00:00:01Z") ;}
                } else {days=0;sdate = new Date("1977-01-01T00:00:01Z") ;}
                if (role_index>=0) {
                    var role_find = {};
                    role_find = roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId);
                    if (j==7) {
                        // Trustee
                        //       if ((!roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName.includes('Lead Volunteer'))&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName!='Trustee')) {
                        if (!is_trustee(role_find.RoleName)) {
                            if(state==0x2705) {state=0x2714;} // Black Tick
                            else {
                                state = 0x0020;
                            }
                        }
                    }
                    if (j==8) {
                        // Team Lead is_teamleader(rolename,unit_type,ParentTeamId,team_name)
                        //   if ((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName.includes('Lead Volunteer'))||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Team Leader')&&((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType!='Group Section')&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType!='District Section'))) {
                        if (is_teamleader(role_find.RoleName,role_find.UnitType,role_find.ParentTeamId,role_find.Team)) {
                        } else {if(state==0x2705) {
                            state=0x2714;}
                                else {
                                    state = 0x0020;
                                }}
                    }

                    if (j==6) {
                        // programme - only section roles
                        //    if ((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType!='Group Section')&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType!='District Section')) {
                        if (!is_section(role_find.UnitType) )
                            if(state==0x2705) {
                                state=0x2714;}
                            else {
                                state = 0x0020;
                            }
                    }

                    if (j==5) {
                        if ((!has_expiry)&&(state== 0x2705)) { state= 0x274C; } // Set to Expired....
                        // First Response
                        //    if ((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType=='Group Section')||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType=='District Section')||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Group Lead Volunteer')||((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Team Leader'))&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).Team=='14-24 Team')) {
                        if (is_firstresponse(role_find.UnitType,role_find.RoleName,role_find.Team)) {
                        } else { if((!has_expiry)&&(state==0x274C)) {state=0x0020;}
                                if(state==0x2705) { // Green
                                    state=0x2714;} // Black Tick
                                else if (state==0x26A1) {} // Flash
                                else if (state==0x274C) {state=0x2716;} // Cross
                                else {
                                    state = 0x0020;
                                }
                               }
                    }
                    // Check for those not needing training
                    //   var rn = roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName;
                    //   if ((rn=='Non Member - Needs disclosure')||(rn=='President')||(rn=='Vice President')||(rn=='Holding')||(rn=='Retired Member')) {
                    if (!needs_training(role_find.RoleName)) {
                        if ((state == 0x26A1)||(state == 0x2705)||(state == 0x2714)) {
                            state=0x2714;}   // Black Tick
                        else if (state==0x2716) {} // Black Cross
                        else {
                            state = 0x0020;
                        }
                    }

                } else {

                    if ((j==7)&&(mems[mem_index].Role!=null)) {
                        // Trustee
                        if (!is_trustee(mems[mem_index].Role)) {
                            //  if ((!mems[mem_index].Role.includes('Lead Volunteer'))&&(mems[mem_index].RoleName!='Trustee')) {

                            if(state==0x2705) {
                                state=0x2714;}
                            else {
                                state = 0x0020;
                            }
                        }
                    }
                    if ((j==8)&&(mems[mem_index].Role!=null)) {
                        // Team Lead
                        if (is_teamleader(mems[mem_index].Role,mems[mem_index].unittype,null,mems[mem_index].Team)) {

                            //         if ((mems[mem_index].Role.includes('Lead Volunteer'))||(mems[mem_index].Role=='Team Leader')&&((mems[mem_index].unittype!='Group Section')&&(mems[mem_index].unittype!='District Section'))) {

                        } else {if(state==0x2705) {
                            state=0x2714;}
                                else {
                                    state = 0x0020;
                                }}
                    }

                    if (j==6) {
                        // programme - only section roles
                        if (!is_section(mems[mem_index].unittype) ){
                            //       if ((mems[mem_index].unittype!='Group Section')&&(mems[mem_index].unittype!='District Section')) {
                            if(state==0x2705) {
                                state=0x2714;}
                            else {
                                state = 0x0020;
                            }
                        }
                    }
                    if ((j==5)&&(mems[mem_index].Role!=null)) {
                        // First Response
                        //     if ((mems[mem_index].unittype=='Group Section')||(mems[mem_index].unittype=='District Section')||(mems[mem_index].Role=='Group Lead Volunteer')||((mems[mem_index].Role=='Team Leader'))&&(mems[mem_index].Team=='14-24 Team')) {
                        if (is_firstresponse(mems[mem_index].unittype,mems[mem_index].Role,mems[mem_index].Team)) {

                        } else  { if(state==0x2705) { // Green
                            state=0x2714;} // Black Tick
                                 else if (state==0x26A1) {} // Flash
                                 else if (state==0x274C) {state=0x1F7A6;} // Cross
                                 else {
                                     state = 0x0020;
                                 }
                                }
                    }
                    // Check for those not needing training
                    var rn = mems[mem_index].Role;
                    if (rn!=null) {
                        if (!needs_training(mems[mem_index].Role)) {
                            if ((state == 0x26A1)||(state == 0x2705)||(state == 0x2714)) {
                                state=0x2714;}
                            else if (state==0x1F7A6) {}
                            else {
                                state = 0x0020;
                            }
                        }
                    }

                }
                line[learnfieldnames[j]] = String.fromCodePoint(state);

                var blankdate =  new Date("1977-01-01T00:00:01Z");
                if (((j==3)||(j==4)||(j==5))) {

                    if (lr.learn.find( (object) => object.title == learn[j])) {
                        //  if (sdate.getTime()!= blankdate.getTime()) {

                        line[learnfieldnames[j]+'_expiry'] = sdate.toLocaleDateString('en-GB');


                    } else {  line[learnfieldnames[j]+'_expiry'] = ""; }
                } else {

                    line[learnfieldnames[j]+'_expiry'] = "";
                }


                if (((j==3)||(j==4)||(j==5))) {

                    if (!document.getElementById("gmwelcome").checked) {
                        //   if ((j!=5)||(((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType=='Group Section')||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).UnitType=='District Section')||(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Group Lead Volunteer')||((roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName=='Team Leader'))&&(roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).Team=='14-24 Team'))&&j==5)) {

                        //       if (sdate!= new Date("1000-01-01T00:00:01Z")) {
                        if (has_expiry) {
                            line[learnfieldnames[j]+'_expirydays'] = days;

                        } else {  line[learnfieldnames[j]+'_expirydays'] = ""}
                        //    }
                    } else {if (sdate!= new Date("1000-01-01T00:00:01Z")) {
                        line[learnfieldnames[j]+'_expirydays'] = days;

                    }}


                }
            }

        }
        if (document.getElementById("gmdbs").checked) {
            if (role_index>=0) {
                rn = roles.find((element) => element.Id==role_detail[role_index].roleApprovalMemberShipId).RoleName;
            } else { rn=mems[mem_index].Role;}
            if (!needs_dbs(rn)) {
                // No DBS Needed
                line.dbs_expiry = "";
                line.dbs_expirydays = "";
                line.dbsstatus = "";
                line.dbs =" ";

            } else {
                var mindex = dbs_detail.findIndex((element)=> element.id == mems[mem_index].id)
                if (mindex>=0) {
                    var time = new Date("1000-01-01T00:00:01Z") ;
                    var timel = new Date("1000-01-01T00:00:01Z") ;
                    var dbs_status = "";
                    if (dbs_detail[mindex].feed.entry != undefined) {
                        if ( dbs_detail[mindex].feed.entry.length != undefined ) {
                            for(var dbsloop=0;dbsloop<dbs_detail[mindex].feed.entry.length;dbsloop++) {
                                sdate = new Date(dbs_detail[mindex].feed.entry[dbsloop].content.m_properties.d_ExpiryDate);
                                if (sdate>time) { time = sdate;}
                                sdate = new Date(dbs_detail[mindex].feed.entry[dbsloop].content.m_properties.d_IssuedDate);
                                if (sdate>timel) { timel = sdate;dbs_status=dbs_detail[mindex].feed.entry[dbsloop].content.m_properties.d_Status; if (dbs_status!="Disclosure issued"){dbs_status+=" "+timel.toLocaleDateString('en-GB')}}
                            }
                        }
                        if (dbs_detail[mindex].feed.entry.length == undefined) {
                            // Single DBS
                            sdate = new Date(dbs_detail[mindex].feed.entry.content.m_properties.d_ExpiryDate);
                            if (sdate>time) { time = sdate;}
                            sdate = new Date(dbs_detail[mindex].feed.entry.content.m_properties.d_DateModified);
                            dbs_status=dbs_detail[mindex].feed.entry.content.m_properties.d_Status;
                            if (dbs_status!="Disclosure issued"){dbs_status+=" "+sdate.toLocaleDateString('en-GB')}
                        }
                    }
                    line.dbs = "";
                    var dbsdatedisp = time.toLocaleDateString('en-GB');
                    if (time<new Date()) {dbsdatedisp=String.fromCodePoint(0x274C)};
                    var expdays  = Math.round((time-new Date()) / (1000 * 60 * 60 * 24));
                    if ((expdays<=90)&&(expdays>0)) {
                      //  dbsdatedisp+=String.fromCodePoint(0x26A1);
                        line.dbs = String.fromCodePoint(0x26A1);
                    }

                    line.dbs_expiry =dbsdatedisp;

                    line.dbsstatus = "";
                    if (expdays<0) {  line.dbs = String.fromCodePoint(0x274C); };
                    if (expdays>90) {  line.dbs = String.fromCodePoint(0x2705); };
                    line.dbsstatus = dbs_status;
                    line.dbs_expirydays = expdays;

                } else {
                    line.dbsexpiry = String.fromCodePoint(0x274C);
                    line.dbs = String.fromCodePoint(0x274C);
                    line.dbsexpirydays = "";
                    line.dbsstatus= "";
                }
            }
        }

        return line;
    }







    function report_filter() {
        if (!document.getElementById("gmnumber").checked) { tabletabulator.hideColumn("number") } else { tabletabulator.showColumn("number") }
        if (!document.getElementById("gmdates").checked) { tabletabulator.hideColumn("safeguarding_expiry");tabletabulator.hideColumn("firstresponse_expiry");tabletabulator.hideColumn("safety_expiry") } else
        { tabletabulator.showColumn("safeguarding_expiry");tabletabulator.showColumn("firstresponse_expiry");tabletabulator.showColumn("safety_expiry") }
        if (!document.getElementById("gmdays").checked) { tabletabulator.hideColumn("safeguarding_expirydays");tabletabulator.hideColumn("firstresponse_expirydays");tabletabulator.hideColumn("safety_expirydays") } else
        { tabletabulator.showColumn("safeguarding_expirydays");tabletabulator.showColumn("firstresponse_expirydays");tabletabulator.showColumn("safety_expirydays") }
        if (!document.getElementById("gmdbsip").checked) { tabletabulator.hideColumn("dbsstatus") } else  { tabletabulator.showColumn("dbsstatus") }

        var titlefieldnames = ['disclosure','gdpr','declaration','profile','references','ce','welcome','learning','trustee'];
        if (document.getElementById("gmwelcome").checked) {
            for(var i=0; i<titlefieldnames.length;i++) {
                tabletabulator.hideColumn(titlefieldnames[i]) }
        }
        else {
            for( i=0; i<titlefieldnames.length;i++) {
                tabletabulator.showColumn(titlefieldnames[i]) }
        }

        var learnfieldnames = ['inclusion','datap','who','safeguarding','safety','firstresponse','programme','trustee2','leading'];
        if (!document.getElementById("gmlearning").checked) {
            for(i=0; i<learnfieldnames.length;i++) {
                tabletabulator.hideColumn(learnfieldnames[i])
                if ((i>=3)&&(i<=5)) {
                    tabletabulator.hideColumn(learnfieldnames[i]+"_expiry")
                    tabletabulator.hideColumn(learnfieldnames[i]+"_expirydays")
                }
            }
        } else {
            for( i=0; i<learnfieldnames.length;i++) {
                tabletabulator.showColumn(learnfieldnames[i])
                if ((i>=3)&&(i<=5)) {
                    tabletabulator.showColumn(learnfieldnames[i]+"_expiry")
                    tabletabulator.showColumn(learnfieldnames[i]+"_expirydays")
                }
            }
        }
        var dbsfieldnames = ['dbs','dbs_expiry','dbs_expirydays','dbsstatus'];
        if (!document.getElementById("gmdbs").checked) {
            for(i=0; i<dbsfieldnames.length;i++) {
                tabletabulator.hideColumn(dbsfieldnames[i])
            }
        } else {
            for(i=0; i<dbsfieldnames.length;i++) {
                tabletabulator.showColumn(dbsfieldnames[i])
            }
        }

        if (!document.getElementById("gmdates").checked) { tabletabulator.hideColumn("safeguarding_expiry");tabletabulator.hideColumn("firstresponse_expiry");tabletabulator.hideColumn("safety_expiry") } else
        { tabletabulator.showColumn("safeguarding_expiry");tabletabulator.showColumn("firstresponse_expiry");tabletabulator.showColumn("safety_expiry") }
        if (!document.getElementById("gmdays").checked) { tabletabulator.hideColumn("safeguarding_expirydays");tabletabulator.hideColumn("firstresponse_expirydays");tabletabulator.hideColumn("safety_expirydays") } else
        { tabletabulator.showColumn("safeguarding_expirydays");tabletabulator.showColumn("firstresponse_expirydays");tabletabulator.showColumn("safety_expirydays") }
        if (!document.getElementById("gmdbsip").checked) { tabletabulator.hideColumn("dbsstatus") } else  { tabletabulator.showColumn("dbsstatus") }

        tabletabulator.setFilter([{field : "name",type: "like", value: document.getElementById("gmpersonname").value}, {field: "team", type: "like", value: document.getElementById("gmteamname").value}]);
        tabletabulator.redraw(true);
    }




    function report_filter2() {


        var expirytext = "Expiry";
        var expirytextdays = "Expiry Days";
        var act = ['managerDisclosureCheck','dataProtectionTrainingComplete','signDeclaration','updateMemberProfile','referenceRequest','safeguardconfidentialEnquiryCheck','managerWelcomeConversation','coreLearning','managerTrusteeCheck'];
        var title = ['Role','Unit','Team','Name','Status','Start Date','Days','Disclosure','GDPR','Declaration','Profile','References','CE Check','Welcome','Learning','Trustee'];
        var learn = ['Creating Inclusion','Data Protection in Scouts','Who We Are and What We Do','Safeguarding','Safety','First Response','Delivering a Great Programme','Being a Trustee in Scouts','Leading Scout Volunteers'];
        $("#gmState")[0].innerText = "Data Collection Completed";
        var myTableDiv = document.getElementById("gmTable");
        var table = document.createElement('TABLE');
        table.border = '1';
        table.id = "GMourreport";
        var tableBody = document.createElement('TBODY');
        table.appendChild(tableBody);
        var tr = document.createElement('TR');
        tableBody.appendChild(tr);
        report_headers(tableBody);

        if (!(document.getElementById("gmwelcome").checked)) {
            for (i = 0; i < role_detail.length; i++) {
                var displine=true;

                if (document.getElementById("gmmissing").checked) {
                    var any_missing = false;
                    for (var j = 0; j < act.length; j++) {
                        if (role_detail[i].actions.find((element)=>element.typeid==act[j])) {
                            var value = role_detail[i].actions.find((element)=>element.typeid==act[j]).status;
                            if ((value!="Satisfactory")&&(value!="Completed")&&(value!="Held - Satisfactory")){ any_missing=true;}
                        }
                    }
                    if (any_missing==false) {displine=false}
                }
                //
                if (document.getElementById("gmteamonly").checked) {
                    var  filter = document.getElementById("gmunitname").value.replaceAll("%", "*");
                    let w = filter.replace(/[.+^${}()|[\]\\]/g, '\\$&'); // regexp escape
                    const re = new RegExp(`^${w.replace(/\*/g,'.*').replace(/\?/g,'.')}$`,'i');
                    if(!re.test(roles.find((element) => element.Id==role_detail[i].roleApprovalMemberShipId).UnitId)){displine=false}
                }

                if (mems.find((element) => element.id == roles.find((element) => element.Id==role_detail[i].roleApprovalMemberShipId).ContactId)!=undefined){

                    var fullname = mems.find((element) => element.id == roles.find((element) => element.Id==role_detail[i].roleApprovalMemberShipId).ContactId).fullname;
                    if (document.getElementById("gmpersonname").value!="") {
                        if(!fullname.toLowerCase().includes(document.getElementById("gmpersonname").value.toLowerCase())){displine=false}
                    }
                } else {displine=false;}

                if (document.getElementById("gmteamname").value!="") {
                    if (!roles.find((element) => element.Id==role_detail[i].roleApprovalMemberShipId).Team.toLowerCase().includes(document.getElementById("gmteamname").value.toLowerCase())){displine=false}
                }

                if (document.getElementById("gmhelper").checked) {
                    var rolename = roles.find((element) => element.Id==role_detail[i].roleApprovalMemberShipId).RoleName;
                    if (rolename.includes("Non Member - Needs disclosure")) {displine=false;}
                    if (rolename.includes("Holding")) {displine=false;}
                }
                if (displine) {
                    output_report_line(tableBody,i,mems.findIndex( (element) => element.id == roles.find( (element) => element.Id == role_detail[i].roleApprovalMemberShipId).ContactId));

                }
            }
        }
        else
        {
            for (var i = 0; i < mems.length; i++) {
                displine = true;
                fullname = mems[i].fullname;
                if (document.getElementById("gmpersonname").value!="") {
                    if(!fullname.toLowerCase().includes(document.getElementById("gmpersonname").value.toLowerCase())){displine=false}
                }
                if (mems[i].Role==null) { displine=false}
                if (displine) {
                    output_report_line(tableBody,-1,i);
                }
            }
        }
        table.style.fontSize = "10pt";
        document.getElementById("gmTable").innerHTML="";
        myTableDiv.appendChild(table);


        //var tf = new TableFilter('GMourreport',filtersConfig );
        //t
    }

  // var level_de = "e5b6ecb0-0360-9cde-f269-7660a2afb0fb";
    var level_de = "89d2f00e-6d57-8032-be02-47393fcf0556";
   function get_dedata5() {
     console.log("role");
     console.log(roles);
     for (var i=0;i<role_detail.data.length;i++) {
         // We could play with team name here as well - we know the ID => Name for the standard teams
         var r = roles.data.find(e=> e["First name"]+" "+e["Surname"]==role_detail.data[i].FullName&e["Role"]==role_detail.data[i].Role&&e["Unit Name"]==role_detail.data[i].UnitName);
         if (r === undefined) {
             r = roles.data.find(e=> e["First name"]+" "+e["Surname"]==role_detail.data[i].FullName);
         }
          console.log(role_detail.data[i].FullName);
         role_detail.data[i].membershipid = r["Membership Number"];
         role_detail.data[i].teamname = r["Team"];
         console.log(r["Membership Number"]+" "+r["First name"]+" "+r["Surname"]+" "+role_detail.data[i].FullName);
     }


   }
    function  get_dedata4() {
       console.log("mems");
        console.log(mems);
         console.log("role_detail");
        console.log(role_detail);
                              var report = { WhereClause: null,
                  aggregateField: "*",
                  aggregateFunction: "COUNT",
                  distinct: true,
                  emailId: null,
                  isDashboardQuery: false,
                  isHierarchy: true,
                  orderby: "",
                  pageNo: 1,
                  pageSize: 5000,
                  query: "",
                  selectContactFields: null,
                  selectFields: ["FirstName","LastName","MembershipNumber","Role","Team","CountyName","DistrictName","GroupName","Telephone"],
                  table: "PreloadedAwardsDashboardView",
                  teamId: "",
                  unitId: level_de,
                //    unitId:"89d2f00e-6d57-8032-be02-47393fcf0556",
              //    treeVal:{"type":"group","id":"cca1f1b8-25e9-4b2f-a0f4-4f141d5ab86f"},
                  userId: null,
                  viewQueryAction: "data"
                 }
                              var report2 = { WhereClause: null,
                  aggregateField: "*",
                  aggregateFunction: "COUNT",
                  distinct: true,
                  emailId: null,
                  isDashboardQuery: false,
                  isHierarchy: true,
                  orderby: "",
                  pageNo: 1,
                  pageSize: 5000,
                  query: "",
                  selectContactFields: null,
                  selectFields: ["FirstName","LastName","MembershipNumber","EmailAddress","Telephone","DistrictName","County","Role","Team","unitName","Line2","PostalCode","Country","Town","GroupName","CountyName","Line1"],
                  table: "PreloadedAwardsDashboardView",
                  teamId: "",
                  unitId: level_de,
                //    unitId:"89d2f00e-6d57-8032-be02-47393fcf0556",
              //    treeVal:{"type":"group","id":"cca1f1b8-25e9-4b2f-a0f4-4f141d5ab86f"},
                  userId: null,
                  viewQueryAction: "data"
                 }

          GM_xmlhttpRequest({
    method: "POST",
    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/DataExplorer/GetResultsAsync",
    data: JSON.stringify(report2),
    headers: {
        "Content-Type": "application/json;odata=minimalmetadata",
        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
        "Accept": "application/json, text/plain, */*",
        "Type": "table"

    },

    onload: function(response) { //  console.log(response);console.log(count);
        console.log("report");
     //   console.log(JSON.parse(response.responseText));
        roles = JSON.parse(response.responseText);
        get_dedata5()
    },
    onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
    onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
});

                            }

    function get_dedata3() {
        var q = "";
        for (var i=0;i<mems.length;i++) {
            q+="unitid='"+mems[i].id+"' ";
            if (i!=mems.length-1) { q+=" OR ";}
        }
        var report2 = {
                contactId: "",
                distinct: true,
                id: "",
         isHierarchy: true,
                isDashboardQuery: false,
                name: "",
                order: "asc",
                orderBy: "Lastname",
                pageNo: 1,
                pageSize: 5000,
             //   query: `unitid='e5b6ecb0-0360-9cde-f269-7660a2afb0fb'`,
                query: q,
       // query: `unitid='89d2f00e-6d57-8032-be02-47393fcf0556'`, Runnymede
                selectFields: ["Id", "PreferredName", "FullName", "Firstname", "Lastname", "RoleStatusName", "Role", "unitid", "TeamId", "ParentTeamId", "UnitPrefix", "Unitname","ContactMembershipId", "UnitTypeId"],

          //          "RoleStatusName", "Role"
          //      ],
//selectFields: ["Id"],
                table: "TeamMembersView"
            };
       GM_xmlhttpRequest({
          method: "POST",
    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/DataExplorer/GetResultsAsync",
    data: JSON.stringify(report2),
    headers: {
        "Content-Type": "application/json;odata=minimalmetadata",
        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
        "Accept": "application/json, text/plain, */*",
        "Type": "table"

    },

    onload: function(response) { //  console.log(response);console.log(count);
          console.log("report2");
        console.log(JSON.parse(response.responseText));
        role_detail = JSON.parse(response.responseText);
        get_dedata4();
    },
    onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
    onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
});

    }

    function get_dedata2(oc) {

         // Get the Tree
        var max=50;
        var datapass = '{"pagesize":'+max+',"nexttoken":'+oc+',"filter":{"global" : "","globaland":false,"fieldand":true},"unitId":"'+level_de+'"}';
        //       console.log(datapass);
        if (debug) {GM_log("Counter for get_Sub Unit "+oc);}
        GM_xmlhttpRequest({
            method: "POST",
            context: oc+1,
            url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/UnitListingAsync",
            data: datapass,
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                "Accept": "application/json, text/plain, */*"
            },

            onload: function(response) { //  console.log(response);console.log(count);
                if (debug) {GM_log("Counter Received "+response.context)}
                // console.log(JSON.parse(response.responseText).data[1]);
                var mem = null;
                try { mem=JSON.parse(response.responseText).data} catch {console.log("JSON Error "+response.responseText);}
                //   mem =(JSON.parse(response.responseText).data);
                if (mem.length>0) {
                    if (debug) {

                    }
                }
                if ((mem.length==0)&&(debug)) {
                    GM_log("GetData: We got an error collecting data for Sub mems page"+response.context);
                }
                if (mem.length!=0) {
                    mems = mems.concat(mem);
                }
                $("#gmorgstatus")[0].innerHTML = mems.length+" Units read";
                if (debug) {GM_log("Sub Units mems now has length "+mems.length);}
                if (debug) {GM_log("Sub Units mem now has length "+mem.length);}
                if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}
                if ( mem.length ==50) {get_dedata2(oc+1); } else {missed = 0; get_dedata3()};},
            onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
            onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
        });

    }

   function get_data_de() {
        mems =[];
       mems.push({id: level_de});
    get_dedata2(0);
    }



    function get_data_deold() {
    var report = { WhereClause: null,
                  aggregateField: "*",
                  aggregateFunction: "COUNT",
                  distinct: true,
                  emailId: null,
                  isDashboardQuery: false,
                  isHierarchy: true,
                  orderby: "",
                  pageNo: 1,
                  pageSize: 5000,
                  query: "",
                  selectContactFields: null,
                  selectFields: ["FirstName","LastName","MembershipNumber","Role","Team","CountyName","DistrictName","GroupName","Telephone"],
                  table: "PreloadedAwardsDashboardView",
                  teamId: "",
            //      unitId:"e5b6ecb0-0360-9cde-f269-7660a2afb0fb",
                    unitId:"89d2f00e-6d57-8032-be02-47393fcf0556",
              //    treeVal:{"type":"group","id":"cca1f1b8-25e9-4b2f-a0f4-4f141d5ab86f"},
                  userId: null,
                  viewQueryAction: "data"
                 }
    //e5b6ecb0-0360-9cde-f269-7660a2afb0fb
    var report2 = {
                contactId: "",
                distinct: true,
                id: "",
         isHierarchy: true,
                isDashboardQuery: false,
                name: "",
                order: "asc",
                orderBy: "Lastname",
                pageNo: 1,
                pageSize: 5000,
                query: `unitid='e5b6ecb0-0360-9cde-f269-7660a2afb0fb'`,
       // query: `unitid='89d2f00e-6d57-8032-be02-47393fcf0556'`, Runnymede
                selectFields: ["Id", "PreferredName", "FullName", "Firstname", "Lastname", "RoleStatusName", "Role", "unitid", "TeamId", "ParentTeamId", "UnitPrefix", "Unitname","ContactMembershipId", "UnitTypeId"],

          //          "RoleStatusName", "Role"
          //      ],
//selectFields: ["Id"],
                table: "TeamMembersView"
            };
        var report3 = {
                                        contactId: "",
                                        id: "",
                                        name: "",
                                 //       query: "ContactId ='".concat(l, "' AND IsNANUnit = 0"),
                        //                query: `unitid='89d2f00e-6d57-8032-be02-47393fcf0556'`,
             query: `unitid='e5b6ecb0-0360-9cde-f269-7660a2afb0fb'`,
                                        table: "ContactUnitsView",
                                        order: "asc",
                                        orderBy: "Team",
             isHierarchy: true,
                                        selectFields: ["TeamId", "Team", "UnitName", "unitid", "ParentTeam", "ParentTeamId", "unitType", "unitTypeId", "AllowSubTeam", "unitPrefix"],
                                        distinct: !0,
                                        isDashboardQuery: !1
                                    };
          GM_xmlhttpRequest({
    method: "POST",
    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/DataExplorer/GetResultsAsync",
    data: JSON.stringify(report),
    headers: {
        "Content-Type": "application/json;odata=minimalmetadata",
        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
        "Accept": "application/json, text/plain, */*",
        "Type": "table"

    },

    onload: function(response) { //  console.log(response);console.log(count);
        console.log("report");
        console.log(JSON.parse(response.responseText));
    },
    onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
    onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
});
         GM_xmlhttpRequest({
    method: "POST",
    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/DataExplorer/GetResultsAsync",
    data: JSON.stringify(report3),
    headers: {
        "Content-Type": "application/json;odata=minimalmetadata",
        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
        "Accept": "application/json, text/plain, */*",
        "Type": "table"

    },

    onload: function(response) { //  console.log(response);console.log(count);
        console.log("report3");
        console.log(JSON.parse(response.responseText));
    },
    onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
    onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
});
                  GM_xmlhttpRequest({
    method: "POST",
    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/DataExplorer/GetResultsAsync",
    data: JSON.stringify(report2),
    headers: {
        "Content-Type": "application/json;odata=minimalmetadata",
        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
        "Accept": "application/json, text/plain, */*",
        "Type": "table"

    },

    onload: function(response) { //  console.log(response);console.log(count);
          console.log("report2");
        console.log(JSON.parse(response.responseText));
    },
    onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
    onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
});
    }

    function report_onclick() {
        // RUn Report
        //     console.log("Report");
        if   (document.getElementById("gmdebug").checked) {
            debug = true;
        }
        var count = 1;
        mem="";
        //  $("#gmpopup").dialog ( {modal: true, height: 500, width: "100%"} );
        mems = [];
        role_detail=[];
        roles=[];
        document.getElementById("gmTable").innerHTML="Table will appear here....";
        //   document.getElementById("gmUnit").innerHTML = "UKJDKJD";
        var responsetext = "";
        var datapass;
        mem_learning= [];
        learn = [];
        dbs_detail=[];
        var tokexpDate = new Date( JSON.parse(localStorage.getItem("authInfo")).account.idTokenClaims.exp *1000);
    //    if(document.getElementById("gmexplorer").checked) {
    //         get_data_de(count);
   //     } else {
             get_data(count);
    //    }

    }

    function report_onclick2() {
        // Open Dialog
        console.log("Report");
        var count = 1;
        mem="";
        var h = $( window ).height();
        $("#gmpopup").dialog ( {modal: true, height: (h*0.75), width: "100%"} );
        mems = [];
        role_detail=[];
        roles=[];
        memsunique=[];
        //   document.getElementById("gmTable").innerHTML="Table will appear here....";
        var responsetext = "";
        var datapass;
        mem_learning= [];
        keepalive = window.setInterval(function () {
            if (window.location.href=='https://membership.scouts.org.uk/#/learning') {
                var ul = document.getElementById('menu-list-item-left-nav-homePageTile');
                ul.click();
            }
            else {
                ul = document.getElementById('menu-list-item-left-nav-learning');
                ul.click();
            }

            console.log("Keep Alive Active");
        }, 25000);  //5000 is 5 Seconds
        $('#gmpopup').on('dialogclose', function(event) {
            // alert('closed');
            clearInterval(keepalive);
            console.log("Keep Alive Cancelled");
        });
        datapass = '{"pagesize":50,"sort":{"field":"unitName","type":"asc"},"contactId":"'+JSON.parse(localStorage.getItem("authInfo")).idTokenClaims.extension_ContactId+'"}';

        GM_xmlhttpRequest({
    method: "POST",
    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/MyUnitsAsync",
    data: datapass,
    headers: {
        "Content-Type": "application/json",
        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
        "Accept": "application/json, text/plain, */*"
    },

    onload: function(response) { //  console.log(response);console.log(count);
        var myUnits = JSON.parse(response.responseText);
        for (var i=0;i<myUnits.data.length;i++) {
            document.getElementById("myUnits").innerHTML+= '<option value="'+myUnits.data[i].unitName+'">';
        }
    },
    onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
    onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
});

    }

    function report_print() {

        // var prtContent = document.getElementById("GMourreport");
        //  var prtContent = (tabletabulator.getHtml());
        var WinPrint = window.open('', '', 'left=0,top=0,width=800,height=900,toolbar=0,scrollbars=0,status=0');
        WinPrint.document.body.innerHTML = tabletabulator.getHtml();
        WinPrint.document.head.innerHTML += '<style> th.blue-background {    writing-mode: vertical-lr;    background-color: lightcyan; } \
th.yellow-background {    writing-mode: vertical-lr;    background-color: lightgoldenrodyellow; } th.orange-background {    writing-mode: vertical-lr;    background-color: salmon; } \
table.tabulator-print-table, tr, th, td {    border: solid;font-family: sans-serif;}</style>';
        //   WinPrint.document.close();
        WinPrint.focus();
        WinPrint.print();
        WinPrint.close();
    }

    function savecolors() {
        GM_setValue("colors", JSON.stringify(colors));
        GM_setValue("colorsT", JSON.stringify(colorsT));
    }
    function resetcolors() {
        colors = defcolors;
        colorsT = defcolorsT;
        for(cloop=0; cloop<colors.length;cloop++){
            document.getElementById("cpreview"+cloop).style.backgroundColor = colors[cloop];
            document.getElementById("cpreview"+cloop).style.color = colorsT[cloop];
            document.getElementById("back"+cloop).value = colors[cloop];
            document.getElementById("fore"+cloop).value = colorsT[cloop];
        }

    }
    function updateFirstC(event) {
        console.log("Update First");
        if (event.target.name.includes("back")) {
            document.getElementById("cpreview"+event.target.name[4]).style.backgroundColor = event.target.value;
            colors[event.target.name[4]] = event.target.value;
        }
        if (event.target.name.includes("fore")) {
            document.getElementById("cpreview"+event.target.name[4]).style.color = event.target.value;
            colorsT[event.target.name[4]] = event.target.value;
        }

        console.log(event);
    }

    function watchColorPicker(event) {
        console.log("Watch CP");
        console.log(event);
    }

    var unitsearchvalues = ["866060008","866060006","866060005","866060004","866060007","866060003","866060002","866060001","866060000"];
    var unitsearchtypes = ["Organisation","Country","Region","County","County Section","District","District Section","Group","Group Section"];
    var unitcount = [];
    var orglevel = 0;
    var  teams = [];
    var subteams = [];
    var maxsubteam = 0;


    function get_orgdata(oc,ol) {
        // Find the root
        var max = 50;
        teams = [];
        subteams = [];
        maxsubteam = 0;
        un = document.getElementById("gmunitnameo").value;
        var unt= unitsearchvalues[ol];
        if (debug) {GM_log("Unit Request for "+un+" Count "+oc+"/"+ol);}
        if (debug) {
            GM_log("Unit Request for "+un+" :"+ JSON.parse(localStorage.getItem("authInfo")).idToken.substring(1,50));
            var tokexpDate = new Date( JSON.parse(localStorage.getItem("authInfo")).account.idTokenClaims.exp *1000);
        }
        $("#gmorgstatus")[0].innerHTML = "Checking for "+unitsearchtypes[ol]+" "+un;
        var datapass = '{"pagesize":'+max+',"nexttoken":'+oc+',"filter":{"global" : "","globaland":false,"fieldand":true,filterfields:[{ "field" : "'+unt+'", "value": "'+un+'"}]},"unitId":""}';
        if (debug) {GM_log("Counter for get_data "+oc);}
        GM_xmlhttpRequest({
            method: "POST",
            context: oc+1,
            url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/UnitListingAsync",
            data: datapass,
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                "Accept": "application/json, text/plain, */*"
            },

            onload: function(response) { //  console.log(response);console.log(count);
                if (debug) {GM_log("Counter Recieved "+response.context)}
                // console.log(JSON.parse(response.responseText).data[1]);
                var mem = null;
                try { mem=JSON.parse(response.responseText).data} catch {console.log("JSON Error "+response.responseText);}
                //   mem =(JSON.parse(response.responseText).data);
                if (mem.length>0) {
                    if (debug) {

                    }
                }
                if ((mem.length==0)&&(debug)) {
                    GM_log("GetData: We got an error collecting data for mems page"+response.context);
                }
                if (mem.length!=0) {
                    mems = mems.concat(mem);
                }
                if (debug) {GM_log("mems now has length "+mems.length);}


                if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}
                $("#gmorgstatus")[0].innerHTML = "Checking for "+unitsearchtypes[response.context]+" "+un;

                if ((ol<unitsearchvalues.length)&&( mems.length==0)) {get_orgdata(oc,ol+1); } else {

                    var  filter = document.getElementById("gmunitnameo").value.replaceAll("%", "*");
                    let w = filter.replace(/[.+^${}()|[\]\\]/g, '\\$&'); // regexp escape
                    const re = new RegExp(`^${w.replace(/\*/g,'.*').replace(/\?/g,'.')}$`,'i');
                    mems = mems.filter( function(v) {return re.test(v.unitName)});
                    //      if (debug) {GM_log("Re: Filter by for Unit only roles now has length "+roles.length);}


                    get_orgdata2(0)};
            },
            onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
            onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
        });

    }

    var tealgrad = new go.Brush('Linear', { 0: 'rgb(32, 178, 170)', 1: 'rgb(0,128,128)' });
    var greengrad = new go.Brush('Linear', { 0: 'rgb(154, 205, 50)', 1: 'rgb(34, 139, 34)' });
    var bluegrad = new go.Brush('Linear', { 0: 'rgb(135, 206, 235)', 1: 'rgb(0, 191, 255)' });
    var redgrad = new go.Brush('Linear', { 0: 'rgb(220, 20, 60)', 1: 'rgb(255, 0, 0)' });
    var browngrad = new go.Brush('Linear', { 0: 'rgb(218, 160, 109)', 1: 'rgb(149, 69, 53 )' });
    var blackgrad = new go.Brush('Linear', { 0: 'rgb(0,0,0)', 1: 'rgb(0,0,0 )' });
    var pinkgrad = new go.Brush('Linear', { 0: 'rgb(128,0,0)', 1: 'rgb(200,0,0 )' });
    var greygrad = new go.Brush('Linear', { 0: 'rgb(128,128,128)', 1: 'rgb(200,200,200 )' });
    var purplegrad = new go.Brush('Linear', { 0: 'rgb(138,43,226)', 1: 'rgb(147,112,219 )' });


    function sectionBrushConverter(node) {
        if (node.unitType === "Group Section") {
            if (node.unitName.includes("Cub")) { return greengrad}
            if (node.unitName.includes("Pack")) { return greengrad}
            if (node.unitName.includes("Squirrel")) { return redgrad}
            if (node.unitName.includes("Drey")) { return redgrad}
            if (node.unitName.includes("Beaver")) { return bluegrad}
            if (node.unitName.includes("Colony")) { return bluegrad}
            return tealgrad;
        }
        if (node.unitType === "District Section") {
            return browngrad;
        }
        if (node.unitType === "Group") {
            return pinkgrad;
        }
        if (node.unitType === "District") {
            return blackgrad;
        }
        if (node.unitType === "Team") {
            return purplegrad;
        }
        if (node.unitType === "Sub Team") {
            return greygrad;
        }

        return "#44CCFF"
        // if (gender === "F") return pinkgrad;
    }

    var missed = 0;

    function get_orgdata2(oc) {
        // Get the Tree
        if (mems.length==0) { $("#gmorgstatus")[0].innerHTML = "No Units found";}

        var max=50;
        var datapass = '{"pagesize":'+max+',"nexttoken":'+oc+',"filter":{"global" : "","globaland":false,"fieldand":true},"unitId":"'+mems[0].id+'"}';
        //       console.log(datapass);
        if (debug) {GM_log("Counter for get_Sub Unit "+oc);}
        GM_xmlhttpRequest({
            method: "POST",
            context: oc+1,
            url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/UnitListingAsync",
            data: datapass,
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                "Accept": "application/json, text/plain, */*"
            },

            onload: function(response) { //  console.log(response);console.log(count);
                if (debug) {GM_log("Counter Received "+response.context)}
                // console.log(JSON.parse(response.responseText).data[1]);
                var mem = null;
                try { mem=JSON.parse(response.responseText).data} catch {console.log("JSON Error "+response.responseText);}
                //   mem =(JSON.parse(response.responseText).data);
                if (mem.length>0) {
                    if (debug) {

                    }
                }
                if ((mem.length==0)&&(debug)) {
                    GM_log("GetData: We got an error collecting data for Sub mems page"+response.context);
                }
                if (mem.length!=0) {
                    mems = mems.concat(mem);
                }
                $("#gmorgstatus")[0].innerHTML = mems.length+" Units read";
                if (debug) {GM_log("Sub Units mems now has length "+mems.length);}
                if (debug) {GM_log("Sub Units mem now has length "+mem.length);}
                if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}
                if ( mem.length ==50) {get_orgdata2(oc+1); } else {missed = 0; get_orgdata5()};},
            onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
            onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
        });

    }




    function  get_orgdata5() {
        // Get Teams
        var minlevel = $("#gmorglevel").val();
        mems = mems.filter(function(n) {return n.UnitLevel>=minlevel});
        if (mems.length==0) { $("#gmorgstatus")[0].innerHTML = "No Units found";}

        //  $("#gmorgstatus")[0].innerHTML = response.context+" sub teams read";

        var exclude_section_teams = document.getElementById("gmnosteams").checked;
        var exclude_all_teams = document.getElementById("gmnoteams").checked;
        if (!exclude_all_teams) {
            for(var i=0;i<mems.length;i++) {

                //(b.unitType === "Group Section")&&(a.unitType === "Group Section")
                if ((exclude_section_teams)&&((mems[i].unitType === "Group Section")||(mems[i].unitType === "District Section")))  {
                    missed+=1;
                } else {
                    var datapass = '{"unitId":"'+mems[i].id+'","type":"team"}';
                    GM_xmlhttpRequest({
                        method: "POST",
                        context: i+1,
                        url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/UnitTeamsAndRolesListingAsync",
                        data: datapass,
                        headers: {
                            "Content-Type": "application/json",
                            "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                            "Accept": "application/json, text/plain, */*"
                        },

                        onload: function(response) { //  console.log(response);console.log(count);
                            if (debug) {GM_log("Counter Recieved "+response.context)}
                            // console.log(JSON.parse(response.responseText).data[1]);
                            var mem = null;
                            try { mem=JSON.parse(response.responseText)} catch {console.log("JSON Error "+response.responseText);}
                            //   mem =(JSON.parse(response.responseText).data);
                            $("#gmorgstatus")[0].innerHTML = response.context+" teams read";
                            teams.push(mem);
                            //console.log(mem);
                            //    $("#gmorgstatus")[0].innerHTML = mems.length+" Units read";

                            if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}

                            $("#gmState")[0].innerText = "Reading Sub Units for "+un+" ("+response.context+") "+"Total read "+mems.length;
                            if ( teams.length+missed== mems.length) {subteams = []; maxsubteam=0; get_orgdata6();}},
                        onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
                        onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
                    });

                }

            }} else {get_orgdata3(false)}
    }


    function  get_orgdata6() {
        for(var i=0;i<teams.length;i++) {
            for (var j=0;j<teams[i].teams.length;j++) {
                if (teams[i].teams[j].allowSubTeam){
                    maxsubteam +=1;
                    var datapass = '{"unitId":"'+teams[i].unitId+'","type":"subteam","teamId" :"'+teams[i].teams[j].teamId+'"}';
                    GM_xmlhttpRequest({
                        method: "POST",
                        context: maxsubteam,
                        url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/UnitTeamsAndRolesListingAsync",
                        data: datapass,
                        headers: {
                            "Content-Type": "application/json",
                            "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                            "Accept": "application/json, text/plain, */*"
                        },

                        onload: function(response) { //  console.log(response);console.log(count);
                            if (debug) {GM_log("Counter Recieved "+response.context)}
                            // console.log(JSON.parse(response.responseText).data[1]);
                            var mem = null;
                            try { mem=JSON.parse(response.responseText)} catch {console.log("JSON Error "+response.responseText);}
                            //   mem =(JSON.parse(response.responseText).data);

                            subteams.push(mem);
                            //console.log(mem);
                            //    $("#gmorgstatus")[0].innerHTML = mems.length+" Units read";
                            $("#gmorgstatus")[0].innerHTML = response.context+" sub teams read";
                            if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}

                            $("#gmState")[0].innerText = "Reading Sub Units for "+un+" ("+response.context+") "+"Total read "+mems.length;
                            if ( response.context == maxsubteam) {get_orgdata3(false);}},
                        onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
                        onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
                    });

                }
            }
        }
    }
    function section_sort(a,b) {
        if (a.UnitLevel!=b.UnitLevel) {
            if (a.UnitLevel<b.UnitLevel) {return -1}
            if (a.UnitLevel==b.UnitLevel) {return 0}
            if (a.UnitLevel>b.UnitLevel) {return 1}
        }

        if ((a.UnitLevel==b.UnitLevel)&&(a.UnitLevel!=1)) {
            if (a.unitName<b.unitName) {return -1}
            if (a.unitName==b.unitName) {return 0}
            if (a.unitName>b.unitName) {return 1}
        }

        if ((b.unitType === "Group Section")&&(a.unitType === "Group Section")) {
            var asection = 4;
            if (a.unitName.includes("Cub")) {asection = 3;}
            if (a.unitName.includes("Pack")) { asection = 3;}
            if (a.unitName.includes("Drey")) { asection = 1;}
            if (a.unitName.includes("Squirrel")) { asection = 1;}
            if (a.unitName.includes("Beaver")) {asection = 2;}
            if (a.unitName.includes("Colony")) { asection = 2;}
            var bsection = 4;
            if (b.unitName.includes("Cub")) {bsection = 3;}
            if (b.unitName.includes("Pack")) { bsection = 3;}
            if (b.unitName.includes("Drey")) { bsection = 1;}
            if (b.unitName.includes("Squirrel")) { bsection = 1;}
            if (b.unitName.includes("Beaver")) {bsection = 2;}
            if (b.unitName.includes("Colony")) { bsection = 2;}
            if (asection<bsection) {return -1}
            if (asection==bsection) {return 0}
            if (asection>bsection) {return 1}
        }




        return 0
    }

    function section_sort2(a,b) {
        var c= section_sort(a,b) ;
        //   console.log(a.unitType +" "+a.unitName+" "+b.unitType +" "+b.unitName+" "+c);
        return c;
    }

    function get_orgdata3(justdisplay) {
        if (!justdisplay) {
            // Add in Teams
            for (var i=0; i<teams.length;i++) {
                for (var j=0; j<teams[i].teams.length; j++) {
                    var mem = new Object;
                    mem.parentUnitId =  teams[i].unitId;
                    mem.id = teams[i].teams[j].teamId+teams[i].unitId;
                    mem.unitName = teams[i].teams[j].teamName;
                    mem.UnitLevel = 98;
                    mem.unitType = "Team";
                    mem.teamunitId = teams[i].unitId;
                    mems.push(mem);
                }
            }
            for (i=0;i<subteams.length; i++) {
                if (subteams[i].teams!=null) {
                    for ( j=0; j<subteams[i].teams.length; j++) {
                        mem = new Object;
                        mem.parentUnitId =  subteams[i].teams[j].parentTeamId+ subteams[i].unitId;
                        mem.id = subteams[i].teams[j].teamId;
                        mem.unitName = subteams[i].teams[j].teamName;
                        mem.UnitLevel = 99;
                        mem.unitType = "Sub Team";
                        mem.teamunitId = subteams[i].unitId;
                        mems.push(mem);
                    }}}
            mems.sort(section_sort2);
            console.log(mems);
            console.log("teams");
            console.log(teams);
            console.log("Subteams");
            console.log(subteams);
        }
        myDiagram.nodeTemplate =
            new go.Node("Horizontal",
                        { background: "#44CCFF" }).bind('background', '', sectionBrushConverter)
            .add(
            //  new go.Picture({ margin: 10, width: 50, height: 50, background: "red" })
            //    .bind("source"),
            new go.TextBlock("Default Text",
                             { margin: 8, stroke: "white", font: "bold 12px sans-serif" })
            .bind("text", "unitName")
        );



        myDiagram.linkTemplate =
            new go.Link(
            // default routing is go.Routing.Normal
            // default corner is 0
            { routing: go.Routing.Orthogonal, corner: 5 })
            .add(
            // the link path, a Shape
            new go.Shape({ strokeWidth: 3, stroke: "#555" }),
            // if we wanted an arrowhead we would also add another Shape with toArrow defined:
            //new go.Shape({  toArrow: "Standard", stroke: null  })
        );


        myDiagram.addDiagramListener("ObjectSingleClicked",
                                     e => {
            //   console.log(e.hm.nl.ii);
            var gotourl = "https://membership.scouts.org.uk/#/teams/allunits/";

            if (e.hm.nl.ii.UnitLevel>97) {
                gotourl+=e.hm.nl.ii.teamunitId } else
                {gotourl+=e.hm.nl.ii.id.substring(0,36);}
            if (e.hm.nl.ii.UnitLevel<20) {
                gotourl+="/unitdetailsau";
            }
            if (e.hm.nl.ii.UnitLevel>97) {
                gotourl+="/teamsau/";
            }
            if (e.hm.nl.ii.UnitLevel==99) {
                gotourl+=(e.hm.nl.ii.parentUnitId).substring(0,36);
            }
            if (e.hm.nl.ii.UnitLevel==98) {
                gotourl+=(e.hm.nl.ii.id).substring(0,36);
            }
            if (e.hm.nl.ii.UnitLevel>97) {
                gotourl+="/teamdashboardaut";
            }
            if (e.hm.nl.ii.UnitLevel==99) {
                gotourl+="/"+(e.hm.nl.ii.id).substring(0,36)+"/subteamdashboardaust";
            }
            window.location.assign(gotourl);
        });


        myDiagram.model =
            new go.TreeModel(
            { nodeParentKeyProperty: "parentUnitId",  // this property refers to the parent node data
             nodeKeyProperty : "id",
             nodeDataArray: mems,
             angle: 90,
             arrangement: true,
             scale: 0.25,
             initialDocumentSpot: go.Spot.Top,
             initialViewportSpot: go.Spot.Top});


    }

    //   var map = {};

    function show_organogram() {
        var h = $( window ).height();
        $("#gmorganogram").dialog ( {modal: false, height: (h*0.75), width: "80%"} );
        try { myDiagram = new go.Diagram("myDiagramDiv",{
            "undoManager.isEnabled": true,
            layout: new go.TreeLayout({ angle: 0, layerSpacing: 35, scale: 0.25 }) // initialAutoScale: go.AutoScale.UniformToFill })
        });} catch {}
        myDiagram.addDiagramListener('InitialLayoutCompleted', function(e) {
            var dia = e.diagram;
            // add height for horizontal scrollbar
            dia.div.style.width = (dia.documentBounds.width + 24) + "px";
            if (( (dia.documentBounds.height + 24)  *  (dia.documentBounds.width))< 20000000 ) {
                dia.div.style.height = (dia.documentBounds.height + 24) + "px";
            } else {  dia.div.style.height = "25000px";}

        });

        orgcounter = 0;
        mems=[];
        orglevel = 0;
        //  debug = true;
        mems = GM_getValue("orgmems", []);

        get_orgdata3(true);
        //   get_orgdata(orgcounter,orglevel);
    }
    var locations = [];

    function get_mapdata5() {
        var errors = [];
        for (var i=1; i<mapunits.length; i++) {
            if (mapunits[i].unitType=="Group") {
                var postcode ="";
                if (mapunits[i].contactDetails.meetingPlaceAddress.postcode !="") {
                    postcode = mapunits[i].contactDetails.meetingPlaceAddress.postcode
                } else {
                    if (mapunits[i].contactDetails.correspondenceAddress.postcode !="") {
                        postcode = mapunits[i].contactDetails.correspondenceAddress.postcode
                    }
                }
                if (postcode!="") {
                    var loc = locations.find(e=>e.query == postcode);
                    if (loc.result!=null){

                        var marker = L.marker([loc.result.latitude,loc.result.longitude],{icon: mappin}).addTo(map).bindPopup(mapunits[i].name).openPopup();
                    } else { errors.push("Invalid postcode for "+mapunits[i].name+" "+postcode);}
                } else { errors.push("No postcode for "+mapunits[i].name);}
            }
        }
        $("#gmerrors")[0].innerHTML ="";
        for (i=0;i<errors.length;i++) {
            $("#gmerrors")[0].innerHTML += errors[i]+"<br>";
        }
    }

    function get_postcodes(index) {
        var subpostcodes = [];
        for(var i=index*100;i<(index*100+100)&&i<postcodes.length;i++) {
            subpostcodes.push(postcodes[i]);
        }
        console.log(subpostcodes);
        var pi = {};
        pi.postcodes = subpostcodes;
        var datapass = JSON.stringify(pi);
        GM_xmlhttpRequest({
            method: "POST",
            context: index+1,
            url: "https://api.postcodes.io/postcodes?filter=postcode,longitude,latitude",
            data: datapass,
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                "Accept": "application/json, text/plain, */*"
            },

            onload: function(response) { //  console.log(response);console.log(count);
                //    console.log(response.responseText);
                locations  = locations.concat(JSON.parse(response.responseText).result);
                $("#gmmapstatus")[0].innerHTML = locations.length+" Postcodes read of "+postcodes.length;
                if ((response.context)*100<postcodes.length) {get_postcodes(response.context)} else {get_mapdata5()};
            },
            onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
            onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
        });

    }

    function get_mapdata4() {
        console.log(mapunits);
        postcodes=[];
        for (var i=0;i<mapunits.length;i++) {
            if (mapunits[i].contactDetails.meetingPlaceAddress.postcode !="") { postcodes.push(mapunits[i].contactDetails.meetingPlaceAddress.postcode) } else {
                if (mapunits[i].contactDetails.correspondenceAddress.postcode !="") {postcodes.push(mapunits[i].contactDetails.correspondenceAddress.postcode) }
            }
        }


        get_postcodes(0);

    }
    function get_mapdata3() {
        debug = true;
        // Remove verything but sections
        console.log(mapmems);
        mapunits = [];
        //https://tsa-memportal-prod-fun01.azurewebsites.net/api/GetUnitDetailAsync
        //unitId: "8942fe62-d032-4651-9317-7a9b929e710d"
        for (var i=1; i<mapmems.length; i++) {
            if (mapmems[i].unitType=="Group") {
                console.log("Unit "+i+" "+mapmems[i].unitName);
                var datapass = '{"unitId": "'+mapmems[i].id+'"}';
                GM_xmlhttpRequest({
                    method: "POST",
                    context: i+1,
                    url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/GetUnitDetailAsync",
                    data: datapass,
                    headers: {
                        "Content-Type": "application/json",
                        "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                        "Accept": "application/json, text/plain, */*"
                    },

                    onload: function(response) { //  console.log(response);console.log(count);
                        if (debug) {GM_log("Counter Received "+response.context)}
                        // console.log(JSON.parse(response.responseText).data[1]);
                        var mem = null;
                        try { mem = JSON.parse(response.responseText)} catch {console.log("JSON Error "+response.responseText);}
                        //   mem =(JSON.parse(response.responseText).data);
                        mapunits.push(mem);
                        //    console.log(mapunits);
                        if (mapunits.length==mapmems.filter(e=>e.unitType=="Group").length) {
                            get_mapdata4();
                        }
                        $("#gmmapstatus")[0].innerHTML = mapunits.length+" Groups read of "+mapmems.filter(e=>e.unitType=="Group").length;
                        if (debug) {GM_log("Units mems now has length "+mapmems.length);}
                        if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}
                    },
                    onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
                    onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
                });
            }}
    }
    function get_mapdata2(oc) {
        // Get the Tree
        if (mems.length==0) { $("#gmorgstatus")[0].innerHTML = "No Units found";}

        var max=50;
        var datapass = '{"pagesize":'+max+',"nexttoken":'+oc+',"filter":{"global" : "","globaland":false,"fieldand":true},"unitId":"'+mapmems[0].id+'"}';
        //       console.log(datapass);
        if (debug) {GM_log("Counter for get_Sub Unit "+oc);}
        GM_xmlhttpRequest({
            method: "POST",
            context: oc+1,
            url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/UnitListingAsync",
            data: datapass,
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                "Accept": "application/json, text/plain, */*"
            },

            onload: function(response) { //  console.log(response);console.log(count);
                if (debug) {GM_log("Counter Received "+response.context)}
                // console.log(JSON.parse(response.responseText).data[1]);
                var mem = null;
                try { mem=JSON.parse(response.responseText).data} catch {console.log("JSON Error "+response.responseText);}
                //   mem =(JSON.parse(response.responseText).data);
                if (mem.length>0) {
                    if (debug) {

                    }
                }
                if ((mem.length==0)&&(debug)) {
                    GM_log("GetData: We got an error collecting data for Sub mems page"+response.context);
                }
                if (mem.length!=0) {
                    mapmems = mapmems.concat(mem);
                }
                $("#gmmapstatus")[0].innerHTML = mapmems.length+" Units read";
                if (debug) {GM_log("Units mems now has length "+mapmems.length);}
                if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}
                if ( mem.length ==50) {get_mapdata2(oc+1); } else {missed = 0; mapunits=[]; get_mapdata3()};},
            onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
            onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
        });

    }

    function get_mapdata(oc,ol) {
        // Find the root
        var max = 50;

        un = document.getElementById("gmunitnamem").value;
        var unt= unitsearchvalues[ol];
        if (debug) {GM_log("Unit Request for "+un+" Count "+oc+"/"+ol);}
        if (debug) {
            GM_log("Unit Request for "+un+" :"+ JSON.parse(localStorage.getItem("authInfo")).idToken.substring(1,50));
            var tokexpDate = new Date( JSON.parse(localStorage.getItem("authInfo")).account.idTokenClaims.exp *1000);
        }
        $("#gmmapstatus")[0].innerHTML = "Checking for "+unitsearchtypes[ol]+" "+un;
        var datapass = '{"pagesize":'+max+',"nexttoken":'+oc+',"filter":{"global" : "","globaland":false,"fieldand":true,filterfields:[{ "field" : "'+unt+'", "value": "'+un+'"}]},"unitId":""}';
        if (debug) {GM_log("Counter for get_data "+oc);}
        GM_xmlhttpRequest({
            method: "POST",
            context: ol+1,
            url: "https://tsa-memportal-prod-fun01.azurewebsites.net/api/UnitListingAsync",
            data: datapass,
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                "Accept": "application/json, text/plain, */*"
            },

            onload: function(response) { //  console.log(response);console.log(count);
                if (debug) {GM_log("Counter Recieved "+response.context)}
                // console.log(JSON.parse(response.responseText).data[1]);
                var mem = null;
                try { mem=JSON.parse(response.responseText).data} catch {console.log("JSON Error "+response.responseText);}
                //   mem =(JSON.parse(response.responseText).data);
                if (mem.length>0) {
                    if (debug) {

                    }
                }
                if ((mem.length==0)&&(debug)) {
                    GM_log("GetData: We got an error collecting data for map mems page"+response.context);
                }
                if (mem.length!=0) {
                    mapmems = mapmems.concat(mem);
                }
                if (debug) {GM_log("map mems now has length "+mapmems.length);}


                if (response.status!=200) {GM_log("HTTP error "+response.status); GM_log(JSON.stringify(response));}
                $("#gmmapstatus")[0].innerHTML = "Checking for "+unitsearchtypes[response.context-1]+" "+un;

                if ((ol<unitsearchvalues.length)&&( mapmems.length==0)) {get_mapdata(oc,response.context); } else {

                    var  filter = document.getElementById("gmunitnamem").value.replaceAll("%", "*");
                    let w = filter.replace(/[.+^${}()|[\]\\]/g, '\\$&'); // regexp escape
                    const re = new RegExp(`^${w.replace(/\*/g,'.*').replace(/\?/g,'.')}$`,'i');
                    mapmems = mapmems.filter( function(v) {return re.test(v.unitName)});



                    get_mapdata2(0)};
            },
            onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
            onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
        });

    }

    function show_map() {
        var h = $( window ).height();
        $("#gmmap").dialog ( {modal: false, height: (h*0.75), width: "80%"} );
        try {
            map = L.map('gmmapdiv').setView([51.505, -0.09], 13);
        } catch {}
        L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
            maxZoom: 19,
            attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
        }).addTo(map);

        mappin = L.icon({
            iconUrl: GM_getResourceURL("LeafletPin"),
            shadowUrl: GM_getResourceURL("LeafletShadow"),

            iconSize:     [25, 41], // size of the icon
            shadowSize:   [41, 41], // size of the shadow
            iconAnchor:   [12, 0], // point of the icon which will correspond to marker's location
            shadowAnchor: [0, 0],  // the same for the shadow
            popupAnchor:  [-3, -41] // point from which the popup should open relative to the iconAnchor
        });
        orgcounter = 0;
        mapmems=[];
        orglevel = 0;
        //  debug = true;
        //    mems = GM_getValue("orgmems", []);

        //  get_mapdata3(true);
        //   get_orgdata(orgcounter,orglevel);
    }

    function myCallback(blob) {
        var url = window.URL.createObjectURL(blob);
        var filename = 'myBlobFile.png';

        var a = document.createElement('a');
        a.style = 'display: none';
        a.href = url;
        a.download = filename;

        // IE 11
        if (window.navigator.msSaveBlob !== undefined) {
            window.navigator.msSaveBlob(blob, filename);
            return;
        }
        document.body.appendChild(a);
        requestAnimationFrame(() => {
            a.click();
            window.URL.revokeObjectURL(url);
            document.body.removeChild(a);
        });
    }

    function  down_organogram() {
        var blob = myDiagram.makeImageData({ background: 'white', returnType: 'blob', callback: myCallback,   size: new go.Size(NaN,NaN) });
        console.log(myDiagram.documentBounds.height);
        console.log(myDiagram.documentBounds.width);
    }

    function  save_organogram() {
        GM_setValue("orgmems", mems);
        $("#gmorgstatus")[0].innerHTML = "Hierarchy saved for instant recall";
    }

    function run_organogram() {

        orgcounter = 0;
        mems=[];
        orglevel = 0;
        orglevel = +document.getElementById("gmsorglevel").value;
        //     debug = true;
        get_orgdata(orgcounter,orglevel);

    }

    function run_map() {

        orgcounter = 0;
        mapmems=[];
        orglevel = 0;
        orglevel = +document.getElementById("gmsmaplevel").value;
        get_mapdata(orgcounter,orglevel);

    }

    function clear_cookies() {
        var cookies = document.cookie.split("; ");
        for (var c = 0; c < cookies.length; c++) {
            var d = window.location.hostname.split(".");
            while (d.length > 0) {
                var cookieBase = encodeURIComponent(cookies[c].split(";")[0].split("=")[0]) + '=; expires=Thu, 01-Jan-1970 00:00:01 GMT; domain=' + d.join('.') + ' ;path=';
                var p = location.pathname.split('/');
                document.cookie = cookieBase + '/';
                while (p.length > 0) {
                    document.cookie = cookieBase + p.join('/');
                    p.pop();
                };
                d.shift();
            }
        }
    }

    function run_learn() {
        GM_xmlhttpRequest({
            method: "GET",
            context: 1,
            url: "https://learn.scouts.org.uk/ilp/restapi/lms/dashboards/8/panels/121/results/table?exportAggregations=false&skill=152688,149148,149147,153014,7804027,7802399,116405,10613435&assessmentType=0",
            //    data: datapass,
            headers: {
                "Content-Type": "application/json",
                "Authorization": "Bearer " + JSON.parse(localStorage.getItem("authInfo")).idToken,
                "Accept": "application/json, text/plain, */*"
            },

            onload: function(response) { //  console.log(response);console.log(count);
                console.log(response);
                console.log(xmlToJson(response.responseText));
                console.log(JSON.parse(xmlToJson(response.responseText)));_
            },
            onerror:   function(response) { GM_log("ON Error");GM_log(JSON.stringify(response))},
            onabort:  function(response) { GM_log("ON Abort");GM_log(JSON.stringify(response))}
        });


    }

    function  report_saveXLS() {
        // document.getElementById("gmTabulator2").innerHTML = (tabletabulator.getHtml());
        tabletabulator.download("xlsx", "data.xlsx", {sheetName:"My Data",documentProcessing: function(wb) {

            /* create a new worksheet */
            var ws = XLSX.utils.aoa_to_sheet([
                ["Data Extracted from Scout Membership system"],
                ["Export Date:", new Date()]
            ]);

            /* add to workbook */
            //https://github.com/gitbrent/xlsx-js-style
            XLSX.utils.book_append_sheet(wb, ws, "Extract Information");
            //  wb.Sheets['My Data']['A1'].s = { font: { bold: true, color: { rgb: "FF0000" } } }
            for(var i=7;i<16;i++) {
                //   wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:i})].s =  { alignment: { textRotation: 180 }  }
            }
            var cw = tabletabulator.getColumnLayout();
            var wc = [];
            var cnames = [];
            var cc = 0;
            // Headers
            for (i=0; i<cw.length;i++) {
                if (cw[i].hasOwnProperty("columns")) {
                    var group_visible = false;
                    var group_start = cc;
                    for (var j=0; j<cw[i].columns.length;j++) {
                        if (cw[i].columns[j].visible) {
                            group_visible = true;
                      //  if ( wb.Sheets['My Data'].hasOwnProperty(XLSX.utils.encode_cell({r:1, c:cc}))) {
                            wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s = { font: { bold: true}};
                            if(cw[i].columns[j].hasOwnProperty("headerVertical")) {
                                wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.alignment  =  { textRotation: 180, vertical: "top", horizontal: "center" }  ;
                            } else
                            {  wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.alignment  =  { vertical: "top" }  ;}
                            if(cw[i].columns[j].hasOwnProperty("cssClass")) {
                                switch(cw[i].columns[j].cssClass) {
                                    case 'blue-background':  wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.fill = { fgColor: { rgb: "ADD8E6" } };
                                        break;
                                    case 'yellow-background':
                                        wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.fill = { fgColor: { rgb: "FFFFE0" } };
                                        break;
                                    case 'orange-background':
                                        wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.fill = { fgColor: { rgb: "FFDAB9" } };
                                        break;
                                }
                            }

                            wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.border = { top: {style: "thin", color: { rgb: "000000" }}}
                            wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.border.bottom = {style: "thin", color: { rgb: "000000" }}
                            wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.border.left = {style: "thin", color: { rgb: "000000" }}
                            wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.border.right = {style: "thin", color: { rgb: "000000" }}
                            wc.push({width: cw[i].columns[j].width*.15});
                            cnames.push(cw[i].columns[j].field);
                            cc++;
                     //   }
                    }
                    if ((group_visible)&&(wb.Sheets['My Data'].hasOwnProperty(XLSX.utils.encode_cell({r:0, c:group_start})))) {
                        // Group Header
                        wb.Sheets['My Data'][XLSX.utils.encode_cell({r:0, c:group_start})].s = { font: { bold: true}};
                        wb.Sheets['My Data'][XLSX.utils.encode_cell({r:0, c:group_start})].s.fill = { fgColor: { rgb: "DCDCDC" } };
                    }
                }
            } else {
                if (cw[i].visible){
                    wc.push({width: cw[i].width*.15});
                    cnames.push(cw[i].field);
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:0, c:cc})].s = { font: { bold: true}, alignment: {vertical:"top"}};
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s = { font: { bold: true}, alignment: {vertical:"top"}};
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:0, c:cc})].s.border = { top: {style: "thin", color: { rgb: "000000" }}}
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:0, c:cc})].s.border.bottom = {style: "thin", color: { rgb: "000000" }}
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:0, c:cc})].s.border.left = {style: "thin", color: { rgb: "000000" }}
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:0, c:cc})].s.border.right = {style: "thin", color: { rgb: "000000" }}
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.border = { top: {style: "thin", color: { rgb: "000000" }}}
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.border.bottom = {style: "thin", color: { rgb: "000000" }}
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.border.left = {style: "thin", color: { rgb: "000000" }}
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:1, c:cc})].s.border.right = {style: "thin", color: { rgb: "000000" }}
                    cc++;
                   }
            }
            // cc++;
        }
                                                      wb.Sheets['My Data']["!cols"]= wc;
        // Color Cells for ticks etc
        for (i=0;i<tabletabulator.getRows().length;i++) {
            for (j=0; j<wc.length; j++) {
                if (wb.Sheets['My Data'].hasOwnProperty(XLSX.utils.encode_cell({r:i+2, c:j}))) {
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s = {};
                    if (wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].hasOwnProperty("v")){
                        if(wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].v=="✅") {
                            wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s = { font: {color: { rgb: "00FF00" } },  alignment: { horizontal: "center" }  }
                        }
                        if(wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].v=="❌") {
                            wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s = { font: {color: { rgb: "FF0000" } },  alignment: { horizontal: "center" } }
                        }
                        if(wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].v=="✖") {
                            wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s = { font: {color: { rgb: "000000" } },  alignment: { horizontal: "center" } }
                        }
                        if(wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].v=="✔") {
                            wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s = { font: {color: { rgb: "000000" } },  alignment: { horizontal: "center" } }
                        }
                        if(wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].v=="⚡") {
                            wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s = { font: {color: { rgb: "FFA500" } },  alignment: { horizontal: "center" } }
                        }
                        if ((cnames[j].indexOf("dbs")==0)||(cnames[j].indexOf("safeguarding")==0)||(cnames[j].indexOf("safety")==0)||(cnames[j].indexOf("firstresponse")==0)) {
                            var ci =-1;
                            if (cnames[j].indexOf("_")==-1) {
                                ci = cnames.findIndex(e=>e==cnames[j]+"_expirydays"); ci = -1; // No colour for Icon column...
                            } else
                            { ci = cnames.findIndex(e=>e== cnames[j].substring(0, cnames[j].indexOf("_"))+"_expirydays");}
                            var expdays = -1;
                            if (ci>-1) {
                                var cs = cnames.findIndex(e=>e== cnames[j].substring(0, cnames[j].indexOf("_")));
                                var css = wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2,c:cs})];
                                if ("✅❌✔⚡✖".includes(css.v)) {
                                    if (wb.Sheets['My Data'].hasOwnProperty(XLSX.utils.encode_cell({r:i+2, c:ci}))) {
                                        if (wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:ci})].hasOwnProperty("v")) {
                                            expdays = wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:ci})].v;
                                        }
                                    }
                                    if(document.getElementById("gmcolor").checked) {
                                        for(cloop=0;cloop<4;cloop++) {
                                            if ((expdays<=daylimits[cloop])&&(expdays>daylimits[cloop+1])) {
                                                wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s.fill =  {fgColor: { rgb:  colors[cloop].substring(1,7)  }}
                                                // td.style.backgroundColor = colors[cloop];
                                                // td.style.textColor = colorsT[cloop];

                                            }}}
                                }}


                        }
                    }
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s.border = { top: {style: "thin", color: { rgb: "000000" }}}
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s.border.bottom = {style: "thin", color: { rgb: "000000" }}
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s.border.left = {style: "thin", color: { rgb: "000000" }}
                    wb.Sheets['My Data'][XLSX.utils.encode_cell({r:i+2, c:j})].s.border.right ={style: "thin", color: { rgb: "000000" }}
                }}
        }
        return wb;
    }});
}

function report_download() {
    tabletabulator.getHtml();
    //    const clipboardItem = new ClipboardItem({
    //        'text/html': new Blob([document.getElementById("GMourreport").outerHTML], { type: 'text/html' }),
    //        'text/plain': new Blob([document.getElementById("GMourreport").innerText], { type: 'text/plain' })
    //    });
    const clipboardItem = new ClipboardItem({
        'text/html': new Blob([tabletabulator.getHtml()], { type: 'text/html' }),
        'text/plain': new Blob([tabletabulator.getHtml().innerText], { type: 'text/plain' })
    });
    navigator.clipboard.write([clipboardItem]);
}
// Navigation Watcher
/*
    navigation.addEventListener("navigate", e => {
        console.log("EventLstener");
        console.log(e);

        if (document.getElementById("quick_nav")!=null){console.log("remove");document.getElementById("quick_nav").innerHTML ="";}

        setTimeout(function() {

        }, 2000);

    }) // Event listener
*/
/*   setTimeout(function() {
        console.log("New Page");
        //    dopage(); Deactive Unit/Teams
    }, 2000);
 */
// Your code here...
var newHTML         = document.createElement ('div');
// <p>Exclude Welcome Information <input  type="checkbox" id="gmexc" name="gmtexc" /><p> \
//<p><input   type="checkbox" id="gmtraffic" name="gmtraffic" /> Show 90 Day Traffic LIghts  \
newHTML.innerHTML   = '             \
<div id="gmpopup" title="Roles Report">   \
<p>      \
Unit <input   type="text" list="myUnits"  id="gmunitname" size=30 name="gmunitname"  placeholder="Enter Unit Name (% =wildcard)"/><datalist id="myUnits"></datalist>  <button id="gmRunReport">Run Report</button> <button id="gmFilterReport">Refresh Display</button></p> \
<p>Include Roles for matching Unit Only <input  type="checkbox" id="gmteamonly" name="gmteamonly" /> Exact Match <input   type="checkbox" id="gmexact" name="gmexact" /> <p> \
<p>Name Filter <input   type="text" id="gmpersonname" name="gmpersonname" /> Team Filter <input   type="text" id="gmteamname" name="gmteamname" /><p> \
<input   type="checkbox" id="gmmissing" name="gmmissing" /> Show volunteers missing  Welcome elements only \
<p><input   type="checkbox" id="gmwelcome" name="gmwelcome" /> Exclude Role Audit</p> \
<p> <input   type="checkbox" id="gmhelper" name="gmhelper" /> Exclude Helpers/Holding Roles <input   type="checkbox" id="gmclosed" name="gmclosed" checked/> Exclude Closed Roles <p> \
<p><input   type="checkbox" id="gmlearning" name="gmlearning" /> Show Detailed learning  \
<input   type="checkbox" id="gmdates" name="gmdates" /> Show Expiry Dates \
<input   type="checkbox" id="gmdays" name="gmdays" /> Show Days to Expiry </p> \
<p><input   type="checkbox" id="gmdbs" name="gmdbs" /> Show DBS Information <input   type="checkbox" id="gmdbsip" name="gmdbsip" /> Show DBS Status</p>  \
<p><input   type="checkbox" id="gmnumber" name="gmnumber" /> Show Membership Number<input   type="checkbox" id="gmcolor" name="gmcolor" /> Use Color</p>\
<strongb>Run Status : </strong> <span id="gmState">Enter Unit Name above (can include % wildcard) and run report</span>  \
<p>Showing All Roles for members of <span id="gmUnit"></span></p>       \
<span id="gmTablex"><button id="gmXLS">Copy to Clipboard</button></span>  \
<span id="gmTablep"><button id="gmPrint">Print</button></span>  \
<span id="gmTablex2"><button id="gmSaveReport">Save XLS</button></span>  \
<span id="gmTable">Report will appear here!</span>  \
<div id="gmTabulator"></div> \
<div id="gmTabulator2"><p>Key: ✅ Required and complete ❌ Required ⚡Complete but near expiry ✔ Not required but complete ✖ Not required but expired </p></div> \
<p><input   type="checkbox" id="gmdebug" name="gmdebug" /> Debug Info - usually this should be off</p> \
<p><button id="gmsavecolors">Save Colors</button> <button id="gmresetcolors">Reset Colors</button></p> \
<div id="htmlset" style=""></div> \
</div> ';
    //<p><input   type="checkbox" id="gmexplorer" name="gmexplorer" /> Experimental Data Collection</p> \
// Add Color Selectors
document.body.appendChild (newHTML);
var colors_JSON = GM_getValue("colors", JSON.stringify(defcolors));
var colorsT_JSON = GM_getValue("colorsT", JSON.stringify(defcolorsT));
colors = JSON.parse(colors_JSON);
colorsT = JSON.parse(colorsT_JSON);
var  HTMLSettings         = document.createElement ('div');
for (var cloop=0;cloop<4;cloop+=1){
    HTMLSettings.innerHTML   += '<p><span style="display: inline-block; width :75px">'+colormeanings[cloop]+'</span> '+'<input type="color" id="back'+cloop+'" name="back'+cloop+'" value="'+colors[cloop]+'" />'+'<span> <input type="color" id="fore'+cloop+'" name="fore'+cloop+'" value="'+colorsT[cloop]+'" /></span>   <span id="cpreview'+cloop+'">1000</span></p>';
}
document.getElementById("htmlset").appendChild (HTMLSettings);
for (cloop=0;cloop<4;cloop+=1){
    document.getElementById("back"+cloop).addEventListener("input", updateFirstC, false);
    document.getElementById("back"+cloop).addEventListener("change", watchColorPicker, false);
    document.getElementById("fore"+cloop).addEventListener("input", updateFirstC, false);
    document.getElementById("fore"+cloop).addEventListener("change", watchColorPicker, false);
    document.getElementById("cpreview"+cloop).style.backgroundColor = colors[cloop];
    document.getElementById("cpreview"+cloop).style.color = colorsT[cloop];
    document.getElementById("cpreview"+cloop).style.padding = "10px";
}
$("#gmpopup").hide();
newHTML         = document.createElement ('div');
newHTML.innerHTML   = '             \
<div id="gmorganogram" title="Organogram">Unit <input   type="text" id="gmunitnameo" size=30 name="gmunitnameo"  placeholder="Enter Unit Name (% =wildcard)"/> \
Start At <select id="gmsorglevel" name="gmsorglevel" /><option value="0">Organisation</option><option value="1">Country</option><option value="2">Region</option><option value="3">County</option><option value="4">County Sections</option> <option value="5">District</option><option value="6">District Sections</option><option value="7">Group</option><option value="8">Group Section</option></select> \
<button id="gmRunOrg">Chart Data</button><button id="gmSaveOrg">Save</button> <button id="gmDownOrg">Download</button>\
<p><input   type="checkbox" id="gmnoteams" name="gmnoteams" /> Show Units Only <input   type="checkbox" id="gmnosteams" name="gmnosteams" /> Exclude section Teams</p> \
<p>Display down to: <select id="gmorglevel" name="gmorglevel" /> <option value="1">Group Section</option><option value="2">Group</option><option value="3">District Sections</option><option value="4">District</option><option value="5">County Sections</option><option value="6">County</option><option value="7">Region</option><option value="8">Country</option><option value="9">Organisation</option></select> \
<p> Status : <span id="gmorgstatus"></span></P> \
<div id="myDiagramDiv" style="width:1000px; height:40000px; background-color: #DAE4E4;"></div><p> <div style="height:1000px;display:none" id="map"></div> </p></div>   \
';
document.body.appendChild (newHTML);
$("#gmorganogram").hide();

newHTML         = document.createElement ('div');
newHTML.innerHTML   = '             \
<div id="gmmap" title="Map">Unit <input   type="text" id="gmunitnamem" size=30 name="gmunitnamem"  placeholder="Enter Unit Name (% =wildcard)"/> \
Start At <select id="gmsmaplevel" name="gmsmaplevel" /><option value="0">Organisation</option><option value="1">Country</option><option value="2">Region</option><option value="3">County</option><option value="4">County Sections</option> <option value="5">District</option><option value="6">District Sections</option><option value="7">Group</option><option value="8">Group Section</option></select> \
<button id="gmRunMap">Map Data</button>\
<p> Status : <span id="gmmapstatus"></span></P> \
<p> <div style="height:1000px" id="gmmapdiv"></div> </p><p>Errors</p><p><div id="gmerrors"></div><div>Key</div><div id="gmkey"></div>\
</div>   \
';
document.body.appendChild (newHTML);
$("#gmmap").hide();
var iconSet1    = GM_getResourceURL ("IconSet1");
var iconSet2    = GM_getResourceURL ("IconSet2");
//  var iconSet3    = GM_getResourceURL ("LeafletPin");
//  var iconSet4    = GM_getResourceURL ("LeafletShadow");
var jqUI_CssSrc = GM_getResourceText ("jqUI_CSS");
var leaf_Css = GM_getResourceText("leaf");
var tabulator_css = GM_getResourceText("tabulator");
jqUI_CssSrc     = jqUI_CssSrc.replace (/url\(images\/ui\-bg_.*00\.png\)/g, "");
jqUI_CssSrc     = jqUI_CssSrc.replace (/images\/ui-icons_222222_256x240\.png/g, iconSet1);
jqUI_CssSrc     = jqUI_CssSrc.replace (/images\/ui-icons_454545_256x240\.png/g, iconSet2);
//  var testing = GM_info();
GM_addStyle (jqUI_CssSrc);
GM_addStyle (leaf_Css);
GM_addStyle (tabulator_css );
addButton("Role Report", report_onclick2, "","T");
addButton("Clear Cookies", clear_cookies,"", "B");
addButton("Organogram", show_organogram,"", "C");
//   addButton("Map Groups", run_map,"", "D");

document.getElementById("gmRunReport").onclick = report_onclick;
document.getElementById("gmFilterReport").onclick = report_filter;
document.getElementById("gmSaveReport").onclick = report_saveXLS;
document.getElementById("gmsavecolors").onclick = savecolors;
document.getElementById("gmresetcolors").onclick = resetcolors;
document.getElementById("gmXLS").onclick = report_download;
document.getElementById("gmPrint").onclick = report_print;
document.getElementById("gmunitname").value = un;
document.getElementById("gmpopup").style.fontSize = "10pt";
document.getElementById("gmorganogram").style.fontSize = "10pt";
document.getElementById("gmmap").style.fontSize = "10pt";
document.getElementById("gmRunOrg").onclick = run_organogram;
document.getElementById("gmSaveOrg").onclick = save_organogram;
document.getElementById("gmDownOrg").onclick = down_organogram;
document.getElementById("gmRunMap").onclick = run_map;

var blkrs = GM_info.script.options.override.use_blockers;
if (blkrs.length>0) {
    var clicked = GM_notification({
        text: "There maybe an issue with your XHR blacklist this may stop things running - check the plugin settings",
        title: "Scout Membership Plugin",
        url: 'https:/example.com/',
        onclick: (event) => {
            //The userscript is still running, so don't open example.com
            event.preventDefault();
            // Display an alert message instead
            //    alert('I was clicked!')
        }
    });
}

// Get MyUnits
// https://tsa-memportal-prod-fun01.azurewebsites.net/api/MyUnitsAsync

//   var datapass = '{"pagesize":100,"sort":{"field":"unitName","type":"asc"}}';





GM_addStyle ('.fa-check-square:before { content: "✅";   }' );
GM_addStyle ('.fa-square:before { content: "🟩";   }' );
GM_addStyle('.tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-sorter-element.blue-background {background : #ADD8E6 }');
GM_addStyle('.tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-sorter-element.yellow-background {background : #FFFFE0 }');
GM_addStyle('.tabulator-col.tabulator-sortable.tabulator-col-sorter-element.orange-background {background : #FFDAB9 }');
GM_addStyle('th.blue-background {    writing-mode: vertical-lr;    background-color: lightcyan; }');
GM_addStyle('th.yellow-background {    writing-mode: vertical-lr;    background-color: lightgoldenrodyellow; }');
GM_addStyle('th.orange-background {    writing-mode: vertical-lr;    background-color: salmon; }');
GM_addStyle('table.tabulator-print-table, tr, th, td {    border: solid;}');

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址