3
\$\begingroup\$

We have the following code which takes about 5 seconds in a modern computer, however the application is also executed on old PCs with Windows XP, and in there it takes 5 minutes to load. I know the old pcs are slow, but 5 minutes its just ridiculous slow, this code was developed by someone else and now we have to improve it.

Any suggestion is welcome.

<script type="text/javascript">
$(document).ready(function () {
    // limpia la localStorage con cambios anteriores
    if (sessionStorage.getItem("limp")==null){
        localStorage.clear();
        sessionStorage.setItem("limp","1");
    }
    var scriptbase = "/_layouts/15/";
    $.getScript(scriptbase + "SP.Runtime.js");
    ExecuteOrDelayUntilScriptLoaded(re, "SP.Runtime.js");
})

function re (){
    $("div[webpartid=608c8fe4-e5db-49de-8550-840b1706ba1c]").hide();
    var scriptbase = "/_layouts/15/";
    $.getScript(scriptbase + "SP.js", function(){

        $.getScript(scriptbase + "SP.Taxonomy.js");

    });
    SetFullScreenMode(true);
    PreventDefaultNavigation();
}

var metaFieldName = "SIG"; //Change to your site column names
var lvl; //Handle the different pages
var anterior;
var filterOutput1;
var TaxConfiguration = {

    SspId: "",
    GroupId: "",
    TermSetId: "",
    Configuration: "",
    FieldName: "",
    ParseConfiguration: function () {

        xmlDoc = $.parseXML(this.Configuration);
        xml = $(xmlDoc);

        var fieldDef = xml.find("Field");
        this.FieldName = $(fieldDef).attr("DisplayName");

        var properties = xml.find("Property");

        for (i = 0; i < properties.length; i++) {

            propertyName = properties[i].firstChild.textContent == undefined ? 
                                properties[i].firstChild.text : properties[i].firstChild.textContent;
            propertyValue = properties[i].lastChild.textContent == undefined ?
                                properties[i].lastChild.text : properties[i].lastChild.textContent;

            if (propertyName == propertyValue) {
                propertyValue = "";
            }

            switch (propertyName) {
                case "SspId":
                    this.SspId = propertyValue;
                    break;
                case "GroupId":
                    this.GroupId = propertyValue;
                    break;
                case "TermSetId":
                    this.TermSetId = propertyValue;
                    break;
            }
        }
    }
}

function maxiFilter() {
    if ($("#maxFilter").html()==' - Ocultar elementos') {
        $("#maxFilter").html(" + Ver elementos");
    } else {
        $("#maxFilter").html(" - Ocultar elementos");
    }
    if (sessionStorage.getItem('lvl')==2){
        $("#start").slideToggle();
    }else {
        $("#filter").slideToggle();
    }
}

function seeBread () {
    var bread = sessionStorage.getItem("bread");
    if (bread != null){
        var breadElements = bread.split("...");
        var output = "";
        var content;
        for(i=0;i<breadElements.length-1;i++)
        {
            content = '"' + breadElements[i].split(",")[1] + '"';
            if (breadElements[i].split(",")[1] == "final") {
                output += "<p class='separator'> > </p><span id='final' onclick='irAtrasBread(" + content + ", true)'>" + breadElements[i].split(",")[0] + "</span>";
            }else {
                output += "<p class='separator'> > </p><a onclick='irAtrasBread(" + content + ", true)'>" + breadElements[i].split(",")[0] + "</a>";
            }
        };
    }
    else {
        output = '';
    }
    var start = "'" + 'start' + "'";
    $("#bread").html('<p class="separator"> > </p><a href="#" onclick="irAtrasBread('+start + ',true)">Mapa de procesos</a>' + output);
}

function irAtras () {
    if (sessionStorage.getItem("lvl")=="3") {
        backToStart();
    }else{
        var a = $("#0").attr("data-anterior");
        var txtHtml = sessionStorage.getItem(a);
        mostrarAtras(txtHtml);
        irAtrasBread(a, false);
    }   
}

function backToStart () {
    if (sessionStorage.getItem("lvl")=="3") {
        $("#filter").hide();
        $("#maxFilter").hide();
        $("#bread").hide();
        $("#atras").hide();
        $("#start").html(sessionStorage.getItem("start"));
        $("#start").show();
        sessionStorage.setItem("lvl", 2);
        sessionStorage.setItem("lastLvl", "start");
        sessionStorage.removeItem("bread");
    }
}

