nickjillings@2131: /* nicholas@2538: * Analysis script for WAET nicholas@2538: */ nickjillings@2131: nickjillings@2192: // Firefox does not have an XMLDocument.prototype.getElementsByName nickjillings@2192: // and there is no searchAll style command, this custom function will nickjillings@2192: // search all children recusrively for the name. Used for XSD where all nickjillings@2192: // element nodes must have a name and therefore can pull the schema node nicholas@2538: XMLDocument.prototype.getAllElementsByName = function (name) { nickjillings@2192: name = String(name); nickjillings@2192: var selected = this.documentElement.getAllElementsByName(name); nickjillings@2192: return selected; nickjillings@2192: } nickjillings@2192: nicholas@2538: Element.prototype.getAllElementsByName = function (name) { nickjillings@2192: name = String(name); nickjillings@2192: var selected = []; nickjillings@2192: var node = this.firstElementChild; nicholas@2538: while (node != null) { nicholas@2538: if (node.getAttribute('name') == name) { nickjillings@2192: selected.push(node); nickjillings@2192: } nicholas@2538: if (node.childElementCount > 0) { nickjillings@2192: selected = selected.concat(node.getAllElementsByName(name)); nickjillings@2192: } nickjillings@2192: node = node.nextElementSibling; nickjillings@2192: } nickjillings@2192: return selected; nickjillings@2192: } nickjillings@2192: nicholas@2538: XMLDocument.prototype.getAllElementsByTagName = function (name) { nickjillings@2192: name = String(name); nickjillings@2192: var selected = this.documentElement.getAllElementsByTagName(name); nickjillings@2192: return selected; nickjillings@2192: } nickjillings@2192: nicholas@2538: Element.prototype.getAllElementsByTagName = function (name) { nickjillings@2192: name = String(name); nickjillings@2192: var selected = []; nickjillings@2192: var node = this.firstElementChild; nicholas@2538: while (node != null) { nicholas@2538: if (node.nodeName == name) { nickjillings@2192: selected.push(node); nickjillings@2192: } nicholas@2538: if (node.childElementCount > 0) { nickjillings@2192: selected = selected.concat(node.getAllElementsByTagName(name)); nickjillings@2192: } nickjillings@2192: node = node.nextElementSibling; nickjillings@2192: } nickjillings@2192: return selected; nickjillings@2192: } nickjillings@2192: nickjillings@2192: // Firefox does not have an XMLDocument.prototype.getElementsByName nickjillings@2192: if (typeof XMLDocument.prototype.getElementsByName != "function") { nicholas@2538: XMLDocument.prototype.getElementsByName = function (name) { nickjillings@2192: name = String(name); nickjillings@2192: var node = this.documentElement.firstElementChild; nickjillings@2192: var selected = []; nicholas@2538: while (node != null) { nicholas@2538: if (node.getAttribute('name') == name) { nickjillings@2192: selected.push(node); nickjillings@2192: } nickjillings@2192: node = node.nextElementSibling; nickjillings@2192: } nickjillings@2192: return selected; nickjillings@2192: } nickjillings@2192: } nickjillings@2192: nickjillings@2192: var chartContext, testData; nicholas@2538: window.onload = function () { nickjillings@2131: // Load the Visualization API and the corechart package. nicholas@2538: google.charts.load('current', { nicholas@2538: 'packages': ['corechart'] nicholas@2538: }); nickjillings@2131: chartContext = new Chart(); nickjillings@2192: testData = new Data(); nickjillings@2192: } nickjillings@2192: nickjillings@2192: function get(url) { nicholas@2538: // Return a new promise. nicholas@2538: return new Promise(function (resolve, reject) { nicholas@2538: // Do the usual XHR stuff nicholas@2538: var req = new XMLHttpRequest(); nicholas@2538: req.open('GET', url); nicholas@2538: req.onload = function () { nicholas@2538: // This is called even on 404 etc nicholas@2538: // so check the status nicholas@2538: if (req.status == 200) { nicholas@2538: // Resolve the promise with the response text nicholas@2538: resolve(req.response); nicholas@2538: } else { nicholas@2538: // Otherwise reject with the status text nicholas@2538: // which will hopefully be a meaningful error nicholas@2538: reject(Error(req.statusText)); nicholas@2538: } nicholas@2538: }; nickjillings@2192: nicholas@2538: // Handle network errors nicholas@2538: req.onerror = function () { nicholas@2538: reject(Error("Network Error")); nicholas@2538: }; nickjillings@2192: nicholas@2538: // Make the request nicholas@2538: req.send(); nicholas@2538: }); nickjillings@2131: } nickjillings@2131: nickjillings@2135: function arrayMean(values) { nickjillings@2135: var mean = 0; nickjillings@2135: for (var value of values) { nickjillings@2135: mean += value; nickjillings@2135: } nickjillings@2135: mean /= values.length; nickjillings@2135: return mean; nickjillings@2135: } nickjillings@2135: nickjillings@2183: function percentile(values, p) { nickjillings@2183: //http://web.stanford.edu/class/archive/anthsci/anthsci192/anthsci192.1064/handouts/calculating%20percentiles.pdf nicholas@2538: values.sort(function (a, b) { nicholas@2538: return a - b; nicholas@2538: }); nickjillings@2135: // get ordinal rank nicholas@2538: var index = values.length * p / 100; nickjillings@2183: var k = Math.floor(index); nickjillings@2183: if (k == index) { nickjillings@2183: return values[k]; nickjillings@2183: } else { nicholas@2538: var f = index - k; nicholas@2538: var x_int = (1 - f) * values[k] + f * values[k + 1]; nickjillings@2183: return x_int; nickjillings@2183: } nickjillings@2135: } nickjillings@2135: nickjillings@2135: function arrayMin(array) { nickjillings@2135: // Return the minimum value of an array nickjillings@2135: var min = array[0]; nickjillings@2135: for (var value of array) { nickjillings@2135: if (value < min) { nickjillings@2135: min = value; nickjillings@2135: } nickjillings@2135: } nickjillings@2135: return min; nickjillings@2135: } nickjillings@2135: nickjillings@2135: function arrayMax(array) { nickjillings@2135: // Return the minimum value of an array nickjillings@2135: var max = array[0]; nickjillings@2135: for (var value of array) { nickjillings@2135: if (value > max) { nickjillings@2135: max = value; nickjillings@2135: } nickjillings@2135: } nickjillings@2135: return max; nickjillings@2135: } nickjillings@2135: nickjillings@2181: function boxplotRow(array) { nickjillings@2181: // Take an array of element values and return array of computed intervals nickjillings@2181: var result = { nicholas@2538: median: percentile(array, 50), nicholas@2538: pct25: percentile(array, 25), nicholas@2538: pct75: percentile(array, 75), nicholas@2538: IQR: null, nickjillings@2181: min: null, nickjillings@2181: max: null, nickjillings@2181: outliers: new Array() nickjillings@2181: } nicholas@2538: result.IQR = result.pct75 - result.pct25; nickjillings@2181: var rest = []; nicholas@2538: var pct75_IQR = result.pct75 + 1.5 * result.IQR; nicholas@2538: var pct25_IQR = result.pct25 - 1.5 * result.IQR; nicholas@2538: for (var i = 0; i < array.length; i++) { nickjillings@2181: //outliers, ranger above pct75+1.5*IQR or below pct25-1.5*IQR nickjillings@2181: var point = array[i]; nickjillings@2181: if (point > pct75_IQR || point < pct25_IQR) { nickjillings@2181: result.outliers.push(point); nickjillings@2181: } else { nickjillings@2181: rest.push(point); nickjillings@2181: } nickjillings@2181: } nickjillings@2181: result.max = arrayMax(rest); nickjillings@2181: result.min = arrayMin(rest); nickjillings@2181: return result; nicholas@2538: nickjillings@2181: } nickjillings@2181: nicholas@2538: function arrayHistogram(values, steps, min, max) { nickjillings@2135: if (steps == undefined) { nickjillings@2135: steps = 0.25; nickjillings@2135: console.log("Warning: arrayHistogram called without steps size set, default to 0.25"); nickjillings@2135: } nicholas@2538: if (min == undefined) { nicholas@2538: min = arrayMin(values); nicholas@2538: } nicholas@2538: if (max == undefined) { nicholas@2538: max = arrayMax(values); nicholas@2538: } nickjillings@2135: var histogram = []; nickjillings@2135: var index = min; nicholas@2538: while (index < max) { nickjillings@2135: histogram.push({ nickjillings@2135: marker: index, nickjillings@2135: lt: index, nicholas@2538: rt: index + steps, nickjillings@2135: count: 0 nickjillings@2135: }); nickjillings@2135: index += steps; nickjillings@2135: } nickjillings@2135: for (var value of values) { nickjillings@2135: for (var entry of histogram) { nickjillings@2135: if (value >= entry.lt && value <= entry.rt) { nickjillings@2135: entry.count++; nickjillings@2135: break; nickjillings@2135: } nickjillings@2135: } nickjillings@2135: } nickjillings@2135: return histogram; nickjillings@2135: } nickjillings@2135: nickjillings@2131: function Chart() { nickjillings@2192: this.valueData; nickjillings@2135: this.charts = []; nicholas@2538: nicholas@2538: this.chartObject = function (name) { nickjillings@2135: // Create the charting object nickjillings@2135: this.name = name; nickjillings@2135: this.root = document.createElement("div"); nickjillings@2135: this.root.className = "chart-holder"; nicholas@2538: this.root.setAttribute("name", name); nickjillings@2135: this.chartDOM = document.createElement("div"); nickjillings@2135: this.tableDOM = document.createElement("div"); nickjillings@2135: this.latexDOM = document.createElement("div"); nickjillings@2135: this.downloadDOM = document.createElement("div"); nickjillings@2135: this.chart = undefined; nickjillings@2135: this.data = new google.visualization.DataTable(); nickjillings@2135: this.options = {}; nickjillings@2135: this.print = document.createElement("button"); nickjillings@2136: this.sortDataButton = document.createElement("button"); nickjillings@2136: this.sortDataButton.textContent = "Sort by Data"; nicholas@2538: this.sortDataButton.addEventListener("click", this); nicholas@2538: this.sortDataButton.setAttribute("name", "sort-data"); nickjillings@2136: this.sortNameButton = document.createElement("button"); nickjillings@2136: this.sortNameButton.textContent = "Sort by Name"; nicholas@2538: this.sortNameButton.addEventListener("click", this); nicholas@2538: this.sortNameButton.setAttribute("name", "sort-name"); nicholas@2538: this.draw = function () { nicholas@2538: if (this.chart == undefined) { nicholas@2538: return; nicholas@2538: } nickjillings@2136: this.tableDOM.innerHTML = null; nickjillings@2136: this.latexDOM.innerHTML = null; nickjillings@2136: this.buildTable(); nickjillings@2136: this.writeLatex(); nicholas@2538: this.chart.draw(this.data, this.options); nickjillings@2136: } nicholas@2538: this.sortData = function () { nickjillings@2173: this.data.sort(1); nickjillings@2136: } nicholas@2538: this.sortName = function () { nickjillings@2173: this.data.sort(0); nickjillings@2136: } nicholas@2538: this.handleEvent = function () { nickjillings@2135: // Only used to handle the chart.event.addListener(this,'ready') callback nicholas@2538: switch (event.currentTarget.getAttribute("name")) { nickjillings@2136: case "download": nickjillings@2136: window.open(this.chart.getImageURI()); nickjillings@2136: break; nickjillings@2136: case "sort-data": nickjillings@2136: this.sortData(); nickjillings@2136: this.draw(); nickjillings@2136: break; nickjillings@2136: case "sort-name": nickjillings@2136: this.sortName(); nickjillings@2136: this.draw(); nickjillings@2136: break; nickjillings@2136: } nickjillings@2135: } nicholas@2538: nickjillings@2135: this.root.appendChild(this.chartDOM); nickjillings@2135: this.root.appendChild(this.tableDOM); nickjillings@2135: this.root.appendChild(this.latexDOM); nickjillings@2136: this.root.appendChild(this.sortDataButton); nickjillings@2136: this.root.appendChild(this.sortNameButton); nickjillings@2135: this.root.appendChild(this.print); nickjillings@2135: this.print.textContent = "Download"; nicholas@2538: this.print.setAttribute("name", "download"); nicholas@2538: this.print.addEventListener("click", this); nickjillings@2135: this.root.appendChild(this.downloadDOM); nicholas@2538: this.buildTable = function () { nickjillings@2135: var table = document.createElement("table"); nickjillings@2135: table.border = "1"; nickjillings@2172: var numRows = this.data.getNumberOfRows(); nickjillings@2172: var numColumns = this.data.getNumberOfColumns(); nicholas@2538: for (var columnIndex = 0; columnIndex < numColumns; columnIndex++) { nickjillings@2181: var tableTitle = this.data.getColumnLabel(columnIndex); nickjillings@2181: if (tableTitle != "") { nickjillings@2181: var table_row = document.createElement('tr'); nickjillings@2181: table.appendChild(table_row); nickjillings@2181: var row_title = document.createElement('td'); nickjillings@2181: table_row.appendChild(row_title); nickjillings@2181: row_title.textContent = tableTitle; nicholas@2538: for (var rowIndex = 0; rowIndex < numRows; rowIndex++) { nickjillings@2181: var row_entry = document.createElement('td'); nickjillings@2181: table_row.appendChild(row_entry); nicholas@2538: var entry = this.data.getValue(rowIndex, columnIndex); nicholas@2538: if (isFinite(Number(entry))) { nickjillings@2181: entry = String(Number(entry).toFixed(4)); nickjillings@2181: } nickjillings@2181: row_entry.textContent = entry; nickjillings@2172: } nickjillings@2135: } nickjillings@2135: } nickjillings@2135: this.tableDOM.appendChild(table); nickjillings@2135: }; nicholas@2538: this.writeLatex = function () { nickjillings@2172: var numRows = this.data.getNumberOfRows(); nickjillings@2172: var numColumns = this.data.getNumberOfColumns(); nickjillings@2135: var root = document.createElement("div"); nickjillings@2135: root.className = "code"; nickjillings@2135: var holder = document.createElement("pre"); nickjillings@2135: // Table start nickjillings@2135: var start = document.createElement("p"); nickjillings@2135: start.textContent = "\\" + "begin{tabular}{|l|"; nickjillings@2135: holder.appendChild(start); nicholas@2538: for (var i = 0; i < numRows; i++) { nicholas@2538: start.textContent = start.textContent + "c|"; nickjillings@2135: } nickjillings@2136: start.textContent = start.textContent.concat("}"); nickjillings@2135: // Now write the rows: nicholas@2538: for (var rIndex = 0; rIndex < numColumns; rIndex++) { nickjillings@2181: var tableTitle = this.data.getColumnLabel(rIndex); nicholas@2538: if (tableTitle != "") { nickjillings@2181: var row = document.createElement("p"); nickjillings@2181: row.textContent = tableTitle.concat(" & "); nicholas@2538: for (var cIndex = 0; cIndex < numRows; cIndex++) { nicholas@2538: var entry = this.data.getValue(cIndex, rIndex); nicholas@2538: if (isFinite(Number(entry))) { nickjillings@2181: entry = String(Number(entry).toFixed(4)); nickjillings@2181: } nickjillings@2181: row.textContent = row.textContent.concat(entry); nicholas@2538: if (cIndex < numRows - 1) { nickjillings@2181: row.textContent = row.textContent.concat(" & "); nickjillings@2181: } else { nickjillings@2181: row.textContent = row.textContent.concat(" \\\\ \\hline"); nickjillings@2181: } nickjillings@2172: } nickjillings@2181: holder.appendChild(row); nickjillings@2135: } nickjillings@2135: } nickjillings@2135: // Table end nickjillings@2135: var end = document.createElement("p"); nickjillings@2135: end.textContent = "\\" + "end{tabular}"; nickjillings@2135: holder.appendChild(end); nickjillings@2135: root.appendChild(holder); nickjillings@2135: this.latexDOM.appendChild(root); nickjillings@2135: } nickjillings@2135: } nicholas@2538: nicholas@2538: this.clear = function () { nickjillings@2135: var inject = document.getElementById("test-pages"); nickjillings@2135: for (var chart of this.charts) { nickjillings@2135: inject.removeChild(chart.root); nickjillings@2135: } nickjillings@2135: this.charts = []; nickjillings@2135: } nicholas@2538: nicholas@2538: this.drawTestMean = function () { nickjillings@2135: // This draws one bargraph per axis with every test element on nickjillings@2135: if (this.valueData == null) { nickjillings@2135: console.log("Error - Data not loaded"); nickjillings@2135: return; nickjillings@2135: } nickjillings@2135: var chartList = []; nicholas@2538: nickjillings@2135: // Create the data table nickjillings@2135: for (var page of this.valueData.pages) { nickjillings@2135: for (var element of page.elements) { nickjillings@2135: for (var axis of element.axis) { nickjillings@2135: // Find the axis nicholas@2538: var axisChart = chartList.find(function (element, index, array) { nicholas@2538: if (element.name == this) { nicholas@2538: return true; nicholas@2538: } else { nicholas@2538: return false; nicholas@2538: } nicholas@2538: }, "mean-test-" + axis.name); nickjillings@2135: if (axisChart == null) { nicholas@2538: axisChart = new this.chartObject("mean-test-" + axis.name); nickjillings@2135: axisChart.options = { nicholas@2538: 'title': 'Mean of axis: ' + axis.name, nicholas@2538: 'width': window.innerWidth * 0.9, nicholas@2538: 'height': (window.innerWidth * 0.9) / 1.77 nickjillings@2135: } nicholas@2538: axisChart.data.addColumn('string', 'id'); nicholas@2538: axisChart.data.addColumn('number', axis.name); nickjillings@2135: chartList.push(axisChart); nickjillings@2135: document.getElementById("test-pages").appendChild(axisChart.root); nickjillings@2135: } nickjillings@2135: var mean = arrayMean(axis.values); nicholas@2538: axisChart.data.addRow([element.id, mean]); nickjillings@2135: } nickjillings@2135: } nickjillings@2135: } nicholas@2538: nickjillings@2135: // Build and push charts nickjillings@2135: for (var chart of chartList) { nickjillings@2135: chart.chart = new google.visualization.ColumnChart(chart.chartDOM); nicholas@2538: chart.chart.draw(chart.data, chart.options); nickjillings@2135: chart.buildTable(); nickjillings@2135: chart.writeLatex(); nickjillings@2135: this.charts.push(chart); nickjillings@2135: } nickjillings@2135: } nicholas@2538: nicholas@2538: this.drawTestBoxplot = function () { nickjillings@2181: if (this.valueData == null) { nickjillings@2181: console.log("Error - Data not loaded"); nickjillings@2181: return; nickjillings@2181: } nickjillings@2181: var chartList = []; nicholas@2538: nickjillings@2181: // Creates one chart per axis nicholas@2538: nickjillings@2181: // Create the data table nickjillings@2181: for (var page of this.valueData.pages) { nickjillings@2181: for (var element of page.elements) { nickjillings@2181: for (var axis of element.axis) { nickjillings@2181: // Find the axis nicholas@2538: var axisChart = chartList.find(function (element, index, array) { nicholas@2538: if (element.name == this) { nicholas@2538: return true; nicholas@2538: } else { nicholas@2538: return false; nicholas@2538: } nicholas@2538: }, "boxplot-test-" + axis.name); nickjillings@2181: if (axisChart == null) { nickjillings@2181: // Axis chart doesn't exist nicholas@2538: axisChart = new this.chartObject("boxplot-test-" + axis.name); nickjillings@2181: axisChart.options = { nicholas@2538: 'title': 'Boxplot of axis ' + axis.name, nicholas@2538: 'width': window.innerWidth * 0.9, nicholas@2538: 'height': (window.innerWidth * 0.9) / 1.77, nicholas@2538: legend: { nicholas@2538: position: 'none' nicholas@2538: }, nickjillings@2181: lineWidth: 0, nicholas@2538: series: [{ nicholas@2538: 'color': '#D3362D' nicholas@2538: }], nickjillings@2181: intervals: { nickjillings@2181: barWidth: 1, nickjillings@2181: boxWidth: 1, nickjillings@2181: lineWidth: 2, nickjillings@2181: style: 'boxes' nickjillings@2181: }, nickjillings@2181: interval: { nickjillings@2181: max: { nickjillings@2181: style: 'bars', nickjillings@2181: fillOpacity: 1, nickjillings@2181: color: '#777' nickjillings@2181: }, nickjillings@2181: min: { nickjillings@2181: style: 'bars', nickjillings@2181: fillOpacity: 1, nickjillings@2181: color: '#777' nickjillings@2181: } nickjillings@2181: } nickjillings@2181: }; nicholas@2538: axisChart.data.addColumn('string', 'id'); nicholas@2538: axisChart.data.addColumn('number', 'median'); nicholas@2538: axisChart.data.addColumn({ nicholas@2538: id: 'max', nicholas@2538: type: 'number', nicholas@2538: role: 'interval' nicholas@2538: }); nicholas@2538: axisChart.data.addColumn({ nicholas@2538: id: 'min', nicholas@2538: type: 'number', nicholas@2538: role: 'interval' nicholas@2538: }); nicholas@2538: axisChart.data.addColumn({ nicholas@2538: id: 'firstQuartile', nicholas@2538: type: 'number', nicholas@2538: role: 'interval' nicholas@2538: }); nicholas@2538: axisChart.data.addColumn({ nicholas@2538: id: 'median', nicholas@2538: type: 'number', nicholas@2538: role: 'interval' nicholas@2538: }); nicholas@2538: axisChart.data.addColumn({ nicholas@2538: id: 'thirdQuartile', nicholas@2538: type: 'number', nicholas@2538: role: 'interval' nicholas@2538: }); nickjillings@2181: chartList.push(axisChart); nickjillings@2181: document.getElementById("test-pages").appendChild(axisChart.root); nickjillings@2181: } nickjillings@2181: var result = boxplotRow(axis.values); nicholas@2538: axisChart.data.addRow([element.id, result.median, result.max, result.min, result.pct25, result.median, result.pct75]); nickjillings@2181: } nickjillings@2181: } nickjillings@2181: } nickjillings@2181: // Build and push charts nickjillings@2181: for (var chart of chartList) { nickjillings@2181: chart.chart = new google.visualization.LineChart(chart.chartDOM); nicholas@2538: chart.chart.draw(chart.data, chart.options); nickjillings@2181: chart.buildTable(); nickjillings@2181: chart.writeLatex(); nickjillings@2181: this.charts.push(chart); nickjillings@2181: } nickjillings@2181: } nicholas@2538: nicholas@2538: this.drawPageMean = function () { nickjillings@2131: // First we must get the value data nickjillings@2134: if (this.valueData == null) { nickjillings@2134: console.log("Error - Data not loaded"); nickjillings@2134: return; nickjillings@2134: } nickjillings@2134: // We create one plot per page nickjillings@2134: for (var page of this.valueData.pages) { nicholas@2538: nickjillings@2135: // Create the chart resulting point nicholas@2538: var chart = new this.chartObject("mean-page-" + page.id); nickjillings@2135: document.getElementById("test-pages").appendChild(chart.root); nicholas@2538: nickjillings@2134: // Create the data table nicholas@2538: chart.data.addColumn('string', 'id'); nickjillings@2134: // Get axis labels nickjillings@2134: for (var axis of page.elements[0].axis) { nicholas@2538: chart.data.addColumn('number', axis.name); nickjillings@2134: } nickjillings@2134: var rows = []; // Rows is an array of tuples [col1, col2, col3 ... colN]; nickjillings@2134: for (var element of page.elements) { nickjillings@2134: var entry = [element.id]; nicholas@2538: for (var i = 0; i < page.elements[0].axis.length; i++) { nicholas@2538: var mean = 0; nickjillings@2134: if (i < element.axis.length) { nickjillings@2134: var axis = element.axis[i]; nickjillings@2135: mean = arrayMean(axis.values); nickjillings@2134: } nickjillings@2134: entry.push(mean); nickjillings@2134: } nickjillings@2134: rows.push(entry); nickjillings@2134: } nickjillings@2135: chart.data.addRows(rows); nickjillings@2135: chart.options = { nicholas@2538: 'title': 'Mean of page: ' + page.id, nicholas@2538: 'width': 800, nicholas@2538: 'height': 700 nicholas@2538: } nicholas@2538: // Draw the chart nickjillings@2135: chart.chart = new google.visualization.ColumnChart(chart.chartDOM); nicholas@2538: chart.chart.draw(chart.data, chart.options); nickjillings@2135: chart.buildTable(); nickjillings@2135: chart.writeLatex(); nickjillings@2135: this.charts.push(chart); nickjillings@2135: } nickjillings@2135: } nicholas@2538: nicholas@2538: this.drawElementHistogram = function () { nickjillings@2135: // First we must get the value data nickjillings@2135: if (this.valueData == null) { nickjillings@2135: console.log("Error - Data not loaded"); nickjillings@2135: return; nickjillings@2135: } nickjillings@2135: // We create one plot per element, enjoy... nickjillings@2135: for (var page of this.valueData.pages) { nickjillings@2135: for (var element of page.elements) { nickjillings@2135: // Build the chart object nicholas@2538: var chart = new this.chartObject("histogram-element-" + element.id); nickjillings@2135: document.getElementById("test-pages").appendChild(chart.root); nicholas@2538: chart.data.addColumn('string', 'index'); nickjillings@2135: var histograms = []; nickjillings@2135: for (var axis of element.axis) { nicholas@2538: chart.data.addColumn('number', axis.name); nicholas@2538: histograms.push(arrayHistogram(axis.values, 0.125, 0.0, 1.0)); nickjillings@2135: } nickjillings@2135: for (var axis of element.axis) { nicholas@2538: for (var i = 0; i < histograms[0].length; i++) { nicholas@2538: var entry = ["" + histograms[0][i].lt.toPrecision(2) + "-" + histograms[0][i].rt.toPrecision(3)] nickjillings@2135: for (var histogram of histograms) { nickjillings@2135: entry.push(histogram[i].count); nickjillings@2135: } nickjillings@2135: chart.data.addRow(entry); nickjillings@2135: } nickjillings@2135: } nickjillings@2135: chart.options = { nicholas@2538: 'title': 'Histogram of element: ' + element.id, nicholas@2538: 'width': 800, nicholas@2538: 'height': 700, nicholas@2538: 'bar': { nicholas@2538: 'groupWidth': '100%' nicholas@2538: } nicholas@2538: } nicholas@2538: // Draw the chart nickjillings@2135: chart.chart = new google.visualization.ColumnChart(chart.chartDOM); nicholas@2538: chart.chart.draw(chart.data, chart.options); nickjillings@2135: chart.buildTable(); nickjillings@2135: chart.writeLatex(); nickjillings@2135: this.charts.push(chart); nickjillings@2135: } nickjillings@2134: } nickjillings@2131: } nickjillings@2192: } nickjillings@2192: nickjillings@2192: function Data() { nickjillings@2192: // This holds the link between the server side calculations and the client side visualisation of the data nicholas@2538: nickjillings@2192: // Dynamically generate the test filtering / page filterting tools nickjillings@2192: var self = this; nickjillings@2192: // Collect the test types and counts nickjillings@2192: this.testSavedDiv = document.getElementById("test-saved"); nickjillings@2192: this.testSaves = null; nickjillings@2192: this.selectURL = null; nicholas@2538: nickjillings@2192: this.specification = new Specification(); nicholas@2538: get("../xml/test-schema.xsd").then(function (response) { nickjillings@2192: var parse = new DOMParser(); nicholas@2538: self.specification.schema = parse.parseFromString(response, 'text/xml'); nicholas@2538: }, function (error) { nickjillings@2192: console.log("ERROR: Could not get Test Schema"); nickjillings@2192: }); nicholas@2538: this.update = function (url) { nickjillings@2192: var self = this; nickjillings@2192: } nicholas@2538: nicholas@2538: this.updateData = function (req_str) { nickjillings@2193: // Now go get that data nicholas@2538: get(req_str).then(function (response) { nickjillings@2193: // Returns the data nickjillings@2193: chartContext.valueData = JSON.parse(response); nicholas@2538: }, function (error) { nicholas@2538: console.error(error); nicholas@2538: }); nickjillings@2193: } nickjillings@2192: } nickjillings@2192: nicholas@2538: var interfaceContext = new function () { nickjillings@2192: // This creates the interface for the user to connect with the dynamic back-end to retrieve data nickjillings@2192: this.rootDOM = document.createElement("div"); nickjillings@2193: this.getDataButton = { nickjillings@2193: button: document.createElement("button"), nickjillings@2193: parent: this, nicholas@2538: handleEvent: function (event) { nickjillings@2193: // Get the list of files: nicholas@2538: var req_str = "../php/get_filtered_score.php" + this.parent.getFilterString(); nickjillings@2193: testData.updateData(req_str); nickjillings@2193: } nickjillings@2193: } nickjillings@2193: this.getDataButton.button.textContent = "Get Filtered Data"; nicholas@2538: this.getDataButton.button.addEventListener("click", this.getDataButton); nicholas@2538: nicholas@2287: this.getRawScoreData = { nicholas@2287: root: document.createElement("div"), nicholas@2287: csvDOM: document.createElement("button"), nicholas@2287: jsonDOM: document.createElement("button"), nicholas@2287: xmlDOM: document.createElement("button"), nicholas@2287: presentDOM: document.createElement("div"), nicholas@2287: parent: this, nicholas@2287: XHR: new XMLHttpRequest(), nicholas@2538: handleEvent: function (event) { nicholas@2287: this.presentDOM.innerHTML = null; nicholas@2538: var url = "../php/get_filtered_score.php" + this.parent.getFilterString(); nicholas@2538: this.XHR.open("GET", url + "&format=" + event.currentTarget.textContent, true); nicholas@2538: switch (event.currentTarget.textContent) { nicholas@2287: case "CSV": nicholas@2538: this.XHR.onload = function () { nicholas@2287: var file = [this.response]; nicholas@2538: var bb = new Blob(file, { nicholas@2538: type: 'text/csv' nicholas@2538: }); nicholas@2538: this.parent.presentDOM.appendChild(this.parent.generateLink(bb, "scores.csv")); nicholas@2287: } nicholas@2287: break; nicholas@2287: case "JSON": nicholas@2538: this.XHR.onload = function () { nicholas@2287: var file = [this.response]; nicholas@2538: var bb = new Blob(file, { nicholas@2538: type: 'application/json' nicholas@2538: }); nicholas@2538: this.parent.presentDOM.appendChild(this.parent.generateLink(bb, "scores.json")); nicholas@2287: } nicholas@2287: break; nicholas@2287: case "XML": nicholas@2538: this.XHR.onload = function () { nicholas@2287: var file = [this.response]; nicholas@2538: var bb = new Blob(file, { nicholas@2538: type: 'text/xml' nicholas@2538: }); nicholas@2538: this.parent.presentDOM.appendChild(this.parent.generateLink(bb, "scores.xml")); nicholas@2287: } nicholas@2287: break; nicholas@2287: } nicholas@2287: this.XHR.send(); nicholas@2287: }, nicholas@2538: generateLink: function (blob, filename) { nicholas@2287: var dnlk = window.URL.createObjectURL(blob); nicholas@2287: var a = document.createElement("a"); nicholas@2287: a.hidden = ''; nicholas@2287: a.href = dnlk; nicholas@2287: a.download = filename; nicholas@2287: a.textContent = "Save File"; nicholas@2287: return a; nicholas@2287: } nicholas@2287: } nicholas@2538: nicholas@2287: this.getRawScoreData.root.appendChild(this.getRawScoreData.csvDOM); nicholas@2287: this.getRawScoreData.root.appendChild(this.getRawScoreData.jsonDOM); nicholas@2287: this.getRawScoreData.root.appendChild(this.getRawScoreData.xmlDOM); nicholas@2287: this.getRawScoreData.root.appendChild(this.getRawScoreData.presentDOM); nicholas@2287: this.getRawScoreData.XHR.parent = this.getRawScoreData; nicholas@2287: this.getRawScoreData.csvDOM.textContent = 'CSV'; nicholas@2538: this.getRawScoreData.csvDOM.addEventListener('click', this.getRawScoreData); nicholas@2287: this.getRawScoreData.jsonDOM.textContent = 'JSON'; nicholas@2538: this.getRawScoreData.jsonDOM.addEventListener('click', this.getRawScoreData); nicholas@2287: this.getRawScoreData.xmlDOM.textContent = 'XML'; nicholas@2538: this.getRawScoreData.xmlDOM.addEventListener('click', this.getRawScoreData); nicholas@2538: nickjillings@2195: this.testSaves = { nickjillings@2195: json: null, nickjillings@2195: selectedURL: null, nickjillings@2195: inputs: [], nickjillings@2195: parent: this nickjillings@2195: }; nicholas@2538: this.init = function () { nickjillings@2195: var self = this; nicholas@2538: get('../php/get_tests.php?format=JSON').then(function (response) { nickjillings@2195: document.getElementById("test-saved").innerHTML = null; nickjillings@2195: var table = document.createElement("table"); nickjillings@2195: table.innerHTML = "Test FilenameCountInclude"; nickjillings@2195: self.testSaves.json = JSON.parse(response); nickjillings@2195: for (var test of self.testSaves.json.tests) { nickjillings@2195: var tableRow = document.createElement("tr"); nickjillings@2195: var tableRowFilename = document.createElement("td"); nickjillings@2195: tableRowFilename.textContent = test.testName; nickjillings@2195: var tableRowCount = document.createElement("td"); nickjillings@2195: tableRowCount.textContent = test.files.length; nickjillings@2195: tableRow.appendChild(tableRowFilename); nickjillings@2195: tableRow.appendChild(tableRowCount); nickjillings@2195: var tableRowInclude = document.createElement("td"); nickjillings@2195: var obj = { nickjillings@2195: root: document.createElement("input"), nickjillings@2195: parent: self.testSaves, nicholas@2538: handleEvent: function (event) { nickjillings@2195: this.parent.selectedURL = event.currentTarget.getAttribute("source"); nickjillings@2195: var self = this; nicholas@2538: get(this.parent.selectedURL).then(function (response) { nickjillings@2195: var parse = new DOMParser(); nicholas@2538: testData.specification.decode(parse.parseFromString(response, 'text/xml')); nickjillings@2195: self.parent.parent.generateFilters(testData.specification); nickjillings@2195: self.parent.parent.getFileCount(); nickjillings@2195: return true; nicholas@2538: }, function (error) { nicholas@2538: console.log("ERROR: Could not get" + url); nickjillings@2195: return false; nickjillings@2195: }); nickjillings@2195: } nickjillings@2195: } nickjillings@2195: obj.root.type = "radio"; nickjillings@2195: obj.root.name = "test-include"; nicholas@2538: obj.root.setAttribute("source", test.testName); nicholas@2538: obj.root.addEventListener("change", obj); nickjillings@2195: tableRowInclude.appendChild(obj.root); nickjillings@2195: tableRow.appendChild(tableRowInclude); nickjillings@2195: table.appendChild(tableRow); nickjillings@2195: self.testSaves.inputs.push(obj); nickjillings@2195: } nickjillings@2195: document.getElementById("test-saved").appendChild(table); nicholas@2538: }, function (error) { nicholas@2538: console.error(error); nicholas@2538: }); nickjillings@2195: } nicholas@2538: nickjillings@2192: this.filterDOM = document.createElement("div"); nickjillings@2192: this.filterDOM.innerHTML = "

