stackedColumns.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. // Licensed Materials - Property of IBM
  2. //
  3. // IBM Cognos Products: cogadmin
  4. //
  5. // (C) Copyright IBM Corp. 2005, 2014
  6. //
  7. // US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  8. //
  9. //
  10. dojo.provide("com.ibm.cognos.admin.chart.stackedColumns");
  11. dojo.require("dojox.charting.Chart2D");
  12. dojo.require("dojox.charting.widget.Legend");
  13. dojo.require("dojox.color");
  14. dojo.require("dojox.json.ref");
  15. dojo.require("dijit.Tooltip");
  16. dojo.require("dojox.charting.themes.BlueDusk");
  17. dojo.require("dojo.date");
  18. dojo.require("com.ibm.cognos.admin.chart.Base");
  19. dojo.require("com.ibm.cognos.admin.utils.Datetime");
  20. (function(){
  21. var DEFAULT_HEIGHT = 200,
  22. DEFAULT_WIDTH = 600,
  23. TITLE_HEIGHT = 20,
  24. SHAPE_OFFSET = 0.5,
  25. THE_LAST_HOUR = 23,
  26. STATUS_SUSPENDED = "suspended",
  27. COLUMN_SUSPENDED = 2,
  28. MINIMUM_LABEL = 15,
  29. NUMBER_OF_TICKS = 3,
  30. WEB_CONTENT = _F_Config.webContent;
  31. var dt = com.ibm.cognos.admin.utils.Datetime;
  32. dojo.declare("com.ibm.cognos.admin.chart.stackedColumns",com.ibm.cognos.admin.chart.Base,{
  33. defaultParams: {
  34. selectDateTime: '', //standard datetime string, the selected date time applied on detail table
  35. selectGranularity: 'day', //"day"/"hour", indicate the granularity of current view of detail table
  36. summaryDate: '', //standard datetime string, the current date applied on chart
  37. data: null //the summary data model
  38. /* Sample of data
  39. * [
  40. {
  41. status: 'scheduled',
  42. count: [
  43. {time: '0', number: 2},
  44. {time: '1', number: 3},
  45. {time: '2', number: 4}
  46. ]
  47. },{
  48. status: 'cancelled',
  49. count: [
  50. {time: '1', number: 1},
  51. {time: '2', number: 4}
  52. ]
  53. },{
  54. status: 'suspended',
  55. count: [
  56. {time: '', number: 2}
  57. ]
  58. }
  59. ]
  60. */
  61. },
  62. constructor : function (frag,args){
  63. this.selectDateTime = new dt(args.selectDateTime);
  64. this.selectGranularity = args.selectGranularity || this.defaultParams.selectGranularity;
  65. this.summaryDate = new dt(args.summaryDate);
  66. this.chart = new dojox.charting.Chart2D("chart" + frag.id,{fill: "transparent"});
  67. this.frag = frag;
  68. this.data = dojox.json.ref.fromJson(args.data);
  69. this.byHourTotal = [];
  70. this._initData();
  71. },
  72. //initial and enrich dataset by adding series and total attributes.
  73. _initData: function(){
  74. var self = this;
  75. dojo.forEach(this.data,function(item){
  76. item.subTotal = self._getSubTotal(item);
  77. item.series = self._getSeries(item);
  78. });
  79. this._initByHourTotal();
  80. },
  81. _getSubTotal: function(data){
  82. var ret = 0;
  83. dojo.forEach(data.count,function(item){
  84. ret += parseInt(item.number);
  85. });
  86. return ret;
  87. },
  88. _initByHourTotal: function() {
  89. var self = this;
  90. dojo.forEach(this.data,function(item){
  91. if (item.status == STATUS_SUSPENDED) return;
  92. for (var i=0; i<item.series.length; i++){
  93. if (self.byHourTotal[i] == undefined) self.byHourTotal[i] = 0;
  94. self.byHourTotal[i] +=parseInt(item.series[i]);
  95. }
  96. });
  97. },
  98. getTotalByHour: function(hour) {
  99. return this.byHourTotal[hour];
  100. },
  101. getMaxTotalByHour: function() {
  102. var max=0;
  103. for(var i=0; i<this.byHourTotal.length; i++) {
  104. if (this.byHourTotal[i] > max) max = this.byHourTotal[i];
  105. };
  106. return max;
  107. },
  108. _getSeries: function(data){
  109. if (data.status == STATUS_SUSPENDED) return;
  110. //initiate the data array with 0
  111. var ret = [];
  112. for (var i = 0; i < 24; i++) {
  113. ret[i] = 0;
  114. }
  115. //overwrite with the real number
  116. dojo.forEach(data.count,function(item){
  117. ret[parseInt(item.time)] = parseInt(item.number);
  118. });
  119. return ret;
  120. },
  121. getTotal: function(/*boolean*/ includeSuspended){
  122. var total = 0;
  123. dojo.forEach(this.data,function(item){
  124. if (!includeSuspended && item.status == STATUS_SUSPENDED) return;
  125. total += parseInt(item.subTotal);
  126. });
  127. return total;
  128. },
  129. /* Drill in and out against the charting
  130. * @param: granularity, enum hour/day/week/month
  131. */
  132. drill : function( /*String*/currentDate, /*int*/ value){
  133. var list = [];
  134. var currentDateTime = currentDate + "T" + ((value.length == 1) ? "0" : "") + value + ":00:00";
  135. list[0] = createTransient("upcoming_filter_granularity", "hour");
  136. list[1] = createTransient("upcoming_filter_date", currentDateTime);
  137. this.frag.transientUpdateList(list);
  138. },
  139. /* Jump to next or previous day or a certain date.
  140. * @param: value int (offset from current) or string (date only no time)
  141. */
  142. jumpTo : function(/*int/string*/value){
  143. var currentDateTime = "";
  144. if (typeof value == "number") {
  145. var newDate = dojo.date.add(this.summaryDate.getObject(), "day", value);
  146. currentDateTime = (new dt(newDate)).getDateString();
  147. }
  148. else {
  149. currentDateTime = (new dt(value)).getDateString();
  150. }
  151. this.frag.retrieve('summary_date=' + encodeURIComponent(currentDateTime));
  152. },
  153. _isToday: function (){
  154. return this.summaryDate.getDateString() == (new dt()).getDateString();
  155. },
  156. _isTheSameDay : function(){
  157. return this.selectDateTime.getDateString() == this.summaryDate.getDateString();
  158. },
  159. _getSelectedHour : function(){
  160. if (this.selectGranularity == "hour") {
  161. return this.selectDateTime.getObject().getHours();
  162. }
  163. else {
  164. return -1;
  165. }
  166. },
  167. _isTheSameHour : function(hour){
  168. if (this._isTheSameDay()) {
  169. return this._getSelectedHour() == hour;
  170. }
  171. return false;
  172. },
  173. getLabels : function(){
  174. var texts = [];
  175. for (var i = 0; i <= THE_LAST_HOUR; i++) {
  176. var o = {};
  177. o.text = i + '';
  178. o.value = i + 1;
  179. texts.push(o);
  180. }
  181. return texts;
  182. },
  183. addSeries : function(){
  184. for (var i=0; i<this.data.length; i++){
  185. if (this.data[i].status == STATUS_SUSPENDED) continue;
  186. this.chart.addSeries("<span class='cogadminLegendText'>" + ADM.CHT["IDS_ADMJS_CHT_" + this.data[i].status.toUpperCase()] + ":</span><span class='cogadminLegendValue'>" + this.data[i].subTotal + "</span>", this.data[i].series, {
  187. stroke: {color: "black",width: 1}
  188. });
  189. }
  190. },
  191. renderChart : function (){
  192. var self = this;
  193. var maxLabel = this.getMaxLabel(MINIMUM_LABEL,this.getMaxTotalByHour());
  194. this.chart.addPlot("default", {
  195. type: "StackedColumns",
  196. gap: 2,
  197. hAxis: "y",
  198. vAxis: "x"
  199. });
  200. this.chart.addPlot("grid", {
  201. type: "Grid",
  202. vMinorLines: false,
  203. vMajorLines: false,
  204. hMinorLines: false,
  205. hMajorLines: true
  206. });
  207. this.chart.addAxis("x", {
  208. vertical: true,
  209. fixLower: "major",
  210. fixUpper: "minor",
  211. majorTicks: true,
  212. minorTicks: false,
  213. microTicks: false,
  214. majorTickStep: maxLabel/NUMBER_OF_TICKS,
  215. min: 0,
  216. max: maxLabel
  217. }).addAxis("y", {
  218. fixLower: "none",
  219. fixUpper: "none",
  220. minorTicks: true,
  221. microTicks: false,
  222. htmlLabels: true,
  223. labels: this.getLabels()
  224. }).setTheme(dojox.charting.themes.BlueDusk);
  225. this.addSeries();
  226. var isRetrieving = false;
  227. if (!isRetrieving){
  228. var aroundRect;
  229. this.chart.connectToPlot("default", null, function(e){
  230. if (e.type == "onclick") {
  231. if (!self._isTheSameHour(e.index)) {
  232. isRetrieving = true;
  233. dojo.byId(self.frag.div).style.cursor = "wait";
  234. self.drill(self.summaryDate.getDateString(), e.index);
  235. return true;
  236. }
  237. return false;
  238. }
  239. if (e.type == "onmouseover"){
  240. if (!isRetrieving && !self._isTheSameHour(e.index)) {
  241. e.event.target.style.cursor = "pointer";
  242. }
  243. //This is a workaround dojo chart tooltip to make the selected column have a unified tooltip and highlighted effect.
  244. aroundRect = dojo.clone(e.shape.getShape());
  245. var lt = dojo.coords(this.chart.node, true);
  246. aroundRect.x += lt.x;
  247. aroundRect.y += lt.y - (((aroundRect.height/e.y)*self.byHourTotal[e.index]) * (1 - (e.y/self.byHourTotal[e.index])));
  248. aroundRect.x = Math.round(aroundRect.x);
  249. aroundRect.y = Math.round(aroundRect.y);
  250. aroundRect.width = Math.ceil(aroundRect.width);
  251. aroundRect.height = Math.ceil(aroundRect.height);
  252. dijit.showTooltip(self.byHourTotal[e.index],aroundRect,"above");
  253. return true;
  254. }
  255. if(e.type === "onplotreset" || e.type === "onmouseout"){
  256. dijit.hideTooltip(aroundRect);
  257. return true;
  258. }
  259. });
  260. }
  261. //render the chart
  262. this.chart.resize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
  263. //This is a workaround dojo chart highlight to make it unified for stacked columns.
  264. if (this._isTheSameDay() && this._getSelectedHour() > -1){
  265. var currentHour = new dt().getObject().getHours();
  266. for (var i=0; i<this.chart.series.length; i++){
  267. //if the data for current series equals to 0, just ignore the current loop
  268. if (this.chart.series[i].data[this._getSelectedHour()] == 0) continue;
  269. var emptyShape = 0;
  270. for (var j=0; j<=this._getSelectedHour(); j++){
  271. var isEmpty = true;
  272. for (var k=0; k<=i; k++){
  273. if (this.chart.series[k].data[j] != 0){
  274. isEmpty = false;
  275. break;
  276. }
  277. }
  278. if (isEmpty) emptyShape++;
  279. }
  280. var shape = this.chart.series[i].group.children[this._getSelectedHour() - emptyShape];
  281. this.highlightedColumn(shape);
  282. }
  283. }
  284. //adjust the fragment content height to meet the dojo chart
  285. dojo.byId(this.frag.id + "content").style.height = DEFAULT_HEIGHT + TITLE_HEIGHT;
  286. //generate the jump to date picker button
  287. dojo.connect(dojo.byId("next" + this.frag.id), "click", this, function(){
  288. this.jumpTo(1);
  289. });
  290. if (!this._isToday()){
  291. dojo.connect(dojo.byId("previous" + this.frag.id), "click", this, function(){
  292. this.jumpTo(-1);
  293. });
  294. }
  295. //render the legend
  296. var legendWidgetId = "legend" + this.frag.id;
  297. if (dijit.byId(legendWidgetId))
  298. dijit.byId(legendWidgetId).destroy();
  299. var legend = new dojox.charting.widget.Legend({
  300. chart: this.chart,
  301. horizontal: false,
  302. templateString: "<table role='presentation' dojoAttachPoint='legendNode' class='dojoxLegendNode'>" +
  303. "<thead>" +
  304. "<tr><td class='dojoxLegendText'>" +
  305. "<span class='cogadminLegendText' style='padding-left:28px;'>"+ADM.CHT.IDS_ADMJS_CHT_SUSPENDED+":</span>" +
  306. "<span class='cogadminLegendValue'>" + this.data[COLUMN_SUSPENDED].subTotal + "</span>" +
  307. "</td></tr>" +
  308. "<tr><td class='dojoxLegendText'>" +
  309. "<img src='" + WEB_CONTENT + "/ps/cogadmin/images/spacer.gif' alt='' width='120' height='1' style='background-color: rgb(156, 156, 156);'/>" +
  310. "</td></tr>" +
  311. "</thead>" +
  312. "<tbody dojoAttachPoint='legendBody'></tbody>" +
  313. "<tfoot>" +
  314. "<tr>" +
  315. "<td style='font-weight:bold' class='dojoxLegendText'><span class='cogadminLegendText' style='padding-left:28px;'>"+ADM.CHT.IDS_ADMJS_CHT_TOTAL+":</span>" +
  316. "<span class='cogadminLegendValue'>" + this.getTotal(false) + "</span>" +
  317. "</td></tr>" +
  318. "</tfoot></table>"
  319. }, legendWidgetId);
  320. //show up the title div
  321. dojo.byId("title" + this.frag.id).style.display = "";
  322. }
  323. })
  324. })();