function irAtrasBread (cookieName, direct){
    $("#maxFilter").hide();
    $("#maxFilter").html(" - Ocultar elementos");
    $("div[webpartid=608c8fe4-e5db-49de-8550-840b1706ba1c]").hide();
    if (sessionStorage.getItem("lvl")=="3" && direct == true) {
        backToStart();
    }else{
        var cookie = sessionStorage.getItem("bread");
        var breadElements = cookie.split("...");
        var bread = "";
        var state = 0;
        for(i=0;i<breadElements.length-1;i++)
        {
            if(breadElements[i].split(",")[1] != cookieName){
                bread += breadElements[i].split(",")[0] + "," + breadElements[i].split(",")[1] + "...";
            } else {
                state = i;
                i = breadElements.length;
            }
        }
        var stateLVL = (breadElements.length-1)-state;
        if(direct&&cookieName!="final"){
            setLvl(stateLVL, "restar");
            if(lvl==2){
                $("#atras").hide();
            }
        }
        if (cookieName!="final") {
            sessionStorage.setItem("lastLvl", cookieName);  
            var cookieValue = sessionStorage.getItem(cookieName);
            if (lvl == 2) {
                $("#filter").hide();
                $("#bread").hide();
                $("#start").html(cookieValue);
                $("#start").show();

            }else{
                seeBread();
                $("#bread").show();
                $("#start").hide();
                $("#filter").html(cookieValue); 
                $("#filter").show();
            }
        };

        sessionStorage.setItem("bread", bread);

    }
}

function setLvl (num, task) {
    lvl = sessionStorage.getItem("lvl");
    if (task == "restar"){
        lvl = lvl - num;
    }else if (task == "sumar"){
        lvl = parseInt(lvl) + num;
    }
    sessionStorage.setItem("lvl", lvl);
}

function mostrarAtras (txtHtml) {
    $("#filter").html(txtHtml);
    setLvl(1, "restar");
    if (lvl == 1) {
        $("#atras").hide(); 
    }
    $("div[webpartid=608c8fe4-e5db-49de-8550-840b1706ba1c]").hide();
}

function GetTermsRecursive(sspId, termSetId){
    var terms = new Array();
    sessionStorage.setItem("SspId", taxconfig.SspId);
    sessionStorage.setItem("TermSetId", taxconfig.TermSetId);
    sessionStorage.setItem("FieldName", taxconfig.FieldName);
    $().SPServices({
        operation: "GetChildTermsInTermSet",
        async: false,
        sspId: sspId,
        termSetId: termSetId,
        lcid: 1033,
        completefunc: GetDataInit
        });
}

function GetChildTerms(sspId, termSetId, roottermId, termName){
    setLvl(1,"sumar");
    $("#bread").show();
    GetChildTermsRecursive(sspId, termSetId, roottermId, termName);
}

function GetChildTermsRecursive(sspId, termSetId, roottermId, termName){
    var cookie = sessionStorage.getItem(cookieName);
    var aux = sessionStorage.getItem("bread");
    $("#filter").show();
    lvl = sessionStorage.getItem("lvl");
    if (sessionStorage.getItem("lvl")>3) {
        var cookieName = "lvl" + lvl + "_" + roottermId;
        var a = document.getElementById('0');
        anterior = a.getAttribute("data-lvl");
        $("#start").hide();
        sessionStorage.setItem("lastLvl", cookieName);
    } else if (sessionStorage.getItem("lvl")==3){
        sessionStorage.setItem("bread", termName + ",start...");
        $("#start").hide();
    }
    $("div[webpartid=608c8fe4-e5db-49de-8550-840b1706ba1c]").hide();
    if(cookie != undefined){
        var aux = sessionStorage.getItem("bread");
        $("#filter").html(cookie);
        $("#atras").show();
    } else {
        var terms = new Array();
        $("#filter").html('');
        $("#loadingOptions").show();
        $().SPServices({
            operation: "GetChildTermsInTerm",
            async: false,
            sspId: sspId,
            termId: roottermId,
            termSetId: termSetId,
            lcid: 1033,
            completefunc: GetData
            });
        if (lvl > "3") {
            a = document.getElementById('0');
            var actual = a.getAttribute("data-lvl");
        }
    }
    if (lvl > "3") {
        sessionStorage.setItem("bread", aux + termName + "," + anterior + "...");
    }
    seeBread();
    return terms;    
}

