diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..2418563
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+ "env": {
+ "commonjs": true,
+ "es2020": true,
+ "node": true
+ },
+ "parserOptions": {
+ "ecmaVersion": 12
+ },
+ "rules": {
+ "space-before-function-paren": ["warn", "always"],
+ "space-before-blocks": ["warn", "always"],
+ "func-call-spacing": ["warn", "always"],
+ "object-curly-spacing": ["warn", "never"],
+ "quotes": ["warn", "double"],
+ "indent": ["warn", "tab"]
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 42d07f7..976f33b 100644
--- a/README.md
+++ b/README.md
@@ -198,6 +198,91 @@ xlsxChart.generate (opts, function (err, data) {
});
```
+## Available chart options
+```js
+var opts = {
+ file: "chart.xlsx", // exported file
+ type: "nodebuffer", // optional: used by JSZip library
+ charts: [
+ {
+ chart: "column", // pie, doughnut, line, area, bar
+ grouping: "clustered", // optional: stacked, standard - allow to create stacked bar or line chart
+ titles: [
+ "title1", // list of chart titles
+ "title2", // list of chart titles
+ ],
+ fields: [
+ "field1", // list of chart fields
+ "field2",
+ ],
+ data: {
+ // chart: "column", // could be used to create multi-layer charts with e.g. column + bar + line + area chart in the same object
+ // grouping: "clustered", // same as common grouping option, but only for current layer;
+ "title1": {
+ "field1": 123, // structured data
+ "field2": 321,
+ },
+ "title2": {
+ "field1": 456,
+ "field2": 654,
+ },
+ },
+ chartTitle: "Title",
+ position: { // optional: chart position
+ fromColumn: 0, // chart top left x coordinate in columns
+ fromColumnOffset: 0, // chart top left x coordinate in pixels
+ fromRow: n * 20, // chart top left y coordinate in columns
+ fromRowOffset: 0, // chart top left y coordinate in pixels
+ toColumn: 10, // chart bottom right x coordinate in columns
+ toColumnOffset: 0, // chart bottom right x coordinate in pixels
+ toRow: (n + 1) * 20, // chart bottom right y coordinate in columns
+ toRowOffset: 0, // chart bottom right y coordinate in pixels
+ },
+ customColors: { // optional: chart colors
+ points: {
+ "title1": {
+ "field1": "FF0000", // colors in the same structure as data; not all points required
+ "field1": {
+ fill: "FF0000", // separate colors for fill and border could be set
+ line: "FF0000",
+ },
+ },
+ },
+ series: {
+ "title1": "FF0000", // whole series color could be also set
+ "title2": {
+ fill: "FF0000", // separate colors for fill and border could be set
+ line: "FF0000",
+ markerColor: "FF0000", // different marker color also could be set for line chart
+ },
+ },
+ },
+ manualLayout: { // optional blocks position on the chart canvas
+ plotArea: { // chart position
+ x: 0.5, // x and y of top left corner in proportion value 0..1
+ y: 0.5,
+ w: 0.5, // width and height of block point in proportion value 0..1
+ h: 0.5,
+ },
+ title: {
+ x: 0.5, // x and y of top left corner in proportion value 0..1
+ y: 0.5,
+ },
+ },
+ firstSliceAng: 270, // optional: first slice angle for pie and doughnut chart in degrees
+ holeSize: 50, // optional: hole size of doughnut chart in percent
+ legendPos: "r", // optional: 'l', 't', 'r', 'b', null - legend position; null to hide; right is default
+ deleteEmptyCells: false, // optional: allow to will empty values with 0 or leave as gaps
+ },
+ ],
+};
+
+xlsxChart.writeFile (opts, function (err) {
+ console.log ("File: ", opts.file);
+});
+
+```
+
## Examples
column.js
diff --git a/chart/base.js b/chart/base.js
index c780587..52d0aaf 100644
--- a/chart/base.js
+++ b/chart/base.js
@@ -5,6 +5,47 @@ var xml2js = require ("xml2js");
var VError = require ("verror");
var fs = require ("fs");
var async = require ("async");
+
+const CHART_TAG_BY_CHART_NAME = {
+ bar: "c:barChart",
+ column: "c:barChart",
+ line: "c:lineChart",
+ radar: "c:radarChart",
+ area: "c:areaChart",
+ scatter: "c:scatterChart",
+ pie: "c:pieChart",
+ doughnut: "c:doughnutChart",
+};
+
+const CHART_GROUPING_BY_CHART_NAME = {
+ bar: "clustered",
+ column: "clustered",
+ line: "standard",
+ radar: undefined, // radar, scatter, pie and doughnut charts should not have "c:grouping" tag, otherwise rest of xml is ignored
+ area: "standard",
+ scatter: undefined,
+ pie: undefined,
+ doughnut: undefined,
+};
+
+const CHART_TYPES = ["bar", "column", "line", "radar", "area", "scatter", "pie", "doughnut"];
+
+/**
+ *
+ * @param {number | null | undefined} value
+ * @param {{ deleteEmptyCells?: boolean }} opts
+ * @returns
+ */
+function normalizeValue (value, opts) {
+ if (!opts.deleteEmptyCells) {
+ return value || 0;
+ }
+ if (value || value === 0) {
+ return value;
+ }
+ return "";
+}
+
var Chart = Backbone.Model.extend ({
/*
Read XML file from xlsx as object
@@ -18,7 +59,7 @@ var Chart = Backbone.Model.extend ({
return new VError (err, "getXML");
}
cb (err, o);
- });
+ });
},
/*
Build XML from object and write to zip
@@ -27,18 +68,18 @@ var Chart = Backbone.Model.extend ({
var me = this;
var builder = new xml2js.Builder ();
var xml = builder.buildObject (opts.object);
- me.zip.file (opts.file, new Buffer (xml), {base64: true});
+ me.zip.file (opts.file, Buffer.from (xml), {base64: true});
},
/*
Get column name
*/
getColName: function (n) {
var abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- n --;
+ n--;
if (n < 26) {
- return abc [n];
+ return abc[n];
} else {
- return abc [(n / 26 - 1) | 0] + abc [n % 26];
+ return abc[(n / 26 - 1) | 0] + abc[n % 26];
}
},
/*
@@ -49,7 +90,7 @@ var Chart = Backbone.Model.extend ({
if (!me.str.hasOwnProperty (s)) {
throw new VError ("getStr: Unknown string: " + s);
}
- return me.str [s];
+ return me.str[s];
},
/*
Write table
@@ -95,7 +136,7 @@ var Chart = Backbone.Model.extend ({
$: {
r: me.getColName (x + 2) + (y + 2)
},
- v: me.data [t][f]
+ v: me.data[t][f]
});
});
r.c = c;
@@ -111,13 +152,13 @@ var Chart = Backbone.Model.extend ({
*/
writeMultTable: function (row, cb) {
let me = this;
-
+
me.read ({file: "xl/worksheets/sheet2.xml"}, function (err, o) {
if (err) {
return cb (new VError (err, "writeMultTable"));
}
o.worksheet.dimension.$.ref = `A${row}:${me.getColName (me.titles.length + 1)}${me.fields.length + row + 1}`;
-
+
let rows = [{
$: {
r: row,
@@ -164,7 +205,7 @@ var Chart = Backbone.Model.extend ({
$: {
r: me.getColName (x + 2) + (y + 2 + row)
},
- v: me.data [t][f]
+ v: me.data[t][f]
});
});
r.c = c;
@@ -184,16 +225,16 @@ var Chart = Backbone.Model.extend ({
*/
writeStrings: function (cb) {
let me = this;
-
+
me.read ({file: "xl/sharedStrings.xml"}, function (err, o) {
if (err) {
return cb (new VError (err, "writeStrings"));
}
o.sst.$.count = me.titles.length + me.fields.length;
o.sst.$.uniqueCount = o.sst.$.count;
-
+
let si = [];
-
+
_.each (me.titles, function (t) {
si.push ({t: t});
});
@@ -201,9 +242,9 @@ var Chart = Backbone.Model.extend ({
si.push ({t: t});
});
me.str = {};
-
+
_.each (si, function (o, i) {
- me.str [o.t] = i;
+ me.str[o.t] = i;
});
o.sst.si = si;
me.write ({file: "xl/sharedStrings.xml", object: o});
@@ -215,57 +256,71 @@ var Chart = Backbone.Model.extend ({
*/
removeUnusedCharts: function (o) {
var me = this;
- if (me.tplName != "charts") {
- return;
- };
+
+ // axis ids that are used in chart layers
var axId = [];
function addId (o) {
+ if (!o ["c:axId"]) {
+ return;
+ }
_.each (o ["c:axId"], function (o) {
axId.push (o.$.val);
});
};
- _.each (["line", "radar", "area", "scatter", "pie"], function (chart) {
- if (!me.charts [chart]) {
- delete o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:" + chart + "Chart"];
+
+ // remove chart layers that are not used in chart
+ _.each (CHART_TYPES, function (chart) {
+ if (chart == "column") {
+ chart = "bar";
+ }
+
+ if (!o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:" + chart + "Chart"]) {
+ return;
+ }
+
+ if (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:" + chart + "Chart"].length) {
+ _.each (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:" + chart + "Chart"], addId);
} else {
addId (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:" + chart + "Chart"]);
- };
+ }
});
- if (!me.charts ["column"] && !me.charts ["bar"]) {
- delete o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"];
- } else
- if (me.charts ["column"] && !me.charts ["bar"]) {
- o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"] = o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][0];
- addId (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"]);
- } else
- if (!me.charts ["column"] && me.charts ["bar"]) {
- o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"] = o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][1];
- addId (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"]);
- } else {
- addId (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][0]);
- addId (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][1]);
- };
- var catAx = [];
- _.each (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"], function (o) {
- if (axId.indexOf (o ["c:axId"].$.val) > -1) {
- catAx.push (o);
- };
- });
- o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"] = catAx;
+ if (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"]) {
+ var catAx = [];
+ // if category axes is single - it's an object, otherwise it's an array of objects
+ if (!o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"].length) {
+ o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"] = [o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"]];
+ }
- var valAx = [];
- _.each (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"], function (o) {
- if (axId.indexOf (o ["c:axId"].$.val) > -1) {
- valAx.push (o);
- };
- });
- o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"] = valAx;
+ // leave only category axes that are used in chart
+ _.each (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"], function (o) {
+ if (axId.indexOf (o ["c:axId"].$.val) > -1) {
+ catAx.push (o);
+ };
+ });
+ o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:catAx"] = catAx;
+ }
+
+ if (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"]) {
+ var valAx = [];
+ // if value axes is single - it's an object, otherwise it's an array of objects
+ if (!o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"].length) {
+ o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"] = [o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"]];
+ }
+
+ // leave only value axes that are used in chart
+ _.each (o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"], function (o) {
+ if (axId.indexOf (o ["c:axId"].$.val) > -1) {
+ valAx.push (o);
+ };
+ });
+ o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:valAx"] = valAx;
+ }
},
/*
Write chart
*/
-/*
+ /*
writeChart: function (chartN, cb) {
var me = this;
@@ -391,18 +446,123 @@ var Chart = Backbone.Model.extend ({
cb ();
});
},
-*/
+ */
writeChart: function (chartN, row, cb) {
var me = this;
-
+
me.read ({file: "xl/charts/chart1.xml"}, function (err, o) {
if (err) {
return cb (new VError (err, "writeChart"));
}
- var ser = {};
+ var seriesByChart = {};
+ const chartOpts = me.charts[chartN - 1];
_.each (me.titles, function (t, i) {
- var chart = me.data [t].chart || me.chart;
- var r = {
+ var chart = me.data[t].chart || me.chart;
+ var grouping = me.data[t].grouping || me.grouping || CHART_GROUPING_BY_CHART_NAME[chart];
+
+ var customColorsPoints = {
+ "c:dPt": [],
+ };
+ var customColorsSeries = {};
+
+ if (chartOpts.customColors) {
+ const customColors = chartOpts.customColors;
+
+ if (customColors.points) {
+ customColorsPoints["c:dPt"] = chartOpts.fields.map (function (field, i) {
+ const color = _.chain (customColors).get ("points").get (t).get (field, null).value ();
+
+ if (!color) {
+ return null;
+ }
+ if (color === "noFill") {
+ return {
+ "c:idx": {
+ $: {
+ val: i,
+ },
+ },
+ "c:spPr": {
+ "a:noFill": ""
+ }
+ }
+ }
+ let fillColor = color;
+ let lineColor = color;
+ if (typeof color === "object") {
+ fillColor = color.fill;
+ lineColor = color.line;
+ }
+ return {
+ "c:idx": {
+ $: {
+ val: i,
+ },
+ },
+ "c:spPr": {
+ "a:solidFill": {
+ "a:srgbClr": {
+ $: {
+ val: fillColor,
+ },
+ },
+ },
+ "a:ln": {
+ "a:solidFill": {
+ "a:srgbClr": {
+ $: {
+ val: lineColor,
+ },
+ },
+ },
+ },
+ },
+ }
+ }).filter (Boolean);
+ }
+
+ if (customColors.series && customColors.series[t]) {
+ let fillColor = customColors.series[t];
+ let lineColor = customColors.series[t];
+ let markerColor = customColors.series[t];
+ if (typeof customColors.series === "object") {
+ fillColor = customColors.series[t].fill;
+ lineColor = customColors.series[t].line;
+ markerColor = customColors.series[t].marker;
+ }
+ customColorsSeries["c:spPr"] = {
+ "a:solidFill": {
+ "a:srgbClr": {
+ $: {
+ val: fillColor,
+ },
+ },
+ },
+ "a:ln": {
+ "a:solidFill": {
+ "a:srgbClr": {
+ $: {
+ val: lineColor,
+ },
+ },
+ },
+ },
+ "c:marker": {
+ "c:spPr": {
+ "a:solidFill": {
+ "a:srgbClr": {
+ $: {
+ val: markerColor,
+ },
+ },
+ },
+ },
+ },
+ };
+ }
+ }
+
+ var ser = {
"c:idx": {
$: {
val: i
@@ -431,6 +591,8 @@ var Chart = Backbone.Model.extend ({
}
}
},
+ ...customColorsPoints,
+ ...customColorsSeries,
"c:cat": {
"c:strRef": {
"c:f": "Table!$A$" + (row + 1) + ":$A$" + (me.fields.length + row),
@@ -466,7 +628,7 @@ var Chart = Backbone.Model.extend ({
$: {
idx: j
},
- "c:v": me.data [t][f]
+ "c:v": me.data[t][f]
};
})
}
@@ -474,11 +636,11 @@ var Chart = Backbone.Model.extend ({
}
};
if (chart == "scatter") {
- r ["c:xVal"] = r ["c:cat"];
- delete r ["c:cat"];
- r ["c:yVal"] = r ["c:val"];
- delete r ["c:val"];
- r ["c:spPr"] = {
+ ser["c:xVal"] = ser["c:cat"];
+ delete ser["c:cat"];
+ ser["c:yVal"] = ser["c:val"];
+ delete ser["c:val"];
+ ser["c:spPr"] = {
"a:ln": {
$: {
w: 28575
@@ -487,31 +649,176 @@ var Chart = Backbone.Model.extend ({
}
};
};
- ser [chart] = ser [chart] || [];
- ser [chart].push (r);
+ const seriesKey = `${chart}\r\r${grouping}`;
+ seriesByChart[seriesKey] = seriesByChart[seriesKey] || [];
+ seriesByChart[seriesKey].push (ser);
});
- _.each (ser, function (ser, chart) {
- if (chart == "column") {
- if (me.tplName == "charts") {
- o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][0]["c:ser"] = ser;
- } else {
- o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"]["c:ser"] = ser;
- };
- } else
- if (chart == "bar") {
- if (me.tplName == "charts") {
- o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"][1]["c:ser"] = ser;
- } else {
- o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:barChart"]["c:ser"] = ser;
+
+ const templateChartName = "c:" + CHART_TYPES.find ((chartType) => me.chartTemplate ["c:chartSpace"]["c:chart"]["c:plotArea"][`c:${chartType}Chart`]) + "Chart";
+
+ // remove template barChart from the XML object;
+ o ["c:chartSpace"]["c:chart"]["c:plotArea"][templateChartName] = [];
+
+ _.each (seriesByChart, function (ser, chart) {
+
+ var [chart, grouping] = chart.split ("\r\r");
+
+ const chartTagName = CHART_TAG_BY_CHART_NAME[chart];
+
+ if (!chartTagName) {
+ return cb (new VError (new Error (`Chart type '${chart}' is not supported`), "writeChart"));
+ }
+
+ o ["c:chartSpace"]["c:chart"]["c:plotArea"][chartTagName] = o ["c:chartSpace"]["c:chart"]["c:plotArea"][chartTagName] || [];
+ // minimal chart config
+ let newChart = {};
+
+ if (chart == "column" || chart == "bar") {
+ // clone barChart from template
+ newChart = _.clone (me.chartTemplate ["c:chartSpace"]["c:chart"]["c:plotArea"][templateChartName]);
+ newChart["c:barDir"] = {
+ $: {
+ val: chart.substr (0, 3),
+ },
};
+
+ } else if (chart == "line" || chart == "area" || chart == "radar" || chart == "scatter") {
+ newChart = _.clone (me.chartTemplate ["c:chartSpace"]["c:chart"]["c:plotArea"][templateChartName]);
+ delete newChart["c:barDir"];
} else {
- o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:" + chart + "Chart"]["c:ser"] = ser;
+ newChart["c:varyColors"] = {
+ $: {
+ val: 1,
+ },
+ };
+
+ newChart["c:ser"] = ser;
+ if (chartOpts.firstSliceAng) {
+ newChart["c:firstSliceAng"] = {
+ $: {
+ val: chartOpts.firstSliceAng,
+ },
+ };
+ }
+ if (chartOpts.holeSize) {
+ newChart["c:holeSize"] = {
+ $: {
+ val: chartOpts.holeSize,
+ },
+ };
+ }
+ // if (chartOpts.showLabels) {
+ // newChart["c:dLbls"] = {
+ // "c:showLegendKey": {
+ // $: {
+ // val: 1,
+ // },
+ // },
+ // "c:showVal": {
+ // $: {
+ // val: 0,
+ // },
+ // },
+ // "c:showCatName": {
+ // $: {
+ // val: 0,
+ // },
+ // },
+ // "c:showSerName": {
+ // $: {
+ // val: 0,
+ // },
+ // },
+ // "c:showPercent": {
+ // $: {
+ // val: 0,
+ // },
+ // },
+ // "c:showBubbleSize": {
+ // $: {
+ // val: 0,
+ // },
+ // },
+ // "c:showLeaderLines": {
+ // $: {
+ // val: 1,
+ // },
+ // },
+ // };
+ // }
};
+
+ newChart["c:ser"] = ser;
+
+ if (grouping != "undefined") {
+ newChart["c:grouping"] = {
+ $: {
+ val: grouping || CHART_GROUPING_BY_CHART_NAME[chart],
+ },
+ };
+ if (grouping == "stacked") {
+ newChart["c:overlap"] = {
+ $: {
+ val: 100, // usually stacked expected to be seen with overlap 100%
+ },
+ };
+ }
+ }
+
+ o ["c:chartSpace"]["c:chart"]["c:plotArea"][chartTagName].push (newChart);
+
+ if (chartOpts.legendPos === undefined || chartOpts.legendPos) {
+ o ["c:chartSpace"]["c:chart"]["c:legend"] = {
+ "c:legendPos": {
+ $: {
+ val: chartOpts.legendPos || "r",
+ },
+ },
+ };
+ } else if (chartOpts.legendPos === null) {
+ delete o ["c:chartSpace"]["c:chart"]["c:legend"];
+ }
+
+ if (chartOpts.manualLayout && chartOpts.manualLayout.plotArea) {
+ o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:layout"] = o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:layout"] || {};
+ o ["c:chartSpace"]["c:chart"]["c:plotArea"]["c:layout"]["c:manualLayout"] = {
+ "c:xMode": {
+ $: {
+ val: "edge",
+ },
+ },
+ "c:yMode": {
+ $: {
+ val: "edge",
+ },
+ },
+ "c:x": {
+ $: {
+ val: chartOpts.manualLayout.plotArea.x,
+ },
+ },
+ "c:y": {
+ $: {
+ val: chartOpts.manualLayout.plotArea.y,
+ },
+ },
+ "c:w": {
+ $: {
+ val: chartOpts.manualLayout.plotArea.w,
+ },
+ },
+ "c:h": {
+ $: {
+ val: chartOpts.manualLayout.plotArea.h,
+ },
+ },
+ };
+ }
});
me.removeUnusedCharts (o);
-
+
if (me.chartTitle) {
- me.writeTitle (o, me.chartTitle);
+ me.writeTitle (o, me.chartTitle, chartOpts);
};
me.write ({file: `xl/charts/chart${chartN}.xml`, object: o});
cb ();
@@ -520,9 +827,36 @@ var Chart = Backbone.Model.extend ({
/*
Chart title
*/
- writeTitle: function (chart, title) {
+ writeTitle: function (o, title, chartOpts = {}) {
var me = this;
- chart ["c:chartSpace"]["c:chart"]["c:title"] = {
+ const layout = {};
+
+ if (chartOpts.manualLayout && chartOpts.manualLayout.title) {
+ layout["c:manualLayout"] = {
+ "c:xMode": {
+ $: {
+ val: "edge",
+ },
+ },
+ "c:yMode": {
+ $: {
+ val: "edge",
+ },
+ },
+ "c:x": {
+ $: {
+ val: chartOpts.manualLayout.title.x,
+ },
+ },
+ "c:y": {
+ $: {
+ val: chartOpts.manualLayout.title.y,
+ },
+ },
+ };
+ }
+
+ o ["c:chartSpace"]["c:chart"]["c:title"] = {
"c:tx": {
"c:rich": {
"a:bodyPr": {},
@@ -542,14 +876,14 @@ var Chart = Backbone.Model.extend ({
}
}
},
- "c:layout": {},
+ "c:layout": layout,
"c:overlay": {
$: {
val: "0"
}
}
};
- chart ["c:chartSpace"]["c:chart"]["c:autoTitleDeleted"] = {
+ o ["c:chartSpace"]["c:chart"]["c:autoTitleDeleted"] = {
$: {
val: "0"
}
@@ -558,30 +892,39 @@ var Chart = Backbone.Model.extend ({
/*
Set template name
*/
- setTemplateName: function () {
+ setTemplateName: function (opts) {
var me = this;
- var charts = {};
- _.each (me.data, function (o) {
- charts [o.chart || me.chart] = true;
+ var chartTypes = {
+ ...getChartTypes (me),
+ };
+
+ _.each (opts.charts, function (chart) {
+ chartTypes = {
+ ...chartTypes,
+ ...getChartTypes (chart),
+ };
});
- me.charts = charts;
- if (charts ["radar"]) {
+ me.charts = opts.charts || [opts];
+
+ me.chartTypes = chartTypes;
+
+ if (chartTypes ["radar"]) {
me.tplName = "radar";
return;
};
- if (charts ["scatter"]) {
+ if (chartTypes["scatter"]) {
me.tplName = "scatter";
return;
};
- if (charts ["pie"]) {
+ if (chartTypes["pie"]) {
me.tplName = "pie";
return;
};
- if (_.keys (charts).length == 1) {
- me.tplName = _.keys (charts) [0];
+ if (_.keys (chartTypes).length == 1) {
+ me.tplName = _.keys (chartTypes)[0];
return;
};
- me.tplName = "charts";
+ me.tplName = "bar";
},
/*
Generate XLSX with chart
@@ -597,25 +940,36 @@ var Chart = Backbone.Model.extend ({
async.series ([
function (cb) {
me.zip = new JSZip ();
- me.setTemplateName ();
+ me.setTemplateName (opts);
let path = me.templatePath ? me.templatePath : (__dirname + "/../template/" + me.tplName + ".xlsx");
- fs.readFile(path, function (err, data) {
+ fs.readFile (path, function (err, data) {
if (err) {
- console.error(`Template ${path} not read: ${err}`);
+ console.error (`Template ${path} not read: ${err}`);
return cb (err);
};
me.zip.load (data);
cb ();
});
},
+ function (cb) {
+ // save template chart so it could be easily removed before adding charts according to config
+ me.read ({file: "xl/charts/chart1.xml"}, function (err, o) {
+ if (err) {
+ return cb (new VError (err, "writeChart"));
+ }
+ me.chartTemplate = o;
+ cb ();
+ });
+ },
function (cb) {
me.writeStrings (cb);
},
function (cb) {
_.each (me.titles, function (t) {
- me.data [t] = me.data [t] || {};
+ me.data[t] = me.data[t] || {};
_.each (me.fields, function (f) {
- me.data [t][f] = me.data [t][f] || (me.deleteEmptyCells ? '' : 0); //deleteEmptyCells - don't display missing values as 0
+ const value = normalizeValue (me.data[t][f], me);
+ me.data[t][f] = value;
});
});
me.writeTable (cb);
@@ -633,19 +987,21 @@ var Chart = Backbone.Model.extend ({
},
generateMult: function (opts, cb) {
let me = this;
-
+
opts.type = opts.type || "nodebuffer";
_.extend (me, opts);
+
+ me.setTemplateName (opts);
async.series ([
function (cb) {
me.zip = new JSZip ();
-
+
let path = me.templatePath || (__dirname + "/../template/mult.xlsx");
-
+
fs.readFile (path, function (err, data) {
if (err) {
- console.error(`Template ${path} not read: ${err}`);
+ console.error (`Template ${path} not read: ${err}`);
return cb (err);
}
me.zip.load (data);
@@ -655,7 +1011,7 @@ var Chart = Backbone.Model.extend ({
function (cb) {
me.titles = [];
me.fields = [];
-
+
me.charts.forEach (chart => {
me.titles = [...me.titles, ...chart.titles];
me.fields = [...me.fields, ...chart.fields];
@@ -665,15 +1021,18 @@ var Chart = Backbone.Model.extend ({
},
function (cb) {
let row = 1;
-
+
async.eachSeries (me.charts, (chart, cb) => {
- ["chart", "titles", "fields", "data", "chartTitle"].forEach (a => me [a] = chart [a]);
-
+ ["chart", "titles", "fields", "data", "chartTitle"].forEach (a => me[a] = chart[a]);
+
+ const deleteEmptyCells = me.deleteEmptyCells || chart.deleteEmptyCells;
+
_.each (me.titles, function (t) {
- me.data [t] = me.data [t] || {};
-
+ me.data[t] = me.data[t] || {};
+
_.each (me.fields, function (f) {
- me.data [t][f] = me.data [t][f] || 0;
+ const value = normalizeValue (me.data[t][f], {deleteEmptyCells});
+ me.data[t][f] = value;
});
});
me.writeMultTable (row, () => {
@@ -685,17 +1044,40 @@ var Chart = Backbone.Model.extend ({
function (cb) {
let n = 0;
let row = 2;
-
+
async.eachSeries (me.charts, (chart, cb) => {
- ["chart", "titles", "fields", "data", "chartTitle"].forEach (a => me [a] = chart [a]);
-
+ ["chart", "titles", "fields", "data", "chartTitle"].forEach (a => me[a] = chart[a]);
+
+ const position = Object.assign ({
+ fromColumn: 0,
+ fromColumnOffset: 0,
+ fromRow: n * 20,
+ fromRowOffset: 0,
+ toColumn: 10,
+ toColumnOffset: 0,
+ toRow: (n + 1) * 20,
+ toRowOffset: 0,
+ },
+ chart.position
+ );
+
async.series ([
function (cb) {
- me.writeChart (++ n, row, cb);
+ // save template chart so it could be easily removed before adding charts according to config
+ me.read ({file: "xl/charts/chart1.xml"}, function (err, o) {
+ if (err) {
+ return cb (new VError (err, "writeChart"));
+ }
+ me.chartTemplate = o;
+ cb ();
+ });
+ },
+ function (cb) {
+ me.writeChart (++n, row, cb);
},
function (cb) {
row += 3 + me.fields.length;
-
+
if (n == 1) {
return cb ();
}
@@ -709,7 +1091,7 @@ var Chart = Backbone.Model.extend ({
"PartName": `/xl/charts/chart${n}.xml`
}
});
- me.write ({file: `[Content_Types].xml`, object: o});
+ me.write ({file: "[Content_Types].xml", object: o});
cb ();
});
},
@@ -731,33 +1113,42 @@ var Chart = Backbone.Model.extend ({
"Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"
}
});
- me.write ({file: `xl/drawings/_rels/drawing1.xml.rels`, object: o});
+ me.write ({file: "xl/drawings/_rels/drawing1.xml.rels", object: o});
cb ();
});
},
function (cb) {
- if (n == 1) {
- return cb ();
- }
me.read ({file: "xl/drawings/drawing1.xml"}, function (err, o) {
if (err) {
return cb (new VError (err, "generateMult"));
}
+ if (n == 1) {
+ o ["xdr:wsDr"]["xdr:twoCellAnchor"]["xdr:from"]["xdr:col"] = position.fromColumn;
+ o ["xdr:wsDr"]["xdr:twoCellAnchor"]["xdr:from"]["xdr:colOff"] = position.fromColumnOffset;
+ o ["xdr:wsDr"]["xdr:twoCellAnchor"]["xdr:from"]["xdr:row"] = position.fromRow;
+ o ["xdr:wsDr"]["xdr:twoCellAnchor"]["xdr:from"]["xdr:rowOff"] = position.fromRowOffset;
+ o ["xdr:wsDr"]["xdr:twoCellAnchor"]["xdr:to"]["xdr:col"] = position.toColumn;
+ o ["xdr:wsDr"]["xdr:twoCellAnchor"]["xdr:to"]["xdr:colOff"] = position.toColumnOffset;
+ o ["xdr:wsDr"]["xdr:twoCellAnchor"]["xdr:to"]["xdr:row"] = position.toRow;
+ o ["xdr:wsDr"]["xdr:twoCellAnchor"]["xdr:to"]["xdr:rowOff"] = position.toRowOffset;
+ me.write ({file: "xl/drawings/drawing1.xml", object: o});
+ return cb ();
+ }
if (n == 2) {
o ["xdr:wsDr"]["xdr:twoCellAnchor"] = [o ["xdr:wsDr"]["xdr:twoCellAnchor"]];
}
o ["xdr:wsDr"]["xdr:twoCellAnchor"].push ({
"xdr:from": {
- "xdr:col": 0,
- "xdr:colOff": 0,
- "xdr:row": (n - 1) * 20,
- "xdr:rowOff": 0
+ "xdr:col": position.fromColumn,
+ "xdr:colOff": position.fromColumnOffset,
+ "xdr:row": position.fromRow,
+ "xdr:rowOff": position.fromRowOffset
},
"xdr:to": {
- "xdr:col": 10,
- "xdr:colOff": 0,
- "xdr:row": n * 20,
- "xdr:rowOff": 0
+ "xdr:col": position.toColumn,
+ "xdr:colOff": position.toColumnOffset,
+ "xdr:row": position.toRow,
+ "xdr:rowOff": position.toRowOffset
},
"xdr:graphicFrame": {
"$": {
@@ -767,7 +1158,7 @@ var Chart = Backbone.Model.extend ({
"xdr:cNvPr": {
"$": {
"id": `${n + 1}`,
- "name": `Диаграмма ${n}`
+ "name": `Diagram ${n}`
}
},
"xdr:cNvGraphicFramePr": {}
@@ -803,7 +1194,7 @@ var Chart = Backbone.Model.extend ({
},
"xdr:clientData": {}
});
- me.write ({file: `xl/drawings/drawing1.xml`, object: o});
+ me.write ({file: "xl/drawings/drawing1.xml", object: o});
cb ();
});
}
@@ -815,9 +1206,27 @@ var Chart = Backbone.Model.extend ({
return cb (new VError (err, "build"));
}
let result = me.zip.generate ({type: me.type});
-
+
cb (null, result);
});
}
});
module.exports = Chart;
+
+/**
+ *
+ * @param {Object} chart
+ * @returns {Object.}
+ */
+const getChartTypes = (chart) => {
+ const chartTypes = {};
+ if (chart.chart) {
+ chartTypes[chart.chart] = true;
+ }
+ _.each (chart.data, function (series) {
+ if (series.chart) {
+ chartTypes[series.chart] = true;
+ }
+ });
+ return chartTypes;
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..210704c
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1056 @@
+{
+ "name": "xlsx-chart",
+ "version": "0.4.3",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.12.11",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
+ "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz",
+ "integrity": "sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg==",
+ "dev": true
+ },
+ "@babel/highlight": {
+ "version": "7.14.5",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz",
+ "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.14.5",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "dependencies": {
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ }
+ }
+ },
+ "@eslint/eslintrc": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz",
+ "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.4",
+ "debug": "^4.1.1",
+ "espree": "^7.3.0",
+ "globals": "^13.9.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "strip-json-comments": "^3.1.1"
+ }
+ },
+ "@humanwhocodes/config-array": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
+ "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==",
+ "dev": true,
+ "requires": {
+ "@humanwhocodes/object-schema": "^1.2.0",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.4"
+ }
+ },
+ "@humanwhocodes/object-schema": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz",
+ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==",
+ "dev": true
+ },
+ "acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+ },
+ "astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true
+ },
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+ },
+ "backbone": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.4.0.tgz",
+ "integrity": "sha512-RLmDrRXkVdouTg38jcgHhyQ/2zjg7a8E6sz2zxfz21Hh17xDJYUHBZimVIt5fUyS8vbfpeSmTL3gUjTEvUV3qQ==",
+ "requires": {
+ "underscore": ">=1.8.3"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+ "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
+ "cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "requires": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ }
+ },
+ "debug": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+ "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "enquirer": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^4.1.1"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "eslint": {
+ "version": "7.30.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.30.0.tgz",
+ "integrity": "sha512-VLqz80i3as3NdloY44BQSJpFw534L9Oh+6zJOUaViV4JPd+DaHwutqP7tcpkW3YiXbK6s05RZl7yl7cQn+lijg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "7.12.11",
+ "@eslint/eslintrc": "^0.4.2",
+ "@humanwhocodes/config-array": "^0.5.0",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "enquirer": "^2.3.5",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^2.1.0",
+ "eslint-visitor-keys": "^2.0.0",
+ "espree": "^7.3.1",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.1.2",
+ "globals": "^13.6.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.0.4",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "progress": "^2.0.0",
+ "regexpp": "^3.1.0",
+ "semver": "^7.2.1",
+ "strip-ansi": "^6.0.0",
+ "strip-json-comments": "^3.1.0",
+ "table": "^6.0.9",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ }
+ },
+ "eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz",
+ "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true
+ },
+ "espree": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz",
+ "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.4.0",
+ "acorn-jsx": "^5.3.1",
+ "eslint-visitor-keys": "^1.3.0"
+ },
+ "dependencies": {
+ "eslint-visitor-keys": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+ "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+ "dev": true
+ }
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz",
+ "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.1.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "extsprintf": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz",
+ "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8="
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^3.0.4"
+ }
+ },
+ "flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "requires": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ }
+ },
+ "flatted": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz",
+ "integrity": "sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globals": {
+ "version": "13.10.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz",
+ "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.20.2"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "jszip": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-2.6.1.tgz",
+ "integrity": "sha1-uI86ey5noqBIFSmCx6N1bZxIKPA=",
+ "requires": {
+ "pako": "~1.0.2"
+ }
+ },
+ "levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ }
+ },
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+ "dev": true
+ },
+ "lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "lodash.truncate": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optionator": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+ "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "dev": true,
+ "requires": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ }
+ },
+ "pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true
+ },
+ "prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "regexpp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
+ "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==",
+ "dev": true
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
+ },
+ "semver": {
+ "version": "7.3.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
+ "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^3.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
+ "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "table": {
+ "version": "6.7.1",
+ "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz",
+ "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==",
+ "dev": true,
+ "requires": {
+ "ajv": "^8.0.1",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.6.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.1.tgz",
+ "integrity": "sha512-42VLtQUOLefAvKFAQIxIZDaThq6om/PrfP0CYk3/vn+y4BMNkKnbli8ON2QCiHov4KkzOSJ/xSoBJdayiiYvVQ==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ }
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "^1.2.1"
+ }
+ },
+ "type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true
+ },
+ "underscore": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz",
+ "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g=="
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "v8-compile-cache": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+ "dev": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ }
+ },
+ "xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+}
diff --git a/package.json b/package.json
index 159cdbb..61f9d15 100644
--- a/package.json
+++ b/package.json
@@ -11,9 +11,13 @@
"verror": "^1.6.0",
"xml2js": "^0.4.13"
},
- "devDependencies": {},
+ "devDependencies": {
+ "eslint": "^7.30.0"
+ },
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "lint": "eslint ./",
+ "lint:fix": "eslint ./ --fix"
},
"repository": {
"type": "git",