Skip to content

Commit 8bbf1bd

Browse files
initial commit
1 parent ef7cdf0 commit 8bbf1bd

8 files changed

Lines changed: 378 additions & 0 deletions

File tree

demos/index.html

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head lang="en">
4+
<meta charset="UTF-8">
5+
<title>Demos</title>
6+
<link rel="stylesheet" href="../source/css/LightPivot.css"/>
7+
<script type="text/javascript" src="../source/js/LightPivot.js"></script>
8+
<script type="text/javascript" src="../source/js/DataSource.js"></script>
9+
<script type="text/javascript" src="../source/js/PivotView.js"></script>
10+
</head>
11+
<body>
12+
<div id="pivot" style="position: fixed; left: 20%; top: 20%; width: 60%; height: 60%;">
13+
14+
</div>
15+
<script type="text/javascript">
16+
var lp = new LightPivot({
17+
container: document.getElementById("pivot"),
18+
dataSource: {
19+
MDX2JSONSource: "http://localhost:57772/SAMPLES",
20+
//basicMDX: "SELECT NON EMPTY [Product].[P1].[Product Category].Members ON 0, NON EMPTY [Outlet].[H1].[Region].Members ON 1 FROM [HoleFoods]"
21+
//basicMDX: "SELECT NON EMPTY [Product].[P1].[Product Category].Members ON 0,NON EMPTY [DateOfSale].[Actual].[YearSold].&[2010].children ON 1 FROM [HoleFoods] %FILTER [DateOfSale].[Actual].[YearSold].&[2010] %FILTER [Measures].[%COUNT]"
22+
basicMDX: "SELECT NON EMPTY [Product].[P1].[Product Category].Members ON 0,NON EMPTY HEAD(NONEMPTYCROSSJOIN([Outlet].[H1].[Region].Members,[Outlet].[H1].[Country].Members),2000) ON 1 FROM [HoleFoods] %FILTER [Measures].[%COUNT]"
23+
//basicMDX: "SELECT NON EMPTY [Product].[P1].[Product Category].Members ON 0,NON EMPTY [DateOfSale].[Actual].[MonthSold].&[201003].children ON 1 FROM [HoleFoods] %FILTER [DateOfSale].[Actual].[YearSold].&[2010] %FILTER [DateOfSale].[Actual].[MonthSold].&[201003] %FILTER [Measures].[%COUNT]"
24+
// "SELECT NON EMPTY [Product].[P1].[Product Category].Members ON 0,NON EMPTY [DateOfSale].[Actual].[YearSold].&[2010].children ON 1 FROM [HoleFoods] %FILTER [DateOfSale].[Actual].[YearSold].&[2010] %FILTER [Measures].[%COUNT]"
25+
//basicMDX: "SELECT NON EMPTY [Product].[P1].[Product Category].Members ON 0,NON EMPTY HEAD(NONEMPTYCROSSJOIN([DateOfSale].[Actual].[YearSold].Members,[DateOfSale].[Actual].[MonthSold].Members),2000) ON 1 FROM [HoleFoods] %FILTER [Measures].[%COUNT]"
26+
//basicMDX: "SELECT NON EMPTY NONEMPTYCROSSJOIN([Product].[P1].[Product Category].Members,[Product].[P1].[Product Name].Members) ON 0,NON EMPTY HEAD(NONEMPTYCROSSJOIN([DateOfSale].[Actual].[YearSold].Members,[DateOfSale].[Actual].[MonthSold].Members),2000) ON 1 FROM [HoleFoods] %FILTER [Measures].[%COUNT]"
27+
//basicMDX: "SELECT NON EMPTY NONEMPTYCROSSJOIN(NONEMPTYCROSSJOIN([Product].[P1].[Product Category].Members,[Product].[P1].[Product Name].Members),{%LABEL([Measures].[%COUNT],\"Кол-во\",\"\"),[Measures].[Units Sold]}) ON 0,NON EMPTY HEAD(NONEMPTYCROSSJOIN([DateOfSale].[Actual].[YearSold].Members,[DateOfSale].[Actual].[MonthSold].Members),2000) ON 1 FROM [HoleFoods]"
28+
//basicMDX: "SELECT {[Measures].[%COUNT]} ON 0 FROM [HoleFoods] %FILTER [Measures].[%COUNT]"
29+
//basicMDX: "SELECT NON EMPTY [Product].[P1].[Product Category].Members ON 0,{[Measures].[%COUNT]} ON 1 FROM [HoleFoods] %FILTER [Measures].[%COUNT]"
30+
}
31+
})
32+
</script>
33+
</body>
34+
</html>

package.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "LightPivotTable",
3+
"version": "0.0.1",
4+
"description": "Light pivot table for MDX2JSON source for InterSystems Cache",
5+
"main": "test/testServer.js",
6+
"directories": {
7+
"test": "test"
8+
},
9+
"devDependencies": {
10+
"express": "^4.10.1"
11+
},
12+
"scripts": {
13+
"test": "node test/testServer.js"
14+
},
15+
"keywords": [
16+
"pivot",
17+
"table",
18+
"data",
19+
"collection",
20+
"visualization"
21+
],
22+
"author": "ZitRo"
23+
}

