Skip to content

Commit 460e676

Browse files
build system, sorting, integration ability and docs
1 parent d62231f commit 460e676

10 files changed

Lines changed: 453 additions & 45 deletions

File tree

demos/index.html renamed to example/index.html

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,34 @@
33
<head lang="en">
44
<meta charset="UTF-8">
55
<title>Demos</title>
6+
<!-- build:css -->
67
<link rel="stylesheet" href="../source/css/LightPivot.css"/>
7-
<script type="text/javascript" src="../source/js/LightPivot.js"></script>
8+
<!-- endbuild -->
9+
<!-- build:js -->
10+
<script type="text/javascript" src="../source/js/LightPivotTable.js"></script>
811
<script type="text/javascript" src="../source/js/DataSource.js"></script>
912
<script type="text/javascript" src="../source/js/PivotView.js"></script>
13+
<script type="text/javascript" src="../source/js/DataController.js"></script>
14+
<!-- endbuild -->
1015
</head>
1116
<body>
1217
<div id="pivot" style="position: fixed; left: 20%; top: 20%; width: 60%; height: 60%;">
1318

1419
</div>
1520
<script type="text/javascript">
16-
var lp = new LightPivot({
17-
container: document.getElementById("pivot"),
21+
var lp = new LightPivotTable({
22+
container: document.getElementById("pivot"), // HTMLElement which will contain table.
1823
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]"
24+
MDX2JSONSource: "http://localhost:57772/SAMPLES", // MDX2JSON server address
2225
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]"
2326
//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]"
2527
//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]"
2628
//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]"
2729
//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]"
2830
//basicMDX: "SELECT {[Measures].[%COUNT]} ON 0 FROM [HoleFoods] %FILTER [Measures].[%COUNT]"
2931
//basicMDX: "SELECT NON EMPTY [Product].[P1].[Product Category].Members ON 0,{[Measures].[%COUNT]} ON 1 FROM [HoleFoods] %FILTER [Measures].[%COUNT]"
32+
//basicMDX: "SELECT NON EMPTY [Product].[P1].[Product Category].Members ON 0, NON EMPTY [Outlet].[H1].[Region].Members ON 1 FROM [HoleFoods]"
33+
//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]"
3034
}
3135
})
3236
</script>