function GetDataInit(xData, Status) 
{
    if(Status == "success")
    {
        terms = new Array();
        xmlData = xData;        
        // Fix for different XML parsing in IE and Chrome
        termsContent = $.parseXML(xmlData.responseText).firstChild.textContent == undefined ?
                            $.parseXML(xmlData.responseText).text : 
                            $.parseXML(xmlData.responseText).firstChild.textContent;
        termsXML = $.parseXML(termsContent);
        $termsXML = $(termsXML);
        var childTerms = $termsXML.find("T");
        for(j = 0; j < 3; j++)
        {
            termName = $(childTerms[j]).find("TL");
            hasChildTermsXml = $(childTerms[j]).find("TM");

            // request if child terms are avaliable
            hasChildTerms = $(hasChildTermsXml).attr("a69");

            var tsTerm = new Object();

            // Requesting actual term id
            tsTerm.termId = $(childTerms[j]).attr("a9");

            // Requestion term value ID
            tsTerm.valueID = $(childTerms[j]).attr("a1000");
            // Requesting term name
            tsTerm.termName = termName.attr("a32");

            sessionStorage.setItem("cat", tsTerm.termId);
            //sessionStorage.setItem("lvl",2);
            GetChildTermsRecursive(sessionStorage.getItem("SspId"), sessionStorage.getItem("TermSetId"), tsTerm.termId, tsTerm.termName);
            //sessionStorage.setItem("lvl",2);
            sessionStorage.setItem("start", $("#start").html());
            sessionStorage.setItem("start", $("#start").html());
            sessionStorage.setItem("lastLvl", "start");
        }

    }
}


function GetData(xData, Status) 
{
    if(Status == "success")
    {
        var hasParent = 0;
        var cons = 0;
        var filterOutput = "";

        terms = new Array();
        xmlData = xData;        
        // Fix for different XML parsing in IE and Chrome
        termsContent = $.parseXML(xmlData.responseText).firstChild.textContent == undefined ?
                            $.parseXML(xmlData.responseText).text : 
                            $.parseXML(xmlData.responseText).firstChild.textContent;
        termsXML = $.parseXML(termsContent);
        $termsXML = $(termsXML);
        var childTerms = $termsXML.find("T");
        parentTermId = null;
        GUID = null;

        lvl = sessionStorage.getItem("lvl");
        $("#loadingOptions").hide();
        $("#mensaje").show();
        for(i = 0; i < childTerms.length; i++)
        {

            termName = $(childTerms[i]).find("TL");
            hasChildTermsXml = $(childTerms[i]).find("TM");

            // request if child terms are avaliable
            hasChildTerms = $(hasChildTermsXml).attr("a69");

            var tsTerm = new Object();

            // Requesting actual term id
            tsTerm.termId = $(childTerms[i]).attr("a9");

            // Requestion term value ID
            tsTerm.valueID = $(childTerms[i]).attr("a1000");
            // Requesting term name
            tsTerm.termName = termName.attr("a32");

            // Setting Parent Term ID
            parentTermId = $(hasChildTermsXml).attr("a25");
            if (GUID == null){
                GUID = parentTermId;    
            }

            if (parentTermId != undefined) {
                hasParent = parentTermId;   
            }

            var sspId = '"'+sessionStorage.getItem("SspId")+'"';
            var termSetId = '"'+sessionStorage.getItem("TermSetId")+'"';
            var termId = '"'+tsTerm.termId+'"';
            var currentPage = $(location).attr('href').split("?")[0];
            var termName1 = '"' + tsTerm.termName + '"';
            var sumar = "sumar";
            var GUID1 = '"'+GUID+'"';
            if (hasChildTerms == "true"){
                filterOutput += "<a id='" + cons + "' data-lvl='lvl" + lvl + "_" + GUID +"' data-anterior='" + anterior + "' onclick='GetChildTerms(" + sspId + "," + termSetId + "," + termId + "," + termName1 + ")'><div class='text'><p>"+
                    tsTerm.termName+"</p></div></a>";               
            } else {
                filterOutput += "<a id='" + cons + "' onclick='setBread(" + termName1 + "," + lvl + "," + GUID1 + ")' data-lvl='lvl" + lvl + "_" + GUID +"' data-anterior='" + anterior + "' href='" + currentPage + "?FilterField1=" + sessionStorage.getItem("FieldName") + "&FilterValue1=" + tsTerm.valueID + "&FilterLookupId1=1'><div class='text'><p>"+tsTerm.termName+"</p></div></a>";
            }   

            terms[i] = tsTerm;
            cons++;
        }
        //onclick='alert(" + '"s"' +")'
        if (lvl == "2") {
            var cat = sessionStorage.getItem("cat");
            if (cat == "9f5fb2bc-c5b0-49d4-bb7d-c662ab91bd9d") {
                $("#direccion").html($("#direccion").html()+filterOutput);
            } else if (cat == "1b1c4e39-c642-42ad-b279-7ff5c9373c43") {
                $("#core").html($("#core").html()+filterOutput);
            } else if (cat == "a334da4e-1606-493b-8776-957c60d592f5") {
                $("#soporte").html($("#soporte").html()+filterOutput);
            }
        } else{
            currentFilter = $("#filter").html();
            $("#filter").html(currentFilter+filterOutput);
            if (hasParent != undefined && hasParent != 0) {
                $("#atras").show();
            };
        }
        sessionStorage.setItem("lvl" + lvl + "_" + hasParent, filterOutput);
        sessionStorage.setItem("lastLvl", "lvl" + lvl + "_" + hasParent);
    }
}

