= cubesService.cubesserver.info.json_record_limit\" class=\"alert alert-warning\" style=\"margin-bottom: 0px;\">\n" +
"
\n" +
" Limit of {{ cubesService.cubesserver.info.json_record_limit }} items has been hit. Dimension value list is
incomplete.
\n" +
diff --git a/dist/cubesviewer.css b/dist/cubesviewer.css
index c9b0fa7..ea6aaa9 100644
--- a/dist/cubesviewer.css
+++ b/dist/cubesviewer.css
@@ -6843,6 +6843,9 @@ button.cv-bootstrap .close {
.cv-grid-header-dimension {
color: #004400;
}
+.cv-grid-header-dimensionattribute {
+ color: #666666;
+}
.cv-grid-header-measure {
color: #444477;
}
@@ -6984,8 +6987,7 @@ div.nvtooltip {
width: 100%;
/* Full Width */
height: 3px;
- margin: 0px 0px 3px 0px;
- /*position: fixed;*/
+ margin: -4px 0px 1px 0px;
position: relative;
overflow-x: hidden;
}
diff --git a/dist/cubesviewer.js b/dist/cubesviewer.js
index b800248..a20d2ae 100644
--- a/dist/cubesviewer.js
+++ b/dist/cubesviewer.js
@@ -234,12 +234,19 @@ angular.module('bootstrapSubmenu', []).directive("submenu", ['$timeout', functio
};
cubes.Server.prototype.get_cube = function(name, callback, errCallback) {
- var self = this;
+
+ var self = this;
// Return the cube if already loaded
if((name in this._cubes) && callback){
- callback(this._cubes[name]);
- return null;
+ var jqxhr = $.Deferred();
+ jqxhr.error = function() { };
+ setTimeout(function() {
+ // TODO: What is the correct ordering of success/complete callbacks?
+ callback(self._cubes[name]);
+ jqxhr.resolve(); //.promise();
+ }, 0);
+ return jqxhr;
}
var options = {dataType : 'json', type : "GET"};
@@ -893,6 +900,8 @@ cubes.Dimension.prototype.isDateDimension = function() {
/**
* List date dimensions.
+ *
+ * @returns An array with the dimensions that are date dimensions (role: time).
*/
cubes.Cube.prototype.dateDimensions = function() {
var result = [];
@@ -916,9 +925,19 @@ cubes.Cube.prototype.cvdim_dim = function(dimensionString) {
return this.dimension(dimname);
};
+cubes.Cube.prototype.dimensionPartsFromCut = function(cut) {
+ var parts = this.dimensionParts(cut.dimension);
+ var depth = (cut.value.split(';')[0].match(/,/g) || []).length + 1;
+
+ var dimstring = parts.dimension.name + '@' + parts.hierarchy.name + ':' + parts.hierarchy.levels[depth - 1].name;
+ return this.dimensionParts(dimstring);
+};
+
cubes.Cube.prototype.dimensionParts = function(dimensionString) {
// Get a dimension info by name. Accepts dimension hierarchy and level in the input string.
+ if (!dimensionString) return null;
+
var dim = this.cvdim_dim(dimensionString);
var hie = dim.default_hierarchy();
@@ -928,9 +947,11 @@ cubes.Cube.prototype.dimensionParts = function(dimensionString) {
}
var lev = null;
+ var levelIndex = 0;
if (dimensionString.indexOf(":") > 0) {
var levelname = dimensionString.split(":")[1];
lev = dim.level(levelname);
+ for (levelIndex = 0; levelIndex < hie.levels.length && hie.levels[levelIndex] != lev; levelIndex++);
} else {
lev = dim.level(hie.levels[0]);
}
@@ -946,13 +967,19 @@ cubes.Cube.prototype.dimensionParts = function(dimensionString) {
return {
dimension: dim,
level: lev,
+ levelIndex: levelIndex,
depth: depth,
hierarchy: hie,
label: dim.label + ( hie.name != "default" ? (" - " + hie.label) : "" ) + ( hie.levels.length > 1 ? (" / " + lev.label) : "" ),
labelShort: (dim.label + ( hie.levels.length > 1 ? (" / " + lev.label) : "" )),
labelNoLevel: dim.label + ( hie.name != "default" ? (" - " + hie.label) : "" ),
+
fullDrilldownValue: dim.name + ( hie.name != "default" ? ("@" + hie.name) : "" ) + ":" + lev.name,
- fullCutValue: dim.name + ( hie.name != "default" ? ("@" + hie.name) : "" )
+ drilldownDimension: dim.name + '@' + hie.name + ':' + lev.name,
+ drilldownDimensionPlus: (hie.levels.length > 1 && levelIndex < hie.levels.length - 1) ? (dim.name + '@' + hie.name + ':' + hie.levels[levelIndex + 1].name) : null,
+ drilldownDimensionMinus: (hie.levels.length > 1 && levelIndex > 0) ? (dim.name + '@' + hie.name + ':' + hie.levels[levelIndex - 1].name) : null,
+
+ cutDimension: dim.name + ( hie.name != "default" ? "@" + hie.name : "" )
};
};
@@ -960,6 +987,8 @@ cubes.Cube.prototype.dimensionParts = function(dimensionString) {
/**
* Returns the aggregates for the given measure, by name.
* If passed null, returns aggregates with no measure.
+ *
+ * @returns The list of aggregates of a measure.
*/
cubes.Cube.prototype.measureAggregates = function(measureName) {
var aggregates = $.grep(this.aggregates, function(ia) { return measureName ? ia.measure == measureName : !ia.measure; } );
@@ -1197,7 +1226,7 @@ angular.module('cv.cubes').service("cubesService", ['$rootScope', '$log', 'cvOpt
/*
* Builds Query Cuts
*/
- this.buildQueryCuts = function(view) {
+ this.buildQueryCutsStrings = function(view) {
var cuts = [];
@@ -1207,20 +1236,29 @@ angular.module('cv.cubes').service("cubesService", ['$rootScope', '$log', 'cvOpt
var dimParts = view.cube.dimensionParts(e.dimension);
var cutDim = dimParts.dimension.name + ( dimParts.hierarchy.name != "default" ? "@" + dimParts.hierarchy.name : "" );
- cuts.push(cubes.cut_from_string(view.cube, invert + cutDim + ":" + e.value.replace("-", "\\-")));
+ cuts.push(invert + cutDim + ":" + e.value.replace("-", "\\-"));
});
// Date filters
$(view.params.datefilters).each(function(idx, e) {
var datefilterval = cubesService.datefilterValue(view, e);
if (datefilterval != null) {
- cuts.push(cubes.cut_from_string(view.cube, e.dimension + ":" + datefilterval));
+ cuts.push(e.dimension + ":" + datefilterval);
}
});
return cuts;
};
+ this.buildQueryCuts = function(view) {
+ var cuts = [];
+ var cutsStrings = cubesService.buildQueryCutsStrings(view);
+ $(cutsStrings).each(function(idx, e) {
+ cuts.push(cubes.cut_from_string(view.cube, e));
+ });
+ return cuts;
+ };
+
/*
* Composes a filter with appropriate syntax and time grain from a
* datefilter
@@ -1590,7 +1628,7 @@ angular.module('cv').run([ '$timeout', '$log', 'cvOptions', 'cubesService', 'cub
function CubesViewer() {
// CubesViewer version
- this.version = "2.0.2-devel";
+ this.version = "2.0.2";
/**
* State of a view that has not yet been fully initialized, and cannot be interacted with.
@@ -2011,8 +2049,8 @@ angular.module('cv.views.cube', []);
*
* FIXME: Some of this code shall be on a parent generic "view" directive.
*/
-angular.module('cv.views.cube').controller("CubesViewerViewsCubeController", ['$rootScope', '$log', '$window','$injector', '$scope', '$timeout', 'cvOptions', 'cubesService', 'viewsService', 'exportService', 'rowSorter',
- function ($rootScope, $log, $window, $injector, $scope, $timeout, cvOptions, cubesService, viewsService, exportService, rowSorter) {
+angular.module('cv.views.cube').controller("CubesViewerViewsCubeController", ['$rootScope', '$log', '$window','$injector', '$scope', '$timeout', 'cvOptions', 'cubesService', 'viewsService', 'exportService', 'rowSorter', 'dialogService',
+ function ($rootScope, $log, $window, $injector, $scope, $timeout, cvOptions, cubesService, viewsService, exportService, rowSorter, dialogService) {
// TODO: Functions shall be here?
$scope.viewController = {};
@@ -2091,7 +2129,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeController", ['$
};
$scope.requestErrorHandler = function() {
- $scope.view._requestFailed = true;
+ $scope.view.requestFailed = true;
};
@@ -2243,7 +2281,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeController", ['$
//return;
} else {*/
view.params.cuts = $.grep(view.params.cuts, function(e) {
- return view.cube.dimensionParts(e.dimension).fullCutValue == view.cube.dimensionParts(dimension).fullCutValue;
+ return view.cube.dimensionParts(e.dimension).cutDimension == view.cube.dimensionParts(dimension).cutDimension;
}, true);
view.params.cuts.push({
"dimension" : dimension,
@@ -2253,7 +2291,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeController", ['$
/*}*/
} else {
view.params.cuts = $.grep(view.params.cuts, function(e) {
- return view.cube.dimensionParts(e.dimension).fullCutValue == view.cube.dimensionParts(dimension).fullCutValue;
+ return view.cube.dimensionParts(e.dimension).cutDimension == view.cube.dimensionParts(dimension).cutDimension;
}, true);
}
} else {
@@ -2295,10 +2333,11 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeController", ['$
$scope.showDimensionFilter = function(dimension) {
- if ($scope.view.dimensionFilter && $scope.view.dimensionFilter == dimension) {
+ var parts = $scope.view.cube.dimensionParts(dimension);
+ if ($scope.view.dimensionFilter && $scope.view.dimensionFilter == parts.drilldownDimension) {
$scope.view.dimensionFilter = null;
} else {
- $scope.view.dimensionFilter = dimension;
+ $scope.view.dimensionFilter = parts.drilldownDimension;
}
};
@@ -2605,6 +2644,18 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeExploreControlle
};
};
+ $scope.exploreCut = function(dimension, value, invert) {
+ $scope.selectCut(dimension, value, invert);
+ if ($scope.view.params.drilldown.length == 1) {
+ // A single item has been selected, so automatically drill one more level
+ var dimparts = $scope.view.cube.dimensionParts($scope.view.params.drilldown[0]);
+ if (dimparts.levelIndex < dimparts.hierarchy.levels.length - 1) {
+ var drilldown = dimparts.dimension.name + ( dimparts.hierarchy.name != "default" ? ("@" + dimparts.hierarchy.name) : "" ) + ":" + dimparts.hierarchy.levels[dimparts.levelIndex + 1].name;
+ $scope.selectDrill(drilldown, true);
+ }
+ }
+ };
+
$scope.processData = function(data) {
var view = $scope.view;
@@ -2697,7 +2748,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeExploreControlle
enableHiding: false,
cutDimension: cutDimension,
width : $scope.defineColumnWidth("key" + i, 190),
- cellTemplate: '
',
+ cellTemplate: '
',
footerCellTemplate: '
' + footer + '
',
sort: $scope.defineColumnSort("key" + i),
sortingAlgorithm: $scope.sortDimensionParts(parts)
@@ -2908,15 +2959,17 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeFilterDimensionC
$scope.searchString = "";
$scope.selectedValues = null;
$scope.filterInverted = null;
+ $scope.filterShowAll = false;
+
+ $scope.currentDataId = null;
$scope.initialize = function() {
// Check if current filter is inverted
var view = $scope.view;
var parts = view.cube.dimensionParts($scope.view.dimensionFilter);
- var cutDimension = parts.dimension.name + ( parts.hierarchy.name != "default" ? "@" + parts.hierarchy.name : "" );
for (var i = 0; i < view.params.cuts.length ; i++) {
- if (view.params.cuts[i].dimension == cutDimension) {
+ if (view.params.cuts[i].dimension == parts.cutDimension) {
$scope.filterInverted = view.params.cuts[i].invert;
break;
}
@@ -2930,8 +2983,12 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeFilterDimensionC
$scope.$on("ViewRefresh", function(view) {
// FIXME: Update checkboxes, but do not reload.
- //$scope.loadDimensionValues();
+ $scope.loadDimensionValues();
});
+ $scope.$watch("filterShowAll", function(view) {
+ $scope.loadDimensionValues();
+ });
+
$scope.closeDimensionFilter = function() {
$scope.view.dimensionFilter = null;
@@ -2943,17 +3000,38 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeFilterDimensionC
$scope.loadDimensionValues = function() {
var params = {
- "hierarchy": $scope.parts.hierarchy.name,
- "depth": $scope.parts.depth
+ "hierarchy": $scope.parts.hierarchy.name,
+ "depth": $scope.parts.depth
};
//view.cubesviewer.views.blockViewLoading(view);
+ if (! $scope.filterShowAll) {
+
+ var parts = $scope.view.cube.dimensionParts($scope.view.dimensionFilter);
+ var buildQueryCutsStrings = cubesService.buildQueryCutsStrings($scope.view);
+
+ if (buildQueryCutsStrings.length > 0) {
+ // Remove current dimension
+ buildQueryCutsStrings = $.grep(buildQueryCutsStrings, function(cs) {
+ return ((cs.indexOf(parts.dimension.name) != 0) && (cs.indexOf("!" + parts.dimension.name) != 0));
+ });
+
+ params["cut"] = buildQueryCutsStrings.join(cubes.CUT_STRING_SEPARATOR_CHAR);
+ }
+
+ };
+
+ var path = "/cube/" + $scope.view.cube.name + "/members/" + $scope.parts.dimension.name;
+ var dataId = path + "?" + $.param(params);
+ if ($scope.currentDataId == dataId) { return; }
+ $scope.currentDataId = dataId;
+
var tdimension = $scope.view.dimensionFilter;
$scope.loadingDimensionValues = true;
var jqxhr = cubesService.cubesRequest(
// Doc says it's dimension, not members
- "/cube/" + $scope.view.cube.name + "/members/" + $scope.parts.dimension.name,
+ path,
params,
$scope._loadDimensionValuesCallback(tdimension));
jqxhr.always(function() {
@@ -3026,8 +3104,8 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeFilterDimensionC
var drilldownLevelLabels = [];
$(infos).each(function(idx, info) {
- drilldownLevelValues.push (info.key);
- drilldownLevelLabels.push (info.label);
+ drilldownLevelValues.push(info.key);
+ drilldownLevelLabels.push(info.label);
});
dimensionValues.push({
@@ -3321,6 +3399,32 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeFactsController"
sortingAlgorithm: $scope.sortDimensionLevel(level)
};
view.grid.columnDefs.push(col);
+
+ // Additional dimension attributes
+ $(level.attributes).each(function(idx, e) {
+ if (e.ref != level.key().ref && e.ref != level.label_attribute().ref) {
+ var col = {
+ name: e.name,
+ field: e.ref,
+ index : e.ref,
+ headerCellClass: "cv-grid-header-dimensionattribute",
+ //cellClass : "text-right",
+ //sorttype : "number",
+ cellTemplate: '
{{ row.entity[col.colDef.field] }}
',
+ //formatter: $scope.columnFormatFunction(ag),
+ //footerValue: $scope.columnFormatFunction(ag)(data.summary[ag.ref], null, col)
+ //formatoptions: {},
+ //cellattr: cubesviewer.views.cube.explore.columnTooltipAttr(ag.ref),
+ //footerCellTemplate = '
{{ col.colDef.footerValue }}
'
+ visible: ! view.params.columnHide[e.ref],
+ width : $scope.defineColumnWidth(e.ref, 85),
+ sort: $scope.defineColumnSort(e.ref),
+ //sortingAlgorithm: $scope.sortDimensionLevel(level)
+ };
+ view.grid.columnDefs.push(col);
+ }
+ });
+
}
}
@@ -3402,10 +3506,16 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeFactsController"
for (var i = 0; i < dimension.levels.length; i++) {
var level = dimension.levels[i];
- var levelData = level.readCell (e);
+ var levelData = level.readCell(e);
row[level.key().ref] = levelData.label;
+ $(level.attributes).each(function(aidx, ae) {
+ if (ae.ref != level.key().ref && ae.ref != level.label_attribute().ref) {
+ row[ae.ref] = levelData.info[ae.ref];
+ }
+ });
+
}
}
@@ -3420,7 +3530,7 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeFactsController"
}
// Set key
- row["id"] = counter++;
+ row["id"] = counter++;
if ("id" in e) row["id"] = e["id"];
row["key"] = row["id"];
@@ -3429,7 +3539,6 @@ angular.module('cv.views.cube').controller("CubesViewerViewsCubeFactsController"
rows.push(row);
});
-
};
$scope.$on("$destroy", function() {
@@ -5512,7 +5621,7 @@ angular.module('cv.views.cube').service("exportService", ['$rootScope', '$timeou
$(dataRows).each(function(idxr, r) {
values = [];
$(view.grid.columnDefs).each(function(idx, e) {
- if (r[e.field].title) {
+ if (r[e.field] && r[e.field].title) {
// Explore view uses objects as values, where "title" is the label
values.push('"' + r[e.field].title + '"');
} else {
@@ -5523,9 +5632,9 @@ angular.module('cv.views.cube').service("exportService", ['$rootScope', '$timeou
content = content + (values.join(",")) + "\n";
});
- var uri = "data:text/csv;charset=utf-8," + encodeURIComponent(content);
+
//window.open (url, "_blank");
- this.saveAs(uri, view.cube.name + "-summary.csv")
+ this.saveAs(content, "text/csv", view.cube.name + "-summary.csv")
};
/**
@@ -5533,14 +5642,22 @@ angular.module('cv.views.cube').service("exportService", ['$rootScope', '$timeou
*
* @memberof cv.views.cube.exportService
*/
- this.saveAs = function(uri, filename) {
- var link = document.createElement('a');
+ this.saveAs = function(content, mime, filename) {
+
+ // Method 1
+ //var uri = "data:" + mime + ";charset=utf-8," + encodeURIComponent(content);
+
+ // Method 2
+ var csvData = new Blob([content], { type: mime });
+ var uri = URL.createObjectURL(csvData);
+
+ var link = document.createElement('a');
if (typeof link.download === 'string') {
document.body.appendChild(link); // Firefox requires the link to be in the body
link.download = filename;
link.href = uri;
link.click();
- document.body.removeChild(link); // remove the link when done
+ document.body.removeChild(link); // remove the link when done
} else {
location.replace(uri);
}
@@ -5759,6 +5876,7 @@ angular.module('cv.studio').service("studioViewsService", ['$rootScope', '$ancho
* Adds a new clean view of type "cube" given a cube name.
*
* @memberof cv.studio.studioViewsService
+ * @returns The created view object.
*/
this.addViewCube = function(cubename) {
@@ -5772,7 +5890,13 @@ angular.module('cv.studio').service("studioViewsService", ['$rootScope', '$ancho
var view = viewsService.createView("cube", { "cubename": cubename, "name": name });
this.views.push(view);
- $timeout(function() { $anchorScroll('cvView' + view.id); }, 100);
+ $timeout(function() {
+ $('.cv-views-container').masonry('appended', $('.cv-views-container').find(".sv" + view.id).show());
+ //$('.cv-views-container').masonry('reloadItems');
+ //$('.cv-views-container').masonry('layout');
+ $timeout(function() { $anchorScroll("cvView" + view.id); }, 500);
+ }, 0);
+
return view;
};
@@ -5781,6 +5905,7 @@ angular.module('cv.studio').service("studioViewsService", ['$rootScope', '$ancho
* a JSON string.
*
* @memberof cv.studio.studioViewsService
+ * @returns The created view object.
*/
this.addViewObject = function(data) {
@@ -5796,7 +5921,13 @@ angular.module('cv.studio').service("studioViewsService", ['$rootScope', '$ancho
var view = viewsService.createView("cube", data);
this.views.push(view);
- $timeout(function() { $anchorScroll('cvView' + view.id); }, 250);
+
+ $timeout(function() {
+ $('.cv-views-container').masonry('appended', $('.cv-views-container').find(".sv" + view.id).show());
+ //$('.cv-views-container').masonry('reloadItems');
+ //$('.cv-views-container').masonry('layout');
+ $timeout(function() { $anchorScroll("cvView" + view.id); }, 500);
+ }, 0);
return view;
};
@@ -5809,11 +5940,13 @@ angular.module('cv.studio').service("studioViewsService", ['$rootScope', '$ancho
this.closeView = function(view) {
var viewIndex = this.views.indexOf(view);
if (viewIndex >= 0) {
+ $('.cv-views-container').masonry('remove', $('.cv-views-container').find(".sv" + view.id));
this.views.splice(viewIndex, 1);
+ //$('.cv-views-container').masonry('reloadItems');
+ $('.cv-views-container').masonry('layout');
}
- };
-
+ };
/**
* Collapses the panel of the given view.
@@ -5822,6 +5955,9 @@ angular.module('cv.studio').service("studioViewsService", ['$rootScope', '$ancho
*/
this.toggleCollapseView = function(view) {
view.collapsed = !view.collapsed;
+ $timeout(function() {
+ $('.cv-views-container').masonry('layout');
+ }, 100);
};
@@ -5839,13 +5975,24 @@ angular.module('cv.studio').controller("CubesViewerStudioViewController", ['$roo
$scope.cvOptions = cvOptions;
$scope.reststoreService = reststoreService;
+ $scope.$watch('__height', function() {
+ $('.cv-views-container').masonry('layout');
+ });
+
}]).directive("cvStudioView", function() {
return {
restrict: 'A',
templateUrl: 'studio/panel.html',
scope: {
view: "="
- }
+ },
+ link: function( scope, elem, attrs ) {
+
+ scope.$watch( function() {
+ scope.__height = elem.height();
+ } );
+
+ }
};
});
@@ -5863,6 +6010,8 @@ angular.module('cv.studio').controller("CubesViewerStudioController", ['$rootSco
$scope.studioViewsService.studioScope = $scope;
+ $scope.initialize = function() {
+ };
$scope.showSerializeAdd = function() {
@@ -5950,6 +6099,9 @@ angular.module('cv.studio').controller("CubesViewerStudioController", ['$rootSco
*/
$scope.toggleTwoColumn = function() {
cvOptions.studioTwoColumn = ! cvOptions.studioTwoColumn;
+ $timeout(function() {
+ $('.cv-views-container').masonry('layout');
+ }, 100);
};
/**
@@ -5957,8 +6109,13 @@ angular.module('cv.studio').controller("CubesViewerStudioController", ['$rootSco
*/
$scope.toggleHideControls = function() {
cvOptions.hideControls = ! cvOptions.hideControls;
+ $timeout(function() {
+ $('.cv-views-container').masonry('layout');
+ }, 100);
};
+ $scope.initialize();
+
}]);
@@ -6732,10 +6889,10 @@ angular.module('cv.cubes').service("gaService", ['$rootScope', '$http', '$cookie
"
\n" +
"\n" +
"
2 column\n" +
- " {{ cvOptions.studioTwoColumn ? \"ON\" : \"OFF\" }}\n" +
+ " {{ cvOptions.studioTwoColumn ? \"ON\" : \"OFF\" }}\n" +
" \n" +
"
Hide controls\n" +
- " {{ cvOptions.hideControls ? \"ON\" : \"OFF\" }}\n" +
+ " {{ cvOptions.hideControls ? \"ON\" : \"OFF\" }}\n" +
" \n" +
"\n" +
"
\n" +
@@ -6781,9 +6938,11 @@ angular.module('cv.cubes').service("gaService", ['$rootScope', '$http', '$cookie
"