gulpfile.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
var gulp = require("gulp"),
2+
clean = require("gulp-clean"),
3+
concat = require("gulp-concat"),
4+
uglify = require("gulp-uglify"),
5+
wrap = require("gulp-wrap"),
6+
minifyCSS = require("gulp-minify-css"),
7+
htmlReplace = require("gulp-html-replace");
8+
9+
gulp.task("clean", function () {
10+
return gulp.src("build", {read: false})
11+
.pipe(clean());
12+
});
13+
14+
gulp.task("gatherScripts", ["clean"], function () {
15+
return gulp.src("source/js/*.js")
16+
.pipe(concat("lightPivotTable.js"))
17+
.pipe(wrap("LightPivotTable = (function(){<%= contents %> return LightPivotTable;}());"))
18+
.pipe(uglify())
19+
.pipe(gulp.dest("build/js/"));
20+
});
21+
22+
gulp.task("gatherCSS", ["clean"], function () {
23+
return gulp.src("source/css/*.css")
24+
.pipe(concat("lightPivotTable.css"))
25+
.pipe(minifyCSS())
26+
.pipe(gulp.dest("build/css/"));
27+
});
28+
29+
gulp.task("addExample", ["clean"], function () {
30+
gulp.src("example/index.html")
31+
.pipe(htmlReplace({
32+
"css": "../css/lightPivotTable.css",
33+
"js": "../js/lightPivotTable.js"
34+
}))
35+
.pipe(gulp.dest("build/example/"));
36+
});
37+
38+
gulp.task("copyLICENSE", ["clean"], function (){
39+
gulp.src("LICENSE")
40+
.pipe(gulp.dest("build/"));
41+
});
42+
43+
gulp.task("copyREADME", ["clean"], function (){
44+
gulp.src("readme.md")
45+
.pipe(gulp.dest("build/"));
46+
});
47+
48+
gulp.task("default", [
49+
"clean", "gatherScripts", "gatherCSS", "addExample", "copyLICENSE", "copyREADME"
50+
]);

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@
77
"test": "test"
88
},
99
"devDependencies": {
10-
"express": "^4.10.1"
10+
"express": "^4.10.1",
11+
"gulp": "^3.8.10",
12+
"gulp-clean": "^0.3.1",
13+
"gulp-concat": "^2.4.1",
14+
"gulp-html-replace": "^1.4.1",
15+
"gulp-minify-css": "^0.3.11",
16+
"gulp-uglify": "^1.0.1",
17+
"gulp-wrap": "^0.5.0"
1118
},
1219
"scripts": {
1320
"test": "node test/testServer.js"

readme.md

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,55 @@
11
Light pivot table for MDX2JSON source
22
====================
33

4-
This is a lightweight and simple pivot table realization for [MDX2JSON](https://github.com/intersystems-ru/Cache-MDX2JSON) source.
4+
This is a lightweight and simple pivot table realization for
5+
[MDX2JSON](https://github.com/intersystems-ru/Cache-MDX2JSON) source.
56

6-
The project is under development right now.
7+
## Features
8+
<ul>
9+
<li>View any MDX;</li>
10+
<li>Sort data in table;</li>
11+
<li>Easy to configure and use;</li>
12+
<li>Lightweight and speedy realization comparing to common DeepSee pivot table.</li>
13+
</ul>
714

815
## Installation
916

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.
17+
<ol>
18+
<li>Install and configure [MDX2JSON](https://github.com/intersystems-ru/Cache-MDX2JSON) on
19+
your Caché instance;</li>
20+
<li>Build the project by running <code>gulp</code> command (see "Build" section below);</li>
21+
<li>Edit a bit code in <code>build/example/index.html</code> (set MDX2JSONSource and basicMDX
22+
properties according to your needs);</li>
23+
<li>Use it!</li>
24+
</ol>
1425

15-
Also you may have a cross-origin (domain) issues when testing. In future, this project will become
16-
a part of DeepSee.
26+
## Integration
27+
28+
Build the project, and then include <code>build/css/lightPivotTable.css</code> and
29+
<code>build/js/lightPivotTable.js</code> files into your project. Usage is shown in
30+
<code>build/example/index.html</code> example.
31+
32+
## Build
33+
34+
You need [NodeJS](http://nodejs.org/) to perform any build tasks.
35+
36+
To build project and see working example, gulp and it's plugins must be installed. Simple run
37+
<code>npm install -g gulp</code> and <code>npm install</code> commands to perform all required
38+
installations.
39+
40+
By running <code>gulp</code> command later, <code>build</code> directory will be created with all
41+
required files there.
1742

1843
## Debug
1944

20-
While [node](http://nodejs.org/) installed, run <code>npm install</code> in the project root to
45+
Run <code>npm install</code> in the project root to
2146
install all required modules, and then execute <code>node test/testServer</code> to run local
22-
server. By doing tests this way you have to to uncomment string
23-
<code>d %response.SetHeader("Access-Control-Allow-Origin","*")</code> in MDX2JSON.REST class in
24-
MDX2JSON package and additionally comment <code>If tMethodMatched=0 Set tSC=..Http405() Quit</code> line
25-
there for cross-domain security purposes. Then give the right URL and MDX as in the example in
26-
demos/index.html file.
47+
server. Then give the right URL and MDX as in the example in example/index.html file.
48+
49+
Also anytime you can build project and then check <code>build/example/index.html</code> page.
2750

2851
## Preview
2952

53+
Run <code>gulp</code> command and then check <code>build/example/index.html</code>.
54+
3055
![Light pivot table](https://cloud.githubusercontent.com/assets/4989256/4876290/ce0918ea-62be-11e4-9583-fa9d78450716.png)

source/css/LightPivot.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@
2626
background: #C0CDFF;
2727
}
2828

29+
.lpt thead tr:last-child th {
30+
cursor: pointer;
31+
-webkit-transition: all .3s ease;
32+
}
33+
34+
.lpt thead tr:last-child th:first-child ~ th:hover {
35+
background: #e2c8ff;
36+
}
37+
38+
.orderedDec {
39+
background: red;
40+
}
41+
42+
.orderedAsc {
43+
background: orange;
44+
}
45+
2946
.lpt td {
3047
background: #E1E8FF;
3148
}

source/js/DataController.js

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
var DataController = function (dataSource, dataChangeTrigger) {
2+
3+
if (dataChangeTrigger && typeof dataChangeTrigger !== "function") {
4+
throw new Error("dataChangeTrigger parameter must be a function");
5+
}
6+
7+
this.data = null;
8+
this.SORT_STATE = {
9+
column: null,
10+
order: -1
11+
};
12+
this.dataChangeTrigger = dataChangeTrigger;
13+
14+
};
15+
16+
DataController.prototype.getData = function () {
17+
18+
return this.data;
19+
20+
};
21+
22+
DataController.prototype.setData = function (data) {
23+
24+
this.data = data;
25+
this.resetRawData();
26+
27+
this._trigger();
28+
return this.data;
29+
30+
};
31+
32+
DataController.prototype.resetRawData = function () {
33+
34+
if (!this.data) {
35+
console.error("Unable to create raw data for given data set.");
36+
return;
37+
}
38+
39+
var rd0 = [], rd1 = [], num = 2, rawData = [];
40+
41+
var transpose = function (a) {
42+
return Object.keys(a[0]).map(function (c) {
43+
return a.map(function (r) {
44+
return r[c];
45+
});
46+
});
47+
};
48+
49+
var dim0raw = function (a, c, arr) {
50+
51+
dim1raw(rd0, c, arr);
52+
rd0 = transpose(rd0);
53+
54+
};
55+
56+
var dim1raw = function (a, c, arr) {
57+
58+
if (!arr) {
59+
arr = [];
60+
}
61+
62+
var cnum;
63+
64+
for (var i in c) {
65+
cnum = num;
66+
if (c[i].children) {
67+
num++;
68+
dim1raw(a, c[i].children, arr.concat({
69+
group: cnum,
70+
source: c[i],
71+
isCaption: true,
72+
value: c[i].caption || ""
73+
}));
74+
} else {
75+
a.push(arr.concat({
76+
group: num,
77+
source: c[i],
78+
isCaption: true,
79+
value: c[i].caption || ""
80+
}));
81+
num++;
82+
}
83+
}
84+
85+
};
86+
87+
dim0raw(rd0, this.data.dimensions[0]);
88+
dim1raw(rd1, this.data.dimensions[1]);
89+
90+
var xw = rd0[0].length,
91+
yh = rd1.length,
92+
xh = rd0.length,
93+
yw = rd1[0].length;
94+
95+
for (var y = 0; y < xh + yh; y++) {
96+
if (!rawData[y]) rawData[y] = [];
97+
for (var x = 0; x < yw + xw; x++) {
98+
if (x < yw) {
99+
if (y < xh) {
100+
rawData[y][x] = {
101+
group: 1,
102+
isCaption: true,
103+
value: (this.data["info"] || {})["cubeName"] || ""
104+
};
105+
} else {
106+
rawData[y][x] = rd1[y-xh][x];
107+
}
108+
} else {
109+
if (y < xh) {
110+
rawData[y][x] = rd0[y][x-yw];
111+
} else {
112+
rawData[y][x] = {
113+
value: this.data.dataArray[(xw)*(y - xh) + x - yw] || ""
114+
};
115+
}
116+
}
117+
}
118+
}
119+
120+
this.data.info.topHeaderRowsNumber = xh;
121+
this.data.info.leftHeaderColumnsNumber = yw;
122+
this.data.rawData = this.data._rawDataOrigin = rawData;
123+
124+
return this.data.rawData;
125+
126+
};
127+
128+
/**
129+
* Trigger the dataChangeTrigger.
130+
*
131+
* @private
132+
*/
133+
DataController.prototype._trigger = function () {
134+
135+
if (this.dataChangeTrigger) this.dataChangeTrigger();
136+
137+
};
138+
139+
/**
140+
* Sort raw data by column.
141+
*
142+
* @param columnIndex
143+
*/
144+
DataController.prototype.sortByColumn = function (columnIndex) {
145+
146+
if (this.SORT_STATE.column !== columnIndex) {
147+
order = this.SORT_STATE.order = 0;
148+
}
149+
150+
var newRawData = this.data._rawDataOrigin.slice(this.data.info.topHeaderRowsNumber),
151+
xIndex = this.data.info.leftHeaderColumnsNumber + columnIndex,
152+
order = this.SORT_STATE.order === -1 ? 1 : this.SORT_STATE.order === 1 ? 0 : -1;
153+
154+
this.SORT_STATE.order = order;
155+
this.SORT_STATE.column = columnIndex;
156+
157+
if (order === 0) {
158+
this.data.rawData = this.data._rawDataOrigin;
159+
this._trigger();
160+
return;
161+
}
162+
163+
order = -order;
164+
165+
newRawData.sort(function (a, b) {
166+
return order*b[xIndex].value - order*a[xIndex].value;
167+
});
168+
169+
this.data.rawData = [this.data._rawDataOrigin[0]].concat(newRawData);
170+
171+
this._trigger();
172+
173+
};

0 commit comments

Comments
 (0)