function setBread (termName, lvl, GUID) {
    sessionStorage.setItem("bread", sessionStorage.getItem("bread") + termName + ",final...");
}

var fieldCollection;
var metaField = null;
var taxconfig = TaxConfiguration;

ExecuteOrDelayUntilScriptLoaded(getMetaField, "SP.Taxonomy.js");

function getMetaField() {
    var lastLvl;
    $("#atras").hide();
    //Saca de la cookie el último grupo
    if(location.href.split('.aspx')[1]!=''){
        $("div[webpartid=608c8fe4-e5db-49de-8550-840b1706ba1c]").show();
        lastLvl = sessionStorage.getItem("lastLvl");
        var lastHtml = sessionStorage.getItem(lastLvl);
        $("#loadingOptions").hide();
        $("#mensaje").show();
        $("#maxFilter").show();
        if(lastLvl != "start") {
            $("#start").hide();
            $("#filter").html(lastHtml);
            $("#atras").show(); 
            $("#bread").show();
        }else {
            $("#bread").hide();
            $("#start").html(lastHtml);
            $("#start").show();
        }
        seeBread ();
    } else {
        var start = sessionStorage.getItem("start");
        if (start!=null) {
            $("#loadingOptions").hide();
            $("#filter").hide();
            $("#bread").hide();
            $("#bread").hide();
            $("#maxFilter").hide();
            $("#start").html(start);
            $("#start").show();
            sessionStorage.setItem("lvl", 2);   
            sessionStorage.setItem("lastLvl","start");
            sessionStorage.removeItem("bread");
        }else{
            context = SP.ClientContext.get_current();
            var web = context.get_web(); 
            var site = context.get_site();
            var rootWeb = site.get_rootWeb();
            fieldCollection = rootWeb.get_fields();
            metaField = fieldCollection.getByInternalNameOrTitle("SIG");
            context.load(fieldCollection);
            context.load(metaField);
            context.executeQueryAsync(
                Function.createDelegate(this, this.OnLoadSuccess),
                Function.createDelegate(this, this.OnLoadFailed)
                );
        }
    }

}

function OnLoadSuccess(sender, args) {
    var fieldInfo = '';
    var fieldschema = metaField.get_schemaXml();
    taxconfig.Configuration = fieldschema;
    taxconfig.ParseConfiguration();
    try{
        var taxsspId = taxconfig.SspId;
        var termSetId = taxconfig.TermSetId;
        sessionStorage.setItem("lvl", 2);   
        sessionStorage.setItem("lastLvl","start");
        sessionStorage.removeItem("bread");
        $("#atras").hide(); 
        $("#bread").hide(); 
        GetTermsRecursive(taxconfig.SspId, taxconfig.TermSetId);
        $("#start").show(); 
    } catch (e) {
        console.log(e);
    }   
}

