416 lines
13 KiB
JavaScript
416 lines
13 KiB
JavaScript
/**
|
|
* manages the connection between the data and the chart
|
|
* @namespace Process
|
|
*/
|
|
var Process = (function (process) {
|
|
'use strict';
|
|
var ns = process;
|
|
|
|
process.globals = {
|
|
resetProperty:"ssnipSettings",
|
|
purchaseLevel:'ssnipLevel',
|
|
fullAccess:10,
|
|
openAccess:true,
|
|
version:"2.2.0.0"
|
|
};
|
|
|
|
process.applyElementer = function () {
|
|
var before = Utils.clone (process.control.sankey.settings);
|
|
process.control.sankey.settings = Sankey.mapSettings (process.control.sankey.elementer);
|
|
return JSON.stringify(before) !== JSON.stringify(process.control.sankey.settings);
|
|
};
|
|
|
|
|
|
process.initialize = function () {
|
|
|
|
var sankeySetup = Sankey.setup();
|
|
|
|
// this will create the structure for retrieving settings
|
|
var elementer = Sankey.doElementer(sankeySetup);
|
|
var elements = elementer.getElements();
|
|
|
|
|
|
process.control = {
|
|
result: {
|
|
data:undefined,
|
|
checksum:undefined
|
|
},
|
|
sankey:{
|
|
setup:sankeySetup,
|
|
elementer:elementer,
|
|
settings:{},
|
|
store:{
|
|
useInitial:null,
|
|
useStandard:null,
|
|
useUser:null,
|
|
useDocument:null,
|
|
reset:{
|
|
}
|
|
},
|
|
testData:getTestData(),
|
|
auth:{},
|
|
premium:false,
|
|
exportName:function () {
|
|
return 'sankeySnipExport-'+(new Date().getTime());
|
|
}
|
|
},
|
|
|
|
|
|
tabs: {
|
|
settings:DomUtils.elem("elementer-root")
|
|
},
|
|
|
|
activeHeadings: {
|
|
from:{
|
|
current:'',
|
|
elem:elements.controls.fromColumn,
|
|
best:'Source'
|
|
},
|
|
to: {
|
|
current:'',
|
|
elem:elements.controls.toColumn,
|
|
best:'Target'
|
|
},
|
|
value:{
|
|
current:'',
|
|
elem:elements.controls.weightColumn,
|
|
best:'Volume'
|
|
}
|
|
},
|
|
|
|
chart: {
|
|
headings:[],
|
|
data:[[]],
|
|
elem:DomUtils.elem("chart"),
|
|
ghost:DomUtils.elem("ghost"),
|
|
instructions:DomUtils.elem("instructions"),
|
|
mainButtons:DomUtils.elem("main-buttons"),
|
|
defOptions:{
|
|
height:DomUtils.elem("chart").offsetHeight,
|
|
width:DomUtils.elem("chart").offsetWidth
|
|
}
|
|
},
|
|
|
|
code: {
|
|
svg:elements.controls.svgCode
|
|
},
|
|
|
|
buttons: {
|
|
insert:DomUtils.elem("insert-button"),
|
|
close:DomUtils.elem("close-button"),
|
|
export:DomUtils.elem("export-button"),
|
|
manage:elements.controls.manageButton,
|
|
apply:elements.controls.applyButton,
|
|
generate:DomUtils.elem("generate-button"),
|
|
reset:[
|
|
elements.controls.resetButton_arrangePreview,
|
|
elements.controls.resetButton_scaleRatio,
|
|
elements.controls.resetButton_nodes,
|
|
elements.controls.resetButton_links,
|
|
elements.controls.resetButton_tooltips,
|
|
elements.controls.resetButton_dataSettings
|
|
],
|
|
selectedRange:elements.controls.selectedRange,
|
|
wholeSheet:elements.controls.wholeSheet
|
|
},
|
|
|
|
watching: {
|
|
watcher:ClientWatcher.addWatcher({
|
|
pollingFrequency:3000,
|
|
watch:{sheets:false,active:false},
|
|
domain:{fiddler:false,scope:elements.controls.wholeSheet.checked ? "Sheet" : "Active"}
|
|
})
|
|
},
|
|
|
|
toast: {
|
|
interval:3500
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* test data for sample sheet
|
|
*/
|
|
function getTestData() {
|
|
return [["Source", "Target", "Volume"], ["CHP", "Electricity", 58], ["CHP", "Losses", 60], ["Electricity", "Domestic", 71], ["Electricity", "Industry", 87], ["Electricity", "Other final", 77], ["Electricity", "Losses", 42], ["Electricity", "Non-energy", 13], ["Natural gas", "CHP", 62], ["Natural gas", "Industry", 90], ["Natural gas", "Losses", 22], ["Natural gas", "Non-energy", 13], ["Natural gas", "Power", 53], ["Coal", "CHP", 61], ["Coal", "Other", 64], ["Coal", "Power", 153], ["Crude oil", "Export", 58], ["Crude oil", "Refineries", 573], ["Nuclear", "Power", 228], ["Petroleum products", "Refineries", 324], ["Power", "Electricity", 222], ["Power", "Losses", 285], ["Refineries", "Domestic", 38], ["Refineries", "Export", 370], ["Refineries", "Industry", 30], ["Refineries", "Non-energy", 84], ["Refineries", "Other final", 33], ["Refineries", "Transport", 329], ["Renewables", "CHP", 75], ["Renewables", "Domestic", 41], ["Renewables", "Other final", 5], ["Renewables", "Power", 74], ["Renewables", "Transport", 15], ["CHP", "Industry", 80]]
|
|
}
|
|
// get and apply any stored properties
|
|
return new Promise (function (resolve, reject) {
|
|
|
|
Provoke.run ('Props','getAll')
|
|
.then( function (result) {
|
|
var pc = Process.control.sankey.store;
|
|
|
|
// the factory settings
|
|
pc.useStandard = elementer.getStandard();
|
|
pc.auth = result.auth;
|
|
|
|
// any data found in property stores
|
|
result.saved.forEach(function(d) {
|
|
pc[d.source] = d.settings;
|
|
elements.controls[d.source].disabled = d.settings ? false : true;
|
|
if (d.settings) {
|
|
elementer.setInitial (d.settings);
|
|
}
|
|
});
|
|
|
|
|
|
// the finally decided upon initial values
|
|
pc.useInitial = elementer.getInitial();
|
|
|
|
// use the defaults for the preview chart dimensions
|
|
if(!parseInt(elements.controls.previewHeight.value,10)){
|
|
elements.controls.previewHeight.value = Process.control.chart.defOptions.height;
|
|
}
|
|
if(!parseInt(elements.controls.previewWidth.value,10)){
|
|
elements.controls.previewWidth.value = Process.control.chart.defOptions.width;
|
|
}
|
|
|
|
// map the values
|
|
process.applyElementer();
|
|
resolve (elementer);
|
|
})
|
|
['catch'](function (err) {
|
|
App.showNotification ("failed while getting saved properties ", err);
|
|
reject (err);
|
|
});
|
|
});
|
|
|
|
};
|
|
|
|
/**
|
|
* will be called to restore any values reserverd for this page
|
|
* @param {Elementer} elementer the elementer
|
|
* @param {string} branch the branchname
|
|
*/
|
|
process.restoreResetValues = function (elementer , branch) {
|
|
|
|
// merge them with the current settings and apply them to the chart
|
|
|
|
if (Process.control.sankey.store.reset[branch]) {
|
|
elementer.applySettings(Utils.vanMerge([elementer.getCurrent(),Process.control.sankey.store.reset[branch]]));
|
|
}
|
|
|
|
|
|
}
|
|
/**
|
|
* will be called on entering a page that needs preserved to store initial values affected by that page
|
|
* @param {Elementer} elementer the elementer
|
|
* @param {string} branch the branchname
|
|
*/
|
|
process.reserveResetValues = function (elementer, branch) {
|
|
|
|
// all the current values
|
|
var current = elementer.getCurrent();
|
|
|
|
// the items on this page
|
|
var items = elementer.getLayout().pages[branch].items;
|
|
|
|
|
|
// store the values on this page
|
|
Process.control.sankey.store.reset[branch] = items.reduce(function (p,c) {
|
|
if (current.hasOwnProperty(c)) p[c] = current[c];
|
|
return p;
|
|
},{});
|
|
|
|
|
|
|
|
}
|
|
/**
|
|
* draw a sankey chart from the matrix data
|
|
*/
|
|
process.drawChart = function () {
|
|
|
|
//disable inserting.. during construction
|
|
process.control.buttons.insert.disabled = true;
|
|
|
|
// clear the chart first
|
|
var clear = true;
|
|
|
|
|
|
var sc = process.control.chart;
|
|
var sts = process.control.sankey.settings;
|
|
var opt = Utils.vanMerge([sc.defOptions, sts.options]);
|
|
|
|
var svg;
|
|
|
|
if (sc.data.length) {
|
|
|
|
opt.height = opt.height || sc.defOptions.height;
|
|
opt.width = opt.width || sc.defOptions.width;
|
|
|
|
// the preview
|
|
Sankey.drawChart (opt , sc.headings, sc.data, sc.elem, clear);
|
|
|
|
// the full sized chart
|
|
svg = scaleChart(clear);
|
|
process.control.code.svg.value = "";
|
|
if (svg && svg.length) {
|
|
// need to tweak any gradient code so it works outside context of apps script
|
|
process.control.code.svg.value = svg[0].replace (/url\([^#]+/g, "url(");
|
|
}
|
|
DomUtils.hide (sc.instructions,true);
|
|
DomUtils.hide(sc.mainButtons,false);
|
|
DomUtils.hide (sc.elem,false);
|
|
|
|
// enable inserting
|
|
process.control.buttons.insert.disabled = false;
|
|
|
|
}
|
|
else {
|
|
// no viable data so reveal instructions and disable insertion
|
|
process.control.buttons.insert.disabled = true;
|
|
DomUtils.hide (sc.elem,true);
|
|
DomUtils.hide (sc.instructions,false);
|
|
DomUtils.hide (sc.mainButtons,true);
|
|
|
|
}
|
|
|
|
function scaleChart(clear, divScale) {
|
|
|
|
// scale up the real one
|
|
var big = Utils.clone(sts);
|
|
var scale = divScale || big.scale;
|
|
|
|
var big = Utils.vanMerge([opt, {
|
|
height: scale.height,
|
|
width: scale.width,
|
|
sankey: {
|
|
node: {
|
|
label: {
|
|
fontSize: scale.font
|
|
},
|
|
labelPadding: opt.sankey.node.labelPadding /opt.sankey.node.label.fontSize * scale.font,
|
|
nodePadding: opt.sankey.node.nodePadding /opt.height * scale.height,
|
|
width: opt.sankey.node.width/ opt.width * scale.width
|
|
}
|
|
}
|
|
}]);
|
|
|
|
|
|
|
|
Sankey.drawChart (big , sc.headings, sc.data, sc.ghost, clear);
|
|
// set up svg code for copying
|
|
return sc.ghost.innerHTML.match(/\<svg.*svg\>/);
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
process.selectFields = function () {
|
|
var sc = process.control;
|
|
|
|
// goodheadings will contain the potential headings
|
|
var goodHeadings = sc.headings.filter(function(d,i,a) {
|
|
return a.indexOf(d) === i && d;
|
|
});
|
|
|
|
// make active headings/ deleting them if not there, along with the chart headings
|
|
sc.chart.headings = Object.keys(sc.activeHeadings).map (function(k) {
|
|
|
|
// the object describing each data column
|
|
var d = sc.activeHeadings[k];
|
|
var options = DomUtils.getOptions(d.elem);
|
|
|
|
// sliced the first one which is a null option if it exists
|
|
if (!options.length || !Utils.isSameAs (goodHeadings, options.slice(1))) {
|
|
|
|
// we have a potentially new set of options, rebuild the select
|
|
var selected = d.elem.value;
|
|
|
|
d.elem.innerHTML= "";
|
|
d.current = d.elem.value = "";
|
|
|
|
// add a null option
|
|
DomUtils.addElem (d.elem,"option");
|
|
goodHeadings.forEach(function (e) {
|
|
var op = DomUtils.addElem (d.elem,"option");
|
|
op.value = e;
|
|
op.text = e;
|
|
});
|
|
|
|
// and if currently selected is no longer in the list then we need to cancel it
|
|
if (goodHeadings.indexOf(selected) === -1) {
|
|
d.elem.value = d.current = "";
|
|
}
|
|
|
|
else if (selected) {
|
|
d.current = d.elem.value = selected;
|
|
}
|
|
}
|
|
return d;
|
|
})
|
|
.map (function (d,i,a) {
|
|
// now we need to deduce new values
|
|
if (!d.current && !a.some(function(e) { return e.current === d.best })) {
|
|
// we dont have a selection for this one, and its not being used by someone else so use the default
|
|
if (goodHeadings.indexOf(d.best) !== -1) {
|
|
d.current = d.best;
|
|
}
|
|
}
|
|
|
|
// if that didnt find anything then use the position
|
|
if (!d.current && goodHeadings.length > i && !a.some(function(e) { return e.current === goodHeadings[i] ; })) {
|
|
d.current = goodHeadings[i];
|
|
}
|
|
|
|
// now set the select value to whatever it now is
|
|
d.elem.value = d.current;
|
|
return d.current;
|
|
});
|
|
|
|
// make the chart data
|
|
var sh = sc.chart.headings;
|
|
|
|
sc.chart.data = sh.every(function(d) { return d;}) && Utils.unique(sh).length === Object.keys(sc.activeHeadings).length ?
|
|
sc.dataObjects.map ( function (row) {
|
|
return sh.map(function(d) {
|
|
return row[d];
|
|
})
|
|
}) : [];
|
|
|
|
}
|
|
/**
|
|
* fix up and store data received from server
|
|
* @param {object} result the result
|
|
*/
|
|
process.syncResult = function (result) {
|
|
|
|
var sc = process.control;
|
|
|
|
// store it
|
|
sc.result = result;
|
|
// in syncresult
|
|
|
|
// make the headings
|
|
if (result.data && (result.data.length || result.clear)) {
|
|
|
|
sc.headings = result.data.length ? result.data[0] : [];
|
|
sc.data = result.data.length > 1 ? result.data.slice(1) : [];
|
|
// starting chart proceeedings
|
|
|
|
|
|
// make the data into k.v pairs
|
|
sc.dataObjects = sc.data.map(function (row) {
|
|
var i = 0;
|
|
return sc.headings.reduce (function (p,c) {
|
|
p[c] = row[i++];
|
|
return p;
|
|
},{});
|
|
});
|
|
|
|
process.selectFields();
|
|
process.drawChart();
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
return process;
|
|
|
|
})( Process || {} );
|