PreTest Filters

"; nickjillings@2192: this.filterObjects = []; nicholas@2538: this.generateFilters = function (specification) { nickjillings@2192: // Filters are based on the pre and post global surverys nicholas@2538: var FilterObject = function (parent, specification) { nickjillings@2192: this.parent = parent; nickjillings@2192: this.specification = specification; nickjillings@2192: this.rootDOM = document.createElement("div"); nicholas@2538: this.rootDOM.innerHTML = "ID: " + specification.id + ", Type: " + specification.type + ""; nickjillings@2192: this.rootDOM.className = "filter-entry"; nicholas@2538: this.handleEvent = function (event) { nicholas@2538: switch (this.specification.type) { nickjillings@2192: case "number": nickjillings@2192: var name = event.currentTarget.name; nicholas@2538: eval("this." + name + " = event.currentTarget.value"); nickjillings@2192: break; nickjillings@2192: case "checkbox": nickjillings@2192: break; nickjillings@2192: case "radio": nickjillings@2192: break; nickjillings@2192: } nickjillings@2192: this.parent.getFileCount(); nickjillings@2192: } nicholas@2538: this.getFilterPairs = function () { nickjillings@2192: var pairs = []; nicholas@2538: switch (this.specification.type) { nickjillings@2192: case "number": nickjillings@2192: if (this.min != "") { nicholas@2538: pairs.push([specification.id + "-min", this.min]); nickjillings@2192: } nickjillings@2192: if (this.max != "") { nicholas@2538: pairs.push([specification.id + "-max", this.max]); nickjillings@2192: } nickjillings@2192: break; nickjillings@2192: case "radio": nickjillings@2192: case "checkbox": nicholas@2538: for (var i = 0; i < this.options.length; i++) { nickjillings@2192: if (!this.options[i].checked) { nicholas@2538: pairs.push([specification.id + "-exclude-" + i, specification.options[i].name]); nickjillings@2192: } nickjillings@2192: } nickjillings@2192: break; nickjillings@2192: } nickjillings@2192: return pairs; nickjillings@2192: } nicholas@2538: switch (specification.type) { nickjillings@2192: case "number": nickjillings@2192: // Number can be ranged by min/max levels nickjillings@2192: this.min = ""; nickjillings@2192: this.max = ""; nickjillings@2192: this.minDOM = document.createElement("input"); nickjillings@2192: this.minDOM.type = "number"; nickjillings@2192: this.minDOM.name = "min"; nicholas@2538: this.minDOM.addEventListener("change", this); nickjillings@2192: this.minDOMText = document.createElement("span"); nickjillings@2192: this.minDOMText.textContent = "Minimum: "; nickjillings@2192: var pairHolder = document.createElement("div"); nickjillings@2192: pairHolder.appendChild(this.minDOMText); nickjillings@2192: pairHolder.appendChild(this.minDOM); nickjillings@2192: this.rootDOM.appendChild(pairHolder); nicholas@2538: nickjillings@2192: this.maxDOM = document.createElement("input"); nickjillings@2192: this.maxDOM.type = "number"; nickjillings@2192: this.maxDOM.name = "max"; nicholas@2538: this.maxDOM.addEventListener("change", this); nickjillings@2192: this.maxDOMText = document.createElement("span"); nickjillings@2192: this.maxDOMText.textContent = "Maximum: "; nickjillings@2192: var pairHolder = document.createElement("div"); nickjillings@2192: pairHolder.appendChild(this.maxDOMText); nickjillings@2192: pairHolder.appendChild(this.maxDOM); nickjillings@2192: this.rootDOM.appendChild(pairHolder); nickjillings@2192: break; nickjillings@2192: case "radio": nickjillings@2192: case "checkbox": nickjillings@2192: this.options = []; nicholas@2538: for (var i = 0; i < specification.options.length; i++) { nickjillings@2192: var option = specification.options[i]; nickjillings@2192: var pairHolder = document.createElement("div"); nickjillings@2192: var text = document.createElement("span"); nickjillings@2192: text.textContent = option.text; nickjillings@2192: var check = document.createElement("input"); nickjillings@2192: check.type = "checkbox"; nicholas@2538: check.setAttribute("option-index", i); nickjillings@2192: check.checked = true; nicholas@2538: check.addEventListener("click", this); nickjillings@2192: this.options.push(check); nickjillings@2192: pairHolder.appendChild(text); nickjillings@2192: pairHolder.appendChild(check); nickjillings@2192: this.rootDOM.appendChild(pairHolder); nickjillings@2192: } nickjillings@2192: break; nickjillings@2192: default: nickjillings@2192: break; nickjillings@2192: } nickjillings@2192: } nicholas@2251: var options = []; nicholas@2538: if (specification.preTest) { nicholas@2251: options = options.concat(specification.preTest.options); nicholas@2251: } nicholas@2251: if (specification.postTest) { nicholas@2251: options = options.concat(specification.postTest.options); nicholas@2251: } nicholas@2251: for (var survey_entry of options) { nicholas@2538: switch (survey_entry.type) { nickjillings@2192: case "number": nickjillings@2192: case "radio": nickjillings@2192: case "checkbox": nicholas@2538: var node = new FilterObject(this, survey_entry); nickjillings@2192: this.filterObjects.push(node); nickjillings@2192: this.filterDOM.appendChild(node.rootDOM); nickjillings@2192: break; nickjillings@2192: default: nickjillings@2192: break; nickjillings@2192: } nickjillings@2192: } nickjillings@2192: document.getElementById("test-saved").appendChild(this.filterDOM); nickjillings@2193: document.getElementById("test-saved").appendChild(this.getDataButton.button); nicholas@2287: document.getElementById("test-saved").appendChild(this.getRawScoreData.root); nickjillings@2192: } nicholas@2538: this.getFilterString = function () { nickjillings@2192: var pairs = []; nickjillings@2192: for (var obj of this.filterObjects) { nickjillings@2192: pairs = pairs.concat(obj.getFilterPairs()); nickjillings@2192: } nicholas@2538: var req_str = "?url=" + this.testSaves.selectedURL; nickjillings@2192: var index = 0; nicholas@2538: while (pairs[index] != undefined) { nickjillings@2192: req_str += '&'; nicholas@2538: req_str += pairs[index][0] + "=" + pairs[index][1]; nickjillings@2192: index++; nickjillings@2192: } nickjillings@2193: return req_str; nickjillings@2193: } nicholas@2538: this.getFilteredUrlArray = function () { nicholas@2538: var req_str = "../php/get_filtered_count.php" + this.getFilterString(); nicholas@2538: return get(req_str).then(function (response) { nickjillings@2192: var urls = JSON.parse(response); nickjillings@2193: return urls.urls; nicholas@2538: }, function (error) { nickjillings@2193: console.error(error); nickjillings@2193: }); nickjillings@2193: } nicholas@2538: this.getFileCount = function () { nickjillings@2193: // First we must get the filter pairs nicholas@2538: this.getFilteredUrlArray().then(function (response) { nicholas@2538: var str = "Filtered to " + response.length + " file"; nickjillings@2193: if (response.length != 1) { nickjillings@2192: str += "s."; nickjillings@2192: } else { nickjillings@2192: str += "."; nickjillings@2192: } nickjillings@2192: document.getElementById("filter-count").textContent = str; nicholas@2538: }, function (error) {}); nickjillings@2192: } nicholas@2538: nickjillings@2195: this.init(); nicholas@2538: }