function OnLoadFailed(sender, args) {
    $("#filter").html('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}

\$\endgroup\$
3
  • \$\begingroup\$ Welcome to Code Review! Looks like quite the spaghetti code you have been left to maintain. I'm sure we will find ways to help you improve it! \$\endgroup\$
    – Phrancis
    Dec 18, 2014 at 15:49
  • \$\begingroup\$ thanks for the follow up!, it doesnt seem complex, for me was easy to understand, but still very slow. :(, \$\endgroup\$ Dec 18, 2014 at 15:50
  • \$\begingroup\$ just received some info, the actual load times are not 5 minutes, its 15 seconds and its everywhere including old and modern laptops too. (firs time load) \$\endgroup\$ Dec 18, 2014 at 18:17

1 Answer 1

1
\$\begingroup\$

There are so many design flaws here it would be hard to tell them all.
Worst is the lack of 'var' when using a variable in a function, which leads to global vars, which might result in horrific bugs. You should "use strict"; and track those issues.

To focus on your main issue, performance, you can get great results with minimal software risks by caching the results of the slowest operations : parsing xml and splitting strings. In quite a few places, those operations are done again and again.

example 1 : simple caching increases both speed and readability of the for loop of TaxConfiguration.ParseConfiguration

for (var i = 0; i < properties.length; i++) { // Rq var was missing
     var thisProperty = properties[i];
     var firstChild   = thisProperty.firstChild ; 
     var lastChild    = thisProperty.lastChild  ; 
     propertyName     = firstChild.textContent || firstChild.text ;
     propertyValue    = lastChild.textContent  || lastChild.text  ; 
     if (propertyName == propertyValue) {
            propertyValue = "";
     }
     this[propertyName] = propertyValue;         
}

example 2 : avoid splitting many times in seeBread. irAtrasBread can benefit from the same improvements.

function seeBread () {
    var output = "";   // much clearer here. 
    var bread = sessionStorage.getItem("bread");
    if (bread != null){
        var breadElements = bread.split("...");
        for(var i=0; i<breadElements.length-1;i++) {
            var thisBreadElementParts  = breadElements[i].split(","); 
            var onClick = " onclick='irAtrasBread(" +'"' thisBreadElementParts[1] + '"' + ", true)' "
            output += "<p class='separator'> > </p>"
            if (thisBreadElementParts[1] == "final") {
                output += "<span id='final'"+ onClick + ">" + thisBreadElementParts[0] + "</span>";
            } else {
                output += "<a "+ onClick + ">" + thisBreadElementParts[0] + "</a>";
            }
        };
    }
    var start = "'" + 'start' + "'";
    $("#bread").html('<p class="separator"> > </p><a href="#" onclick="irAtrasBread('+start + ',true)">Mapa de procesos</a>' + output);
}

3) In GetChildTermsRecursive, sessionStorage.getItem("lvl") should be cached.

4) Then caching the xml parsing might bring the more important improvements : example in GetDataInit (same can be done in GetData ).

function GetDataInit(xData, Status) 
{
    if(Status != "success") return;
    var xmlData = xData;        
    // Fix for different XML parsing in IE and Chrome
    var parsedResponseText = $.parseXML(xmlData.responseText) ;
    var termsContent = parsedResponseText.firstChild.textContent || parsedResponseText.text ; 
    var termsXML = $.parseXML(termsContent); // ??? 
    var $termsXML = $(termsXML);
    var childTerms = $termsXML.find("T");
    for(j = 0; j < 3; j++)  {
        var thisChildTerm = $(childTerms[j]);
        var termName = thisChildTerm.find("TL");
        var hasChildTermsXml = thisChildTerm.find("TM");
        // request if child terms are avaliable
        var hasChildTerms = $(hasChildTermsXml).attr("a69");
        // Requesting actual term id
        var tsTermId = thisChildTerm.attr("a9");    
        // Requestion term value ID
        var tsValueID = thisChildTerm.attr("a1000");
        // Requesting term name
        var tsTermName = termName.attr("a32");    
        sessionStorage.setItem("cat", tsTermId);
        GetChildTermsRecursive(sessionStorage.getItem("SspId"), sessionStorage.getItem("TermSetId"), tsTermId, tsTermName);
        sessionStorage.setItem("start", $("#start").html()); // once is enough
        sessionStorage.setItem("lastLvl", "start");
    }
}
\$\endgroup\$
4
  • \$\begingroup\$ on example 1, is this caching: this[propertyName] = propertyValue; ??? \$\endgroup\$ Dec 22, 2014 at 13:11
  • \$\begingroup\$ no, it's just a shorter way to perform the assignment you do in your switch() \$\endgroup\$ Dec 22, 2014 at 13:24
  • \$\begingroup\$ what did you change then on example 1? where is the caching? \$\endgroup\$ Dec 22, 2014 at 16:36
  • \$\begingroup\$ properties[i].firstChild and properties[i].lastChild. You should see that everything is read only once (properties[i] and both children) \$\endgroup\$ Dec 22, 2014 at 16:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.