readme.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Light pivot table for MDX2JSON source
2+
====================
3+
4+
This is a lightweight and simple pivot table realization for [MDX2JSON](https://github.com/intersystems-ru/Cache-MDX2JSON) source.
5+
6+
The project is under development right now.
7+
8+
## Installation
9+
10+
There is no way to install this simply right now. First, you need to install and configure
11+
[MDX2JSON](https://github.com/intersystems-ru/Cache-MDX2JSON). Then take a look for
12+
<code>demos/index.html</code> file code. By replacing there URL's and MDX queries you can make this
13+
work for you.
14+
15+
Also you may have a cross-origin (domain) issues when testing. In future, this project will become
16+
a part of DeepSee.
17+
18+
## Preview
19+
20+
![Light pivot table](https://cloud.githubusercontent.com/assets/4989256/4876290/ce0918ea-62be-11e4-9583-fa9d78450716.png)

source/css/LightPivot.css

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.lpt {
2+
position: relative;
3+
width: 100%;
4+
height: 100%;
5+
box-shadow: 0 0 30px rgb(136, 160, 255);
6+
border-radius: 5px;
7+
}
8+
9+
.lpt > .tableContainer {
10+
overflow: auto;
11+
width: 100%;
12+
height: 100%;
13+
}
14+
15+
.lpt > .tableContainer > table {
16+
min-width: 100%;
17+
min-height: 100%;
18+
}
19+
20+
.lpt > .tableContainer td, .lpt > .tableContainer th {
21+
border-radius: 3px;
22+
padding: .5em;
23+
}
24+
25+
.lpt th {
26+
background: #C0CDFF;
27+
}
28+
29+
.lpt td {
30+
background: #E1E8FF;
31+
}
32+
33+
.lpt tr:hover td {
34+
background: #FFDEBF;
35+
}
36+
37+
.lpt tbody > tr > th {
38+
cursor: pointer;
39+
}

source/js/DataSource.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* Data source.
3+
*
4+
* Must implement methods.
5+
*
6+
* @constructor
7+
*/
8+
var DataSource = function (config) {
9+
10+
this.SOURCE_URL = config.MDX2JSONSource ||
11+
location.host + ":" + location.port + "/" + (location.pathname.split("/") || [])[1];
12+
13+
this.BASIC_MDX = config.basicMDX;
14+
15+
};
16+
17+
/**
18+
* @param {string} url
19+
* @param {object} data
20+
* @param {function} callback
21+
* @private
22+
*/
23+
DataSource.prototype._post = function (url, data, callback) {
24+
25+
var xhr = new XMLHttpRequest();
26+
xhr.open("POST", url);
27+
xhr.setRequestHeader("Content-Type", "application/json");
28+
xhr.onreadystatechange = function () {
29+
if (xhr.readyState === 4 && xhr.status === 200) {
30+
callback(JSON.parse(xhr.responseText));
31+
} else if (xhr.readyState === 4 && xhr.status !== 200) {
32+
callback({ error: xhr.responseText
33+
|| "Error while trying to retrieve data from server." });
34+
}
35+
};
36+
xhr.send(JSON.stringify(data));
37+
38+
};
39+
40+
/**
41+
* Converts data from MDX2JSON source to LightPivot representation.
42+
*
43+
* @param {object} data
44+
* @private
45+
*/
46+
DataSource.prototype._convert = function (data) {
47+
48+
if (typeof data !== "object" || data.error) return { error: data.error || true };
49+
50+
try {
51+
return {
52+
dimensions: [
53+
(data["Cols"][0]["tuples"][0]["children"] || [])[0],
54+
(data["Cols"][1]["tuples"][0]["children"] || [])[0]
55+
],
56+
dataArray: data["Data"],
57+
info: data["Info"]
58+
}
59+
} catch (e) {
60+
console.error("Error while parsing data:", e);
61+
return { error: data.error || true };
62+
}
63+
64+
};
65+
66+
/**
67+
* @param {function} callback
68+
*/
69+
DataSource.prototype.getCurrentData = function (callback) {
70+
71+
var _ = this;
72+
73+
this._post(this.SOURCE_URL + "/MDX", {
74+
MDX: this.BASIC_MDX
75+
}, function (data) { console.log(data); callback(_._convert(data)); });
76+
77+
};

source/js/LightPivot.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var LightPivot = function (configuration) {
2+
3+
var _ = this;
4+
5+
if (typeof configuration !== "object") configuration = {};
6+
7+
this.pivotView = new PivotView(configuration.container);
8+
this.dataSource = new DataSource(configuration.dataSource || {});
9+
10+
this.dataSource.getCurrentData(function (data) {
11+
console.log(data);
12+
_.pivotView.render(data);
13+
});
14+
15+
};

source/js/PivotView.js

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
var PivotView = function (container) {
2+
3+
if (!(container instanceof HTMLElement)) throw new Error("Please, provide HTMLElement " +
4+
"instance \"container\" into pivot table configuration.");
5+
6+
this.elements = {
7+
container: container,
8+
base: document.createElement("div"),
9+
tableContainer: document.createElement("div"),
10+
controlsContainer: document.createElement("div")
11+
};
12+
13+
this.init();
14+
15+
};
16+
17+
PivotView.prototype.init = function () {
18+
19+
var els = this.elements;
20+
21+
els.base.className = "lpt";
22+
els.tableContainer.textContent = "Loading...";
23+
els.tableContainer.className = "tableContainer";
24+
els.base.appendChild(els.tableContainer);
25+
els.container.appendChild(els.base);
26+
27+
};
28+
29+
PivotView.prototype.render = function (data) {
30+
31+
if (data.error) {
32+
this.elements.tableContainer.innerHTML = "<h1>Unable to render data</h1><p>"
33+
+ data.error + "</p>";
34+
return;
35+
}
36+
37+
this.elements.tableContainer.textContent = "";
38+
39+
var vTree, horDimArr = [], tableWidth, td, extraTh, tr, i,
40+
table = document.createElement("table"),
41+
thead = document.createElement("thead"),
42+
tbody = document.createElement("tbody");
43+
44+
var build = function (tree, barr, dim) {
45+
46+
var d = [], ch, n, o, nn = 0;
47+
48+
if (barr) {
49+
if (!dim) dim = 0;
50+
if (!horDimArr[dim]) horDimArr[dim] = [];
51+
}
52+
53+
for (var i in tree) {
54+
ch = tree[i].children ? build(tree[i].children, barr, barr ? dim + 1 : dim) : null;
55+
n = ch ? ch.numOfChildren : 1;
56+
nn += n;
57+
d.push(o = {
58+
caption: tree[i].caption,
59+
dimension: tree[i].dimension,
60+
path: tree[i].path,
61+
children: ch ? ch.children : ch,
62+
numOfChildren: n
63+
});
64+
if (barr) horDimArr[dim].push(o);
65+
}
66+
67+
return {
68+
children: d,
69+
numOfChildren: nn
70+
}
71+
72+
};
73+
74+
var prepend = function (cont, el) {
75+
cont.insertBefore(el, cont.firstChild);
76+
};
77+
78+
var lastTr,
79+
trs = [],
80+
maxLev = 1;
81+
82+
var verticalTree = function (children, sti, lev) {
83+
84+
if (!sti) sti = 0;
85+
if (!lev) lev = 1;
86+
if (lev > maxLev) maxLev = lev;
87+
88+
for (var i in children) {
89+
90+
var ch = children[i],
91+
td;
92+
93+
if (ch.children) {
94+
verticalTree(ch.children, sti, lev + 1);
95+
td = document.createElement("th");
96+
td.setAttribute("rowspan", ch.numOfChildren);
97+
td.textContent = ch.caption || "";
98+
prepend(trs[sti], td);
99+
sti += ch.numOfChildren;
100+
} else {
101+
trs.push(lastTr = document.createElement("tr"));
102+
tbody.appendChild(lastTr);
103+
td = document.createElement("th");
104+
td.textContent = ch.caption || "";
105+
prepend(lastTr, td);
106+
}
107+
108+
}
109+
110+
};
111+
112+
build(data.dimensions[0], 1);
113+
tableWidth = horDimArr[horDimArr.length - 1].length;
114+
vTree = build(data.dimensions[1]);
115+
116+
verticalTree(vTree.children);
117+
118+
for (var u = 0; u < horDimArr.length; u++) {
119+
120+
tr = document.createElement("tr");
121+
122+
if (u == 0) {
123+
var cornerTd = document.createElement("th");
124+
//cornerTd.innerHTML = "";
125+
cornerTd.setAttribute("rowspan", horDimArr.length.toString());
126+
cornerTd.setAttribute("colspan", maxLev.toString());
127+
tr.appendChild(cornerTd);
128+
}
129+
130+
for (i in horDimArr[u]) {
131+
132+
var ch = horDimArr[u][i];
133+
134+
td = document.createElement("th");
135+
136+
td.textContent = ch.caption || "";
137+
td.setAttribute("colspan", ch.numOfChildren);
138+
tr.appendChild(td);
139+
}
140+
141+
thead.appendChild(tr);
142+
143+
}
144+
145+
for (i = 0; i < data.dataArray.length; i++) {
146+
td = document.createElement("td");
147+
td.textContent = data.dataArray[i];
148+
tr = trs[Math.floor(i / tableWidth)];
149+
if (!tr) {
150+
trs[Math.floor(i / tableWidth)] = tr = document.createElement("tr");
151+
extraTh = document.createElement("th");
152+
tr.appendChild(extraTh);
153+
tbody.appendChild(tr);
154+
}
155+
tr.appendChild(td);
156+
}
157+
158+
table.appendChild(thead);
159+
table.appendChild(tbody);
160+
this.elements.tableContainer.appendChild(table);
161+
162+
};

test/testServer.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
var express = require("express"),
2+
app = express();
3+
4+
app.use(express.static(__dirname + "/.."));
5+
6+
app.listen(80);
7+
8+
console.info("Server ready on port 80.");

0 commit comments

Comments
 (0)