performance – JavaScript formatting and modularization – Creating Google graphics on SharePoint

I created a script to extract data from a SharePoint list and display its contents graphically on another page. My original script was very complicated and I asked you how to improve readability. Steve Kwan's inspiring GitHub, I tried to refactor the script below. It's about twice as long as the previous one but I think a lot more readable …

I would like advice on where / how I can improve this script. Mainly in terms of style, but any obvious efficiency would also be appreciated.

I have to make the script compatible with Explorer, so new features like arrow expressions (arr.map (a => a * 2)) or the propagation syntax ([obj, ...arr2] is not usable.

// Namespace for the current initiative
var printingStatistics = printingStatistics || {};

// Module displaying the statistics of the current page
printingStatistics.graphPages = function () {
var oData = null; // data table sent to the Google chart
var oElem = null;

// initialization method. The entries are Data and a document.querySelector () entry.
var init = function (data, location) {
if (! data)
printingStatistics.requireData (& # 39; data & # 39;)
if (! location)
printingStatistics.requireData (& # 39; location & # 39;)

// Create the parent element in which the graph (s) will be inserted in the page.
oElem = document.createElement ("div");
oElem.id = "container";
oElem = document.querySelector (location) .appendChild (oElem);

dataManip (data);
}

// aggregator of data for each object name.
var dataManip = function (data) {
oData = {};

// Browse the data and put it in a more usable format.
for (var i = 0; i <data.length; i ++) {
// must be the current quarter or the previous quarter
var qStatus = data[i][1];
// Take the name and delete the sub-categorization (-zvl) and
// replace an observed discontinuity (tip) in the names.
var qStatusGroup = data[i][0].replace (/  s *  -  s * ZVL / i, "") .replace (/ Council / g, "Counsel");
// Will be the nth week in a quarter. Used to combine the entries of the same week.
var weekNumbers;
// variable shortener for temporary use.
var tmp = null;
// 2 long chalkboard [number of pages, week Number]
         var pageWeekTuple = null;

if (["Current Quarter", "Previous Quarter"].indexOf (qStatus) === -1)
Carry on;

// Create the main network of the data structure from the initialized attributes
if (! oData[qStatus])
oData[qStatus] = {};
if (! oData[qStatus][qStatusGroup])
oData[qStatus][qStatusGroup]    = [];
tmp = oData[qStatus][qStatusGroup];

if (qStatus === "Quarter in progress") {
// Take the value of the date and find the number of the week in which the date was.
// week = (ISO week of the year) - ((Quarter Quarter) -1) * 13
var eDate = new Date (data[i][2])
var week = eDate.getWeek () - (eDate.getWeekQuarter () - 1) * 13;

// Get a duplicate week, if it exists
pageWeekTuple = tmp[tmp.map (function (o) {
return o[0] === week
}). indexOf (true)]// Was there a duplicate? Otherwise, create a new table.
if (! pageWeekTuple) {
tmp.push ([0, 0])
pageWeekTuple = tmp[tmp.length - 1];
}
// week = (ISO week of the year) - ((Quarter Quarter) -1) * 13
pageWeekTuple[0] = eDate.getWeek () - (eDate.getWeekQuarter () - 1) * 13;
}
else if (qStatus === "Previous Quarter") {
// only interested in a painting here. There should be no duplicates.
if (! tmp[tmp.length - 1])
tmp.push ([0, 0])
pageWeekTuple = tmp[tmp.length - 1]
         }
// Sum all page numbers of weeks together
pageWeekTuple[1] + = data[i][3];
}
google.charts.setOnLoadCallback (defineCharts);
}

// Create the columns and rows for the Google graph
var defineCharts = function () {
var gDataTable = new google.visualization.DataTable ();
var cols = [];

// Create the lines for each label in the Google graph
for (var property in oData["Current Quarter"]) {
// If the label is not in the previous quarter or the current quarter, ignore it.
if (! oData["Previous Quarter"].hasOwnProperty (propert)) continues;
if (! oData["Current Quarter"].hasOwnProperty (propert)) continues;
// [Label, col1, col2, ...]
         var row = [];
row.push (propert);

// For each item in the current label, compare its value to the previous quarter total
// Invert this ratio as a percentage (0-100) and add a substitution value for the tooltip.
oData["Current Quarter"][propert].forEach (function (obj) {
var lastQuarter;
if (oData["Previous Quarter"][propert])
lastQuarter = oData["Previous Quarter"][propert][0][1]
            
            
            
            var ratio = Math.round (obj[1] / lastQuarter * 2000) / 20

row.push (ratio);
row.push (0);
});
// Get the cumulative percentage and create the HTML tooltip string.
total var = parseInt (20 * row.slice (1) .sum ()) / 20
for (var i = 2; i < row.length; i += 2) {
            row[i] = createHTMLObject(propert, total, row[i - 1], parseInt(i / 2)).outerHTML;
         }

         // Finish by pushing the row to a collection array.
         cols.push(row);
      }

      // Filter each column by type === number and sort them in increasing order.
      cols.sort(function (a, b) {
         var f = function (v) {
            return typeof v === 'number'
         }
         var first = a.filter(f).sum()
         var second = b.filter(f).sum()
         return first - second
      });

      // Find the largest length sub-array in the 2d array.
      var highest = 0;
      highest = Math.max.apply(
         Math,
         cols.map(function (o) {
            return o.length
         })
      );

      // Add the respective columns.
      // Every even column >0 is a tooltip
// Each odd column is the report of a week
gDataTable.addColumn ('string & # 39;, & quot; SBU & quot;);
for (var i = 1; i <higher; i ++) {
if (i% 2)
gDataTable.addColumn ('Number', 'Week' + (i) .toString ());
other
gDataTable.addColumn ({
& # 39; type & # 39 ;: chain & # 39;
& # 39; role & # 39 ;: tooltip & # 39 ;,
& # 39; id: Quarter (%): & # 39;;
& # 39; p:
& # 39; html & # 39 ;: true
}
});
}

// add the rows to the table, now that the columns are sorted
// Draw the table with the specified title.
gDataTable.addRows (collars)
drawChart (# of pages printed, compared to last quarter (by SBU), gDataTable)
}

// Draw the Google Charts chart with predefined options
// The entries are the chart title (chartTitle)
// the data to display.
var drawChart = function (chartTitle, dTable) {
var options = {
title: chartTitle,
caption: "no",
height: 700
isStacked: true,
& # 39; tooltip:
& # 39; isHtml & # 39 ;: true
}
colors: ['#137C14', '#257F15', '#378216', '#498517', '#5B8818', '#6D8B1A', '#7F8E1B', '#92911C', '#A4941D', '#B6971F', '#C89A20', '#DA9D21', '#ECA022', '#FFA324']
      };

// Instantiate and draw the graph.
var elem = createContainer (chartTitle.replace (/[^a-zA-Z]/ g, ""));
var chart = new google.visualization.ColumnChart (elem);
chart.draw (dTable, options);
}

// Create containers as needed, the id being a string that is
// non-alpha characters are deleted
var createContainer = function (id) {
var newElem = document.createElement ("div");
newElem.id = id.replace (/[^a-zA-Z]+ /);
oElem.appendChild (newElem);
return newElem;
}

// Create tooltips for each week's data.
// Create the structured element as follows:
// 
   
   
   
   var createHTMLObject = function (Title, totalPercent, weekPercent, weekNum) {
var returnElem = document.createElement (& # 39;);
var titleElem = document.createElement (& # 39;);
titleElem.textContent = Title
titleElem.style.fontWeight = "more daring"
var totalElem = document.createElement (& # 39;);
totalElem.textContent = Quarter (%): & # 39; + totalPercent.toString () + & # 39;% & # 39;
var weekElem = document.createElement (& # 39;);
weekElem.textContent = & # 39; Week & # 39; + weekNum + (%): & # 39; + weekPercent.toString () + & # 39;% & # 39;
returnElem.appendChild (titleElem)
returnElem.appendChild (totalElem)
returnElem.appendChild (weekElem)

return returnElem;
}

// Getter for created data.
var getData = function () {
return oData;
}

// Make the variables / methods public.
var oPublic = {
init: init,
getData: getData
}

back oPublic;
} ();

// Data collection module specified by the user.
printingStatistics.executeQueries = function () {
var oListTitle = null; // Prepare the ClientContext
var oViewTitle = null; // Prepare the ClientContext
var oView; // Prepare the ClientContext
var oProperties = null; // Push the properties specified in oDictStats
var oItems = null; // Used to initialize the enumerator after an async request
var oClientContext = null; // used to hold ClientContext for later use
var oDictStats = null; // output variable

// Define internal variables
var init = function (listName, properties, viewName) {
if (! listName)
printingStatistics.requireData (& # 39; listName & # 39;)

oListTitle = listName;
if (viewName)
oViewTitle = viewName;
if (properties)
oProperties = properties
}

// Execute queries after initialization.
// Make sure sp.js is loaded and sp.clientContext is available.
var execute = function () {
SP.SOD.executeFunc (& # 39; sp.js & # 39; SP.ClientContext & quot ;, queryInit);
}

// First request.
// If a list view is defined (oViewTitle), retrieve the CAML query from the view
// else, get all the items in the list. This can be long if there are many items in the list.
var queryInit = function () {
var oList;
oClientContext = new SP.ClientContext.get_current ();
oList = oClientContext.get_web (). get_lists (). getByTitle (oListTitle);

if (oViewTitle) {
oView = oList.get_views (). getByTitle (oViewTitle);

oClientContext.load (oView);
oClientContext.executeQueryAsync (
queryGetListView,
The request failed
)
}
other {
oItems = oList.getItems ();

oClientContext.load (oItems);
oClientContext.executeQueryAsync (
queryGetListItems,
The request failed
)
}
}

// If queries fail, notify the user.
var queryFailed = function (sender, arguments) {
alert ("The request failed." + args.get_message () + & # 39;  n +
args.get_stackTrace () + & # 39;  n & # 39; +
"Please contact an administrator");
}

// Retrieve the CAML view from the list, then retrieve all items from this view.
var queryGetListView = function (sender, arguments) {
oClientContext = new SP.ClientContext.get_current ();
oList = oClientContext.get_web (). get_lists (). getByTitle (oListTitle);

var query = new SP.CamlQuery ();
query.set_viewXml (oView.get_viewQuery ());
oItems = oList.getItems (request);

oClientContext.load (oItems);
oClientContext.executeQueryAsync (
queryGetListItems,
The request failed
)
}

// enumerate items and extract new generation data from objects.
var queryGetListItems = function (sender, arguments) {
var listItemEnumerator = oItems.getEnumerator ();
var oListItem;
oDictStats = [];

while (listItemEnumerator.moveNext ()) {
// Get all the defined fields
oListItem = listItemEnumerator.get_current (). get_fieldValues ​​();
var quarter = oListItem.PreviousOrCurrentQuarter;
var itemTitle = oListItem.Title.replace ( / s *  -  s * ZvL / i, "");
var itemStats = [];

// if the properties have been defined by the requestor, extract the data
// Otherwise, pull all the object.
if (properties) {
for (var i = 0; i <oProperties.length; i ++) {
itemStats.push ((oListItem[oProperties[oProperties[oProperties[oProperties[i]]));
}
}
other {
itemStats.push (oListItem);
}

oDictStats.push (itemStats);
}
}

// Getter for the stats.
var getStats = function () {
return oDictStats;
}

// Make variables and methods available to the public.
var oPublic = {
init: init,
execute: execute,
getStats: getStats
}

back oPublic;
} ();

// to throw errors when missing data
printingStatistics.requireData = function (paramName) {
throw "Missing parameter:" + paramName.toString () ;;
}

// Module to add additional features to data infrastructures
printingStatistics.addPrototypes = function () {
/ *
* * * * * * Additional scripting tools * * * * * *
*
* This script is published in the public domain and can be used, modified and
* distributed without restrictions. Attribution not necessary but appreciated.
* Source: https://weeknumber.net/how-to/javascript
*
* Returns the ISO week of the date.
* /
Date.prototype.getWeek = function () {
var date = new Date (this.getTime ());
date.setHours (0, 0, 0, 0);
// Thursday of the current week decides the year.
date.setDate (date.getDate () + 3 - (date.getDay () + 6)% 7);
// January 4th is still week 1.
var week1 = new date (date.getFullYear (), 0, 4);
// Adjust to the Thursday of week 1 and count the number of weeks from date to week1.
return 1 + Math.round (((date.getTime () - week1.getTime ()) / 86400000 -
3 + (week1.getDay () + 6)% 7) / 7);
}

// Returns how many weeks there are in a given year.
Date.prototype.getWeeksInYear = function () {
var date = new Date (this.getTime ());
var isLeap = new Date (date.getYear () + 1900, 1, 29) .getMonth () === 1;
// check if January 1st is a Thursday or a leap year that has a
// Wednesday, January 1st. Otherwise, it is 52
return date.getDay () === 4 || isLeap && date.getDay () === 3? 53: 52
}

// Returns the quarter in which a specified date is found
Date.prototype.getWeekQuarter = function () {
var date = new Date (this.getTime ());
weekNum = date.getWeek ();
return weekNum == 53? 4: Math.ceil (weekNum / 13);
}

// create a sum of the array
Array.prototype.sum = function () {
returns this.reduce (function (a, b) {
return a + b
}, 0);
}
} ();

var interval = null;
var done = function (obj) {
// Interval function that checks whether the object exists and executes
// printingStatistics.graphPages.init () once it's done.
if (obj) {
clearInterval (interval)
printingStatistics.graphPages.init (obj, ".ms-rte-layoutszone-inner")
}
}

printingStatistics.executeQueries.init (
"Print Statistics",
   ["Title",
      "PreviousOrCurrentQuarter",
      "RelevantDate",
      "TotalPrintedPages"],
"Current statistics"
)
printingStatistics.executeQueries.execute ()
interval = setInterval (function () {
done (printingStatistics.executeQueries.getStats ())
}, 200)