├── LICENSE ├── README.md ├── css ├── jquery.cy-pivot.css └── jquery.cy-pivot.min.css ├── cynteka-pivot-table.jquery.json └── js ├── jquery.cy-pivot.js └── jquery.cy-pivot.min.js /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sergey Grigorchuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Cynteka Pivot Table jQuery plugin 2 | ================================= 3 | 4 | This is a jQuery plugin that can be used on a website for data analysis, smart navigation, data input... 5 | 6 | To get started, checkout examples and documentation at http://ukman.github.com 7 | 8 | Bug tracker 9 | ----------- 10 | 11 | Have a bug? Please create an issue here on GitHub! 12 | https://github.com/ukman/pivot/issues 13 | 14 | Copyright and license 15 | --------------------- 16 | 17 | Copyright 2012-2014 Sergey Grigorchuk sergey.grigorchuk@gmail.com -------------------------------------------------------------------------------- /css/jquery.cy-pivot.css: -------------------------------------------------------------------------------- 1 | /* Cynteka Pivot JQuery Plugin. 2 | * Copyright (C) Cynteka http://www.cynteka.com, 2011-2014 3 | * Author Sergey Grigorchuk sng@cynteka.com, sergey.grigorchuk@gmail.com 4 | */ 5 | 6 | .div12 { 7 | white-space: nowrap; 8 | } 9 | .div12 .dim-cell{ 10 | display: inline-block; 11 | vertical-align:top; 12 | } 13 | 14 | .div12 .dim-label { 15 | min-height:30px; 16 | white-space: normal; 17 | } 18 | 19 | .div21 .dim-cell { 20 | padding-left : 20px; 21 | } 22 | 23 | .div21 .dim-label { 24 | white-space: nowrap; 25 | padding-left:10px; 26 | padding-right:10px; 27 | overflow-x:hidden; 28 | text-overflow: ellipsis; 29 | } 30 | 31 | .div21 .level-0 { 32 | padding-left:0px; 33 | } 34 | 35 | .dim-label { 36 | padding1:10px; 37 | border:0px solid blue; 38 | background:darkcyan; 39 | border-radius: 10px; 40 | margin1:6px; 41 | margin-bottom:2px; 42 | margin-right:2px; 43 | text-align: center; 44 | color:white; 45 | font-weight: bold; 46 | /* display:table-cell; 47 | vertical-align: middle;*/ 48 | } 49 | 50 | .dim-label span { 51 | vertical-align: middle; 52 | } 53 | 54 | .div21 .dim-label { 55 | text-align: left; 56 | } 57 | 58 | .expandable-dim-label { 59 | cursor:pointer; 60 | 61 | } 62 | 63 | .dim-total-cell .dim-label { 64 | background:DarkSlateGray; 65 | } 66 | 67 | .cell11, .cell12, .cell21, .cell22 { 68 | vertical-align: top; 69 | } 70 | 71 | .div22 { 72 | width:200px; 73 | height:500px; 74 | overflow: scroll; 75 | } 76 | 77 | .div12, 78 | .div21 { 79 | overflow: hidden; 80 | } 81 | .div21 { 82 | max-width:400px; 83 | } 84 | 85 | table.pivot-data-table { 86 | border:0px solid #AAAAAA; 87 | border-collapse:collapse; 88 | } 89 | 90 | table.pivot-data-table td{ 91 | padding:0px; /* cellpadding="0" */ 92 | border:1px solid white; 93 | } 94 | 95 | table.pivot-data-table .dim-label { 96 | margin:3px; 97 | } 98 | 99 | .div-data-cell { 100 | padding1:10px; 101 | width:100px; 102 | border:1px solid #BBB; 103 | border-radius: 10px; 104 | margin:1px; 105 | min-height:30px; 106 | } 107 | 108 | .data-cell { 109 | vertical-align:top; 110 | } 111 | 112 | .collapsed-dim-cell .dim-total-cell { 113 | /* display: none; */ 114 | } 115 | 116 | 117 | .div11 { 118 | background: SaddleBrown; 119 | cursor:pointer; 120 | border-radius:10px; 121 | padding:5px; 122 | color:white; 123 | font-weight:bold; 124 | text-align: center; 125 | } 126 | 127 | .config-window-activated { 128 | background: Chocolate; 129 | } 130 | 131 | .dimension-list li { 132 | cursor:move; 133 | } 134 | 135 | .dimension-list li.dimension-list-title { 136 | cursor:default; 137 | } 138 | 139 | .config-dialog ul li { 140 | display:block; 141 | } 142 | 143 | .config-dialog ul { 144 | padding:0px; 145 | } 146 | 147 | .dimension-list-title, 148 | .first-dimension-list-title { 149 | margin-top:10px; 150 | border-bottom: 1px solid #333; 151 | /*background-color:#DDD;*/ 152 | font-weight:bold; 153 | } 154 | 155 | .dayClass { 156 | background-color : #FEE; 157 | } 158 | 159 | .workDayClass { 160 | background-color : #EEF; 161 | } 162 | 163 | .work-table-cell { 164 | padding:5px; 165 | border-radius: 9px; 166 | } 167 | 168 | i.dim-cell-icon { 169 | width:14px; 170 | height:14px; 171 | display:inline-block; 172 | margin-top:1px; 173 | line-height: 14px; 174 | vertical-align: text-top; 175 | background-repeat:no-repeat; 176 | margin-right: 3px; 177 | } 178 | 179 | .collapsed-dim-label i.dim-cell-icon { 180 | background-image1: url("../img/glyphicons-halflings-white.png"); 181 | background-position: -408px -96px; 182 | } 183 | 184 | .dark-icons i.dim-cell-icon { 185 | background-image: url("../img/glyphicons-halflings.png"); 186 | } 187 | 188 | i.dim-cell-icon { 189 | background-image: url("../img/glyphicons-halflings-white.png"); 190 | background-position: 100px 100px; 191 | } 192 | 193 | .expanded-dim-label i.dim-cell-icon { 194 | background-image1: url("../img/glyphicons-halflings-white.png"); 195 | background-position: -433px -96px; 196 | } 197 | 198 | .dark-icons .collapsed-dim-label i.dim-cell-icon { 199 | background-image: url("../img/glyphicons-halflings.png"); 200 | } 201 | -------------------------------------------------------------------------------- /css/jquery.cy-pivot.min.css: -------------------------------------------------------------------------------- 1 | .div12{white-space:nowrap}.div12 .dim-cell{display:inline-block;vertical-align:top}.div12 .dim-label{min-height:30px;white-space:normal}.div21 .dim-cell{padding-left:20px}.div21 .dim-label{white-space:nowrap;padding-left:10px;padding-right:10px;overflow-x:hidden;text-overflow:ellipsis}.div21 .level-0{padding-left:0}.dim-label{padding1:10px;border:0 solid blue;background:darkcyan;border-radius:10px;margin1:6px;margin-bottom:2px;margin-right:2px;text-align:center;color:white;font-weight:bold}.dim-label span{vertical-align:middle}.div21 .dim-label{text-align:left}.expandable-dim-label{cursor:pointer}.dim-total-cell .dim-label{background:DarkSlateGray}.cell11,.cell12,.cell21,.cell22{vertical-align:top}.div22{width:200px;height:500px;overflow:scroll}.div12,.div21{overflow:hidden}.div21{max-width:400px}table.pivot-data-table{border:0 solid #aaa;border-collapse:collapse}table.pivot-data-table td{padding:0;border:1px solid white}table.pivot-data-table .dim-label{margin:3px}.div-data-cell{padding1:10px;width:100px;border:1px solid #BBB;border-radius:10px;margin:1px;min-height:30px}.data-cell{vertical-align:top}.div11{background:SaddleBrown;cursor:pointer;border-radius:10px;padding:5px;color:white;font-weight:bold;text-align:center}.config-window-activated{background:Chocolate}.dimension-list li{cursor:move}.dimension-list li.dimension-list-title{cursor:default}.config-dialog ul li{display:block}.config-dialog ul{padding:0}.dimension-list-title,.first-dimension-list-title{margin-top:10px;border-bottom:1px solid #333;font-weight:bold}.dayClass{background-color:#FEE}.workDayClass{background-color:#EEF}.work-table-cell{padding:5px;border-radius:9px}i.dim-cell-icon{width:14px;height:14px;display:inline-block;margin-top:1px;line-height:14px;vertical-align:text-top;background-repeat:no-repeat;margin-right:3px}.collapsed-dim-label i.dim-cell-icon{background-image1:url("../img/glyphicons-halflings-white.png");background-position:-408px -96px}.dark-icons i.dim-cell-icon{background-image:url("../img/glyphicons-halflings.png")}i.dim-cell-icon{background-image:url("../img/glyphicons-halflings-white.png");background-position:100px 100px}.expanded-dim-label i.dim-cell-icon{background-image1:url("../img/glyphicons-halflings-white.png");background-position:-433px -96px}.dark-icons .collapsed-dim-label i.dim-cell-icon{background-image:url("../img/glyphicons-halflings.png")} -------------------------------------------------------------------------------- /cynteka-pivot-table.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "cynteka-pivot-table", 3 | "version" : "0.9.2", 4 | "title" : "Cynteka Pivot Table", 5 | "author" : { 6 | "name" : "Sergey Grigorchuk", 7 | "email" : "sergey.grigorchuk@gmail.com", 8 | "url" : "http://www.linkedin.com/in/sgrigorchuk" 9 | }, 10 | "licenses" : [ 11 | { 12 | "type" : "MIT License", 13 | "url" : "http://opensource.org/licenses/MIT" 14 | } 15 | ], 16 | "dependencies": { 17 | "jquery": ">=1.7.1", 18 | "jquery-ui": ">=1.9.1", 19 | "jquery-cookie": ">=1.3.1" 20 | }, 21 | 22 | "description" : "This is a jQuery plugin that can be used on a website for olap data analysis, smart navigation, data input...", 23 | "keywords" : ["pivot", "pivottable", "table", "analysis", "olap", "ui", "jquery", "grid", "hierarchy", "widget"], 24 | "homepage" : "http://ukman.github.io/", 25 | "docs" : "http://ukman.github.io#documentation", 26 | "demo" : "http://ukman.github.io#demo", 27 | "download" : "https://github.com/ukman/pivot/archive/v0.9.2.zip", 28 | "bugs" : "https://github.com/ukman/pivot/issues", 29 | "maintainers" : [ 30 | { 31 | "name" : "Sergey Grigorchuk", 32 | "email" : "sergey.grigorchuk@gmail.com", 33 | "url" : "http://www.linkedin.com/in/sgrigorchuk" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /js/jquery.cy-pivot.js: -------------------------------------------------------------------------------- 1 | /* Cynteka Pivot JQuery Plugin. 2 | * Copyright (C) Cynteka http://www.cynteka.com, 2011-2014 3 | * Author Sergey Grigorchuk sng@cynteka.com, sergey.grigorchuk@gmail.com 4 | */ 5 | (function( $ ){ 6 | var methods = { 7 | init : function(options) { 8 | var opts = $.extend({}, $.fn.cypivot.defaults, options); 9 | 10 | var outThis = this; 11 | if(opts.resizable) { 12 | $(window).resize(function(){ 13 | if(opts.resizableWidth) { 14 | doResizeWidth(outThis); 15 | } 16 | if(opts.resizableHeight) { 17 | doResizeHeight(outThis); 18 | } 19 | }); 20 | } 21 | var res = this.each(function(){ 22 | var $this = $(this); 23 | $this.data('cypivot', { 24 | target : $this, 25 | options : opts 26 | }); 27 | 28 | if(opts.cookiePrefix && opts.storeDimConfig) { 29 | var sHorizontalDims = $.cookie(opts.cookiePrefix + 'horizontalDims'); 30 | var sVerticalDims = $.cookie(opts.cookiePrefix + 'verticalDims'); 31 | var sFilterDims = $.cookie(opts.cookiePrefix + 'filterDims'); 32 | if(sHorizontalDims && hasAllDims(opts.dimensions, sHorizontalDims) && hasAllDims(opts.dimensions, sVerticalDims)) 33 | opts.horizontalDimensions = sHorizontalDims.split(';'); 34 | if(sVerticalDims && hasAllDims(opts.dimensions, sHorizontalDims) && hasAllDims(opts.dimensions, sVerticalDims)) 35 | opts.verticalDimensions = sVerticalDims.split(';'); 36 | if(sFilterDims && hasAllDims(opts.dimensions, sFilterDims) && hasAllDims(opts.dimensions, sFilterDims)) 37 | opts.filterDimensions = sFilterDims.split(';'); 38 | } 39 | 40 | var configurationHtml = '
' + opts.configLabel + '
'; 41 | if(!opts.configuration) { 42 | configurationHtml = ''; 43 | } 44 | $this.html('' + 45 | '' + 46 | ' ' + 47 | ' ' + 50 | ' ' + 54 | ' ' + 55 | ' ' + 56 | ' ' + 60 | '
' + 48 | configurationHtml + 49 | ' ' + 51 | '
' + 52 | '
' + 53 | '
' + 57 | '
' + 58 | '
' + 59 | '
' + 61 | '
' + 62 | '
' + 63 | ' ' + 64 | ' ' + 65 | ' ' + 66 | ' ' + 67 | ' ' + 68 | '
' + 69 | '
' 70 | ); 71 | 72 | 73 | // Size/scroll synchronization init 74 | $this.find('.' + opts.div22Class).on('scroll', function(){ 75 | syncScrollAndSize($this, opts); 76 | }); 77 | $this.find('.' + opts.cell22Class).resize(function(){ 78 | syncScrollAndSize($this, opts); 79 | }); 80 | $this.find('.' + opts.div11Class).on('click', function(){ 81 | toggleConfig($this, opts); 82 | }); 83 | createConfigDialog($this, opts); 84 | $this.find('#' + opts.pivotConfigDialogId).dialog({ 85 | modal : false, 86 | title : 'Pivot Table Configuration', 87 | position:["right", "50px"], 88 | beforeClose : function() { 89 | var $div11 = $this.find('.' + opts.div11Class); 90 | $div11.removeClass(opts.configWindowActivatedClass); 91 | }, 92 | }); 93 | jQuery('#' + opts.pivotConfigDialogId).dialog('close'); 94 | 95 | syncScrollAndSize($this, opts); 96 | incrementalDraw($this, [], [opts.verticalDimensions[0], opts.horizontalDimensions[0]], 0, 0, opts); 97 | initExpandListeners($this, opts); 98 | /* 99 | redraw($this, opts); 100 | */ 101 | }); 102 | if(opts.resizable) { 103 | if(opts.resizableWidth) { 104 | doResizeWidth(outThis); 105 | } 106 | if(opts.resizableHeight) { 107 | doResizeHeight(outThis); 108 | } 109 | } 110 | return res; 111 | }, 112 | 113 | options : function(newData, reasonCell) { 114 | var $this = $(this); 115 | var data = $this.data('cypivot'); 116 | return data.options; 117 | }, 118 | 119 | reload : function(newData, reasonCell) { 120 | return this.each(function(){ 121 | var $this = $(this), 122 | data = $this.data('cypivot'); 123 | if(newData) 124 | data.options.data=newData; 125 | 126 | if(reasonCell) { 127 | var $dataTable = $this.find('.' + data.options.div22Class + ' .' + data.options.pivotDataTableClass); 128 | var dataTable = $dataTable[0]; 129 | var rowItems; 130 | for(var rowIdx = 0; rowIdx < dataTable.rows.length; rowIdx++) { 131 | var row = dataTable.rows[rowIdx]; 132 | for(var colIdx = 0; colIdx < row.cells.length; colIdx++) { 133 | var cell = row.cells[colIdx]; 134 | if(isSubContext(cell.colContext, reasonCell.colContext) && 135 | isSubContext(cell.rowContext, reasonCell.rowContext)) { 136 | cell.isCalculated = false; 137 | } 138 | } 139 | } 140 | fillDataValues($this, data.options); 141 | } else { 142 | fillDataValues($this, data.options, true); 143 | } 144 | }); 145 | }, 146 | 147 | reconfig : function(newOpts) { 148 | var opts = $.extend({}, $.fn.cypivot.defaults, newOpts); 149 | var $this = $(this); 150 | redraw($this, opts); 151 | } 152 | 153 | } 154 | 155 | 156 | $.fn.cypivot = function(method) { 157 | // Method calling logic 158 | if ( methods[method] ) { 159 | return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )); 160 | } else if ( typeof method === 'object' || ! method ) { 161 | return methods.init.apply( this, arguments ); 162 | } else { 163 | $.error( 'Method ' + method + ' does not exist on jQuery.tooltip' ); 164 | } 165 | } 166 | 167 | function incrementalDraw($table, dimensions, context, row, col, opts) { 168 | // Prepare callback that will be invoked when data is ready 169 | var callBack = function(curData){ 170 | var dimName = opts.verticalDimensions[0]; 171 | var values = getDataValues(curData, context, dimName, ''); 172 | console.log("curData", curData); 173 | console.log("values", values); 174 | // drawDimension($div, opts, dimensions, context, curData) // fffff 175 | 176 | $pivotDataTable = $table.find('.' + opts.div22Class + ' .' + opts.pivotDataTableClass); 177 | 178 | var $horizontalDimDiv = $table.find('.' + opts.div21Class); 179 | var values = drawDimension($horizontalDimDiv, opts, opts.horizontalDimensions, [], curData); 180 | insertRows($pivotDataTable[0], opts, 0, values.length); 181 | 182 | 183 | var $verticalDimDiv = $table.find('.' + opts.div12Class); 184 | values = drawDimension($verticalDimDiv, opts, opts.verticalDimensions, [], curData); 185 | insertColumns($pivotDataTable[0], opts, 0, values.length, curData); 186 | 187 | syncDimensionsSizes($table, opts); 188 | }; 189 | 190 | // Trying to get current data 191 | var curData = opts.dataProvider(opts, dimensions, context, callBack); 192 | 193 | if(curData) { 194 | // If method returns data - display it 195 | callBack(curData); 196 | } 197 | } 198 | 199 | function createConfigDialog($table, opts) { 200 | var $dialog = $table.find('.' + opts.configDialogClass); 201 | var busyDims = {}; 202 | 203 | var verticalDimensionsHtml = '
  • ' + opts.verticalDimensionListTitle + '
  • '; 204 | for(var i = 0; i < opts.verticalDimensions.length; i++) { 205 | var key = opts.verticalDimensions[i]; 206 | busyDims[key] = true; 207 | var dim = opts.dimensions[key]; 208 | verticalDimensionsHtml = verticalDimensionsHtml + '
  • ' + dim.label + '
  • '; 209 | } 210 | if(!opts.configuration || !opts.configuration.verticalDimensions) { 211 | verticalDimensionsHtml = ''; 212 | } 213 | 214 | var horizontalDimensionsHtml = '
  • ' + opts.horizontalDimensionListTitle + '
  • '; 215 | for(var i = 0; i < opts.horizontalDimensions.length; i++) { 216 | var key = opts.horizontalDimensions[i]; 217 | busyDims[key] = true; 218 | var dim = opts.dimensions[key]; 219 | horizontalDimensionsHtml = horizontalDimensionsHtml + '
  • ' + dim.label + '
  • '; 220 | } 221 | if(!opts.configuration || !opts.configuration.horizontalDimensions) { 222 | var horizontalDimensionsHtml = ''; 223 | } 224 | 225 | var filterDimensionsHtml = '
  • ' + opts.filterDimensionListTitle + '
  • '; 226 | for(var i = 0; i < opts.filterDimensions.length; i++) { 227 | var key = opts.filterDimensions[i]; 228 | busyDims[key] = true; 229 | var dim = opts.dimensions[key]; 230 | var filterValues = getDataValues(opts, [], key, ''); 231 | 232 | var filterValuesHtml = '
      '; 233 | for(var idx = 0; idx < filterValues.length; idx++) { 234 | var filterValue = filterValues[idx]; 235 | filterValuesHtml = filterValuesHtml + "
    • " + filterValue.label + "
    • "; 236 | 237 | } 238 | filterValuesHtml = filterValuesHtml + '
    '; 239 | 240 | 241 | filterDimensionsHtml = filterDimensionsHtml + '
  • ' + dim.label + 242 | filterValuesHtml + 243 | '
  • '; 244 | } 245 | if(!opts.configuration || !opts.configuration.filterDimensions) { 246 | filterDimensionsHtml = ''; 247 | } 248 | 249 | var dimensionsHtml = ''; 250 | for(var key in opts.dimensions) { 251 | if(busyDims[key] != true) { 252 | var dim = opts.dimensions[key]; 253 | dimensionsHtml = dimensionsHtml + '
  • ' + dim.label + '
  • '; 254 | } 255 | } 256 | 257 | $dialog.append( 258 | '
      ' + 259 | '
    • ' + opts.dimensionListTitle + '
    • ' + 260 | '
    ' + 261 | '
      ' + 262 | dimensionsHtml + 263 | horizontalDimensionsHtml + 264 | verticalDimensionsHtml + 265 | filterDimensionsHtml + 266 | '
    ' 267 | ); 268 | $dialog.find('ul.' + opts.dimensionListClass).sortable({ 269 | cancel: '.' + opts.dimensionListTitleClass, 270 | update : function() { 271 | // Update pivot table when dimensions has been changed 272 | var vdims = [], hdims = [], fdims=[], curdims=[]; 273 | $dialog.find('.' + opts.dimensionListClass + ' li').each(function(){ 274 | 275 | var dim = jQuery(this).attr('dim'); 276 | if(dim) { 277 | curdims[curdims.length] = dim; 278 | } 279 | if(jQuery(this).attr('vdims') == 'true') { 280 | curdims = vdims; 281 | } 282 | if(jQuery(this).attr('hdims') == 'true') { 283 | curdims = hdims; 284 | } 285 | if(jQuery(this).attr('fdims') == 'true') { 286 | curdims = fdims; 287 | } 288 | }); 289 | if(opts.configuration && opts.configuration.verticalDimensions == true) { 290 | opts.verticalDimensions = vdims; 291 | } 292 | if(opts.configuration && opts.configuration.horizontalDimensions == true) { 293 | opts.horizontalDimensions = hdims; 294 | } 295 | if(opts.configuration && opts.configuration.filterDimensions == true) { 296 | opts.filterDimensions = fdims; 297 | } 298 | if(opts.cookiePrefix && opts.storeDimConfig) { 299 | var sHorizontalDims = opts.horizontalDimensions.join(';'); 300 | var sVerticalDims = opts.verticalDimensions.join(';'); 301 | var sFilterDims = opts.filterDimensions.join(';'); 302 | $.cookie(opts.cookiePrefix + 'horizontalDims', sHorizontalDims, {expires:15}); 303 | $.cookie(opts.cookiePrefix + 'verticalDims', sVerticalDims, {expires:15}); 304 | $.cookie(opts.cookiePrefix + 'filterDims', sFilterDims, {expires:15}); 305 | } 306 | redraw($table, opts); 307 | } 308 | }); 309 | } 310 | 311 | function toggleConfig($table, opts) { 312 | if(opts.configuration) { 313 | var $div11 = $table.find('.' + opts.div11Class); 314 | $div11.toggleClass(opts.configWindowActivatedClass); 315 | if($div11.hasClass(opts.configWindowActivatedClass)) { 316 | jQuery('#' + opts.pivotConfigDialogId).dialog('open'); 317 | } else { 318 | var $dialog = jQuery('#' + opts.pivotConfigDialogId); 319 | $dialog.dialog('close'); 320 | } 321 | } 322 | } 323 | 324 | function getColIndex($div, opts) { 325 | // Find Top level dimension div 326 | var $top; 327 | if($div.hasClass(opts.levelClassPrefix + '0')) { 328 | $top = $div.parent(); 329 | } else { 330 | $top = $div.parents('.' + opts.levelClassPrefix + '0').parent(); 331 | } 332 | var index = 0; 333 | var found = false; 334 | $top.find('.' + opts.dimLabelClass).each(function() { 335 | if(!found) { 336 | var $div0 = $div[0]; 337 | if(this == $div0) { 338 | found = true; 339 | return false; 340 | } else { 341 | var $this = $(this); 342 | if($this.parent().children('.' + opts.dimCellClass).size() == 0) { 343 | index++; 344 | } 345 | } 346 | } 347 | }); 348 | return index; 349 | } 350 | 351 | function getRowIndex($div, opts) { 352 | // Find Top level dimension div 353 | var $top; 354 | if($div.hasClass(opts.levelClassPrefix + '0')) { 355 | $top = $div.parent(); 356 | } else { 357 | $top = $div.parents('.' + opts.levelClassPrefix + '0').parent(); 358 | } 359 | var index = 0; 360 | var found = false; 361 | $top.find('.' + opts.dimLabelClass).each(function() { 362 | if(!found) { 363 | var $div0 = $div[0]; 364 | if(this == $div0) { 365 | found = true; 366 | return false; 367 | } else { 368 | var $this = $(this); 369 | index++; 370 | } 371 | } 372 | }); 373 | return index; 374 | } 375 | 376 | function getRowContexts($table, opts) { 377 | var contexts = []; 378 | $table.find('.' + opts.div21Class + ' .' + opts.dimCellClass).each(function(){ 379 | contexts[contexts.length] = this.pivotContext; 380 | }); 381 | return contexts; 382 | } 383 | 384 | function getColContexts($table, opts) { 385 | var contexts = []; 386 | $table.find('.' + opts.div12Class + ' .' + opts.dimCellClass).not('.' + opts.expandedDimCellClass).each(function(){ 387 | contexts[contexts.length] = this.pivotContext; 388 | }); 389 | return contexts; 390 | } 391 | 392 | function dimColSubLevelCount($div, opts) { 393 | // Find Top level dimension div 394 | var $top = $div.parent(); 395 | var index = 0; 396 | var $div0 = $div[0]; 397 | $top.find('.' + opts.dimLabelClass).each(function() { 398 | var $this = $(this); 399 | if($this.parent().children('.' + opts.dimCellClass).size() == 0) { 400 | index++; 401 | } 402 | }); 403 | return index; 404 | } 405 | 406 | function dimRowSubLevelCount($div, opts) { 407 | // Find Top level dimension div 408 | var $top = $div.parent(); 409 | var index = 0; 410 | var $div0 = $div[0]; 411 | index = $top.find('.' + opts.dimLabelClass).not($div0).size(); 412 | /* 413 | $top.find('.' + opts.dimLabelClass).each(function() { 414 | if($div[0] != this) { 415 | index++; 416 | } 417 | }); 418 | //*/ 419 | return index; 420 | } 421 | 422 | function redraw($table, opts) { 423 | var $horizontalDimDiv = $table.find('.' + opts.div21Class); 424 | $horizontalDimDiv.html(''); 425 | // var curData = opts.dataProvider(opts, [], [opts.verticalDimensions[0], opts.horizontalDimensions[0]]);// TODO 426 | 427 | // drawDimension($horizontalDimDiv, opts, opts.horizontalDimensions, [], curData); 428 | 429 | var $verticalDimDiv = $table.find('.' + opts.div12Class); 430 | $verticalDimDiv.html(''); 431 | var $dataDimDiv = $table.find('.' + opts.div22Class); 432 | $dataDimDiv.html(''); 433 | /* 434 | drawDimension($verticalDimDiv, opts, opts.verticalDimensions, [], curData); 435 | 436 | drawDataTable($table, opts, curData) 437 | */ 438 | incrementalDraw($table, [], [opts.verticalDimensions[0], opts.horizontalDimensions[0]], 0, 0, opts); 439 | } 440 | 441 | function drawDataTable($table, opts, curData) { 442 | $pivotDataTable = $table.find('.' + opts.div22Class + ' .' + opts.pivotDataTableClass); 443 | $pivotDataTable.html(''); 444 | 445 | drawDataRows($table, $pivotDataTable, opts, [], curData); 446 | 447 | fillDataValues($table, opts); 448 | syncDimensionsSizes($table, opts); 449 | } 450 | 451 | function initExpandListeners($table, opts) { 452 | // Expand/Collapse column/row 453 | // Columns 454 | $table.on('click', '.' + opts.div12Class + ' .' + opts.dimLabelClass, function(event){ 455 | var $pivotDataTable = $table.find('.' + opts.div22Class + ' .' + opts.pivotDataTableClass); 456 | var t1 = new Date(); 457 | var onExpand, onCollapse; 458 | var $this = $(this); 459 | var resync = false; 460 | if($this.hasClass(opts.expandedDimLabelClass)) { 461 | // Collapse columns 462 | var countToDelete = dimColSubLevelCount($this, opts); 463 | if(countToDelete > 0) { 464 | var firstColumn = getColIndex($this, opts); 465 | var pivotDataTable = $pivotDataTable[0]; 466 | var totalWidth = getTotalWidth(pivotDataTable, firstColumn, countToDelete - 1); 467 | 468 | // $this.parent().find('.' + opts.dimCellClass).animate({opacity:0}, 200); 469 | 470 | deleteColumns(pivotDataTable, firstColumn, countToDelete - 1, function(){ 471 | $this.parent().find('.' + opts.dimCellClass).remove(); 472 | 473 | $this.width(totalWidth + "px") 474 | 475 | var width = -1; 476 | for(var rowIdx = 0; rowIdx < pivotDataTable.rows.length; rowIdx++) { 477 | var row = pivotDataTable.rows[rowIdx]; 478 | cell = row.cells[firstColumn]; 479 | if(width < 0) { 480 | width = jQuery(cell).width(); 481 | } 482 | } 483 | 484 | $this.removeClass(opts.expandedDimLabelClass).addClass(opts.collapsedDimLabelClass); 485 | $this.parent().removeClass(opts.expandedDimCellClass).addClass(opts.collapsedDimCellClass); 486 | 487 | fillDataValues($table, opts); 488 | syncDimensionsSizes($table, opts); 489 | }); 490 | } 491 | onCollapse = opts.onCollapse; 492 | } else if($this.hasClass(opts.collapsedDimLabelClass)){ 493 | // Expand columns 494 | var pivotContext = this.parentElement.pivotContext; 495 | var colIdx = getColIndex($this, opts); 496 | var $parent = $this.parent(); 497 | 498 | var callBack = function(curData) { 499 | drawDimension($parent, opts, opts.verticalDimensions, null, curData); 500 | var colCount = dimColSubLevelCount($this, opts); 501 | if(colCount > 0) { 502 | $this.addClass(opts.expandedDimLabelClass).removeClass(opts.collapsedDimLabelClass); 503 | $this.parent().addClass(opts.expandedDimCellClass).removeClass(opts.collapsedDimCellClass); 504 | if(colCount > 0) { 505 | insertColumns($pivotDataTable[0], opts, colIdx, colCount - 1, curData); 506 | } 507 | resync = true; 508 | } 509 | syncDimensionsSizes($table, opts); 510 | 511 | onExpand = opts.onExpand; 512 | }; 513 | // Create context 514 | var context = createProviderContext(pivotContext, opts.verticalDimensions); 515 | var visibleDims = getVisibleDimensions($table, opts); 516 | var maxLevel = getCurrentVerticalMaxLevel($table, opts); 517 | if(pivotContext.length == maxLevel) { 518 | visibleDims.push(opts.verticalDimensions[pivotContext.length]); 519 | } 520 | 521 | var data = opts.dataProvider(opts, context, visibleDims, callBack);// TODO 522 | if(data) { 523 | callBack(data); 524 | } 525 | } 526 | if(resync) { 527 | fillDataValues($table, opts); 528 | if(opts.resizable) { 529 | if(opts.resizableWidth) { 530 | doResizeWidth($table); 531 | } 532 | if(opts.resizableHeight) { 533 | doResizeHeight($table); 534 | } 535 | // doResize($table); 536 | } 537 | if(onCollapse) { 538 | onCollapse(this); 539 | } 540 | if(onExpand) { 541 | onExpand(this); 542 | } 543 | syncDimensionsSizes($table, opts); 544 | } 545 | return false; 546 | }); 547 | // Rows 548 | $table.on('click', '.' + opts.div21Class + ' .' + opts.dimLabelClass, function(event){ 549 | var t1 = new Date(); 550 | var onExpand, onCollapse; 551 | if(event.isPropagationStopped()) 552 | return; 553 | 554 | var $this = $(this); 555 | var $parent = $this.parent(); 556 | var resync = false; 557 | if($this.hasClass(opts.expandedDimLabelClass)) { 558 | var countToDelete = dimRowSubLevelCount($this, opts); 559 | var firstRow = getRowIndex($this, opts); 560 | var pivotDataTable = $pivotDataTable[0]; 561 | if(countToDelete > 0) { 562 | deleteRows(pivotDataTable, firstRow + 1, countToDelete); 563 | $parent.find('.' + opts.dimCellClass).remove(); 564 | $this.removeClass(opts.expandedDimLabelClass); 565 | $this.addClass(opts.collapsedDimLabelClass); 566 | $parent.removeClass(opts.expandedDimCellClass); 567 | $parent.addClass(opts.collapsedDimCellClass); 568 | resync = true; 569 | } 570 | onCollapse = opts.onCollapse; 571 | } else if($this.hasClass(opts.collapsedDimLabelClass)){ 572 | // Expand row 573 | var pivotContext = this.parentElement.pivotContext; 574 | 575 | var rowIdx = getRowIndex($this, opts); 576 | var callBack = function(curData) { 577 | drawDimension($parent, opts, opts.horizontalDimensions, null, curData); 578 | $this.addClass(opts.expandedDimLabelClass).removeClass(opts.collapsedDimLabelClass); 579 | $parent.addClass(opts.expandedDimCellClass).removeClass(opts.collapsedDimCellClass); 580 | var rowCount = dimRowSubLevelCount($this, opts); 581 | if(rowCount > 0) { 582 | insertRows($pivotDataTable[0], opts, rowIdx + 1, rowCount, curData); 583 | resync = true; 584 | } 585 | syncDimensionsSizes($table, opts); 586 | 587 | onExpand = opts.onExpand; 588 | 589 | }; 590 | 591 | // Create context 592 | var context = createProviderContext(pivotContext, opts.horizontalDimensions); 593 | var visibleDims = getVisibleDimensions($table, opts); 594 | var maxLevel = getCurrentHorizontalMaxLevel($table, opts); 595 | if(pivotContext.length == maxLevel) { 596 | visibleDims.push(opts.horizontalDimensions[pivotContext.length]); 597 | } 598 | 599 | var data = opts.dataProvider(opts, context, visibleDims, callBack);// TODO 600 | if(data) { 601 | callBack(data); 602 | } 603 | 604 | onExpand = opts.onExpand; 605 | } 606 | if(resync) { 607 | if(opts.resizable) { 608 | if(opts.resizableWidth) { 609 | doResizeWidth($table); 610 | } 611 | if(opts.resizableHeight) { 612 | doResizeHeight($table); 613 | } 614 | // doResize($table); 615 | } 616 | fillDataValues($table, opts); 617 | if(onCollapse) { 618 | onCollapse(this); 619 | } 620 | if(onExpand) { 621 | onExpand(this); 622 | } 623 | syncDimensionsSizes($table, opts); 624 | } 625 | var t2 = new Date(); 626 | // jQuery('#log').html("Time = " + (t2.valueOf() - t1.valueOf())); 627 | return false; 628 | }); 629 | } 630 | 631 | function createProviderContext(pivotContext, dimensions) { 632 | var res = {}; 633 | for(var i = 0; i < pivotContext.length; i++) { 634 | res[dimensions[i]] = pivotContext[i]; 635 | } 636 | return res; 637 | } 638 | 639 | function getVisibleDimensions($table, opts) { 640 | var res = []; 641 | getInnerVisibleDimensions($table, opts, opts.div21Class, opts.horizontalDimensions, res); 642 | getInnerVisibleDimensions($table, opts, opts.div12Class, opts.verticalDimensions, res); 643 | return res; 644 | } 645 | 646 | function getInnerVisibleDimensions($table, opts, selector, dimensions, res) { 647 | var max = 0; 648 | $table.find('.' + selector + ' .' + opts.dimCellClass).each(function(){ 649 | if(max < this.pivotContext.length) 650 | max = this.pivotContext.length; 651 | }); 652 | for(var i = 0; i < max; i++) { 653 | res.push(dimensions[i]); 654 | } 655 | } 656 | 657 | /** 658 | * Calclulates max expanded level for vertical dimensions 659 | */ 660 | function getCurrentVerticalMaxLevel($table, opts) { 661 | var max = 0; 662 | $table.find('.' + opts.div12Class + ' .' + opts.dimCellClass).each(function(){ 663 | if(max < this.pivotContext.length) 664 | max = this.pivotContext.length; 665 | }); 666 | return max; 667 | } 668 | 669 | /** 670 | * Calclulates max expanded level for horizontal dimensions 671 | */ 672 | function getCurrentHorizontalMaxLevel($table, opts) { 673 | var max = 0; 674 | $table.find('.' + opts.div21Class + ' .' + opts.dimCellClass).each(function(){ 675 | if(max < this.pivotContext.length) 676 | max = this.pivotContext.length; 677 | }); 678 | return max; 679 | } 680 | 681 | function getTotalWidth(table, firstColumn, columnCount) { 682 | var res = 0; 683 | var row = table.rows[0]; 684 | for(var i = 0; i < columnCount; i++) { 685 | cell = row.cells[firstColumn + i]; 686 | var width = jQuery(cell).width(); 687 | res += width; 688 | } 689 | return res; 690 | } 691 | 692 | function fillDataValues($table, opts, force) { 693 | var rowContexts = getRowContexts($table, opts); 694 | var colContexts = getColContexts($table, opts); 695 | var $dataTable = $table.find('.' + opts.div22Class + ' .' + opts.pivotDataTableClass); 696 | var dataTable = $dataTable[0]; 697 | var rowItems; 698 | for(var rowIdx = 0; rowIdx < dataTable.rows.length; rowIdx++) { 699 | var row = dataTable.rows[rowIdx]; 700 | var rowContext = rowContexts[rowIdx]; 701 | rowItems = null; 702 | for(var colIdx = 0; colIdx < row.cells.length; colIdx++) { 703 | var colContext = colContexts[colIdx]; 704 | var cell = row.cells[colIdx]; 705 | cell.colContext = colContext; 706 | cell.rowContext = rowContext; 707 | if(cell.isCalculated != true || force == true) { 708 | if(rowItems == null) { 709 | rowItems = opts.map(rowContext, [], opts.data); 710 | } 711 | var maps = opts.map(rowContext, colContext, rowItems['default']); 712 | var reduces = opts.reduce(maps); 713 | var html = ""; 714 | if(opts.dataCellRenderer) { 715 | html = opts.dataCellRenderer(reduces, colContext, rowContext, opts); 716 | } else if($("#cellTemplate").size() > 0){ 717 | //* 718 | var template = $("#cellTemplate").html(); 719 | html = _.template(template,{items:reduces, rowContext:rowContext, colContext:colContext, options:opts}) 720 | // var html = "555"; 721 | //*/ 722 | } 723 | jQuery(cell).html('
    ' + html + '
    '); 724 | cell.isCalculated = true; 725 | } 726 | } 727 | } 728 | } 729 | 730 | function contextToString(context) { 731 | var s = '['; 732 | for(var i = 0; i < context.length; i++) { 733 | var item = context[i]; 734 | if(i > 0) { 735 | s = s + ', '; 736 | } 737 | s = s + item.label; 738 | } 739 | s = s + ']'; 740 | return s; 741 | } 742 | 743 | function insertColumns(table, opts, startColumnIdx, columnCount, curData) { 744 | var $table = $(table); 745 | var rowContexts = getRowContexts($table.parents('.cypivot'), opts); 746 | var colContexts = getColContexts($table.parents('.cypivot'), opts); 747 | 748 | for(var rowIdx = 0; rowIdx < table.rows.length; rowIdx++) { 749 | var row = table.rows[rowIdx]; 750 | var rowContext = rowContexts[rowIdx]; 751 | for(var colCount = 0; colCount < columnCount; colCount++) { 752 | var colContext = colContexts[startColumnIdx + colCount]; 753 | 754 | var cell = row.insertCell(startColumnIdx + colCount); 755 | cell.className = opts.dataCellClass; 756 | var cellDiv = document.createElement('div'); 757 | cell.appendChild(cellDiv); 758 | cellDiv.className = opts.divDataCellClass; 759 | 760 | var maps = opts.map(rowContext, colContext, curData); 761 | var reduces = opts.reduce(maps); 762 | var html = ""; 763 | if(opts.dataCellRenderer) { 764 | html = opts.dataCellRenderer(reduces, colContext, rowContext, opts); 765 | } 766 | $(cellDiv).html(html); 767 | 768 | } 769 | } 770 | } 771 | 772 | function insertRows(table, opts, startRowIdx, rowCount, curData) { 773 | var $table = $(table); 774 | var rowContexts = getRowContexts($table.parents('.cypivot'), opts); 775 | var colContexts = getColContexts($table.parents('.cypivot'), opts); 776 | 777 | var firstRow = table.rows.length > 0 ? table.rows[0] : null; 778 | for(var rowIdx = 0; rowIdx < rowCount; rowIdx++) { 779 | var row = table.insertRow(startRowIdx + rowIdx); 780 | var rowContext = rowContexts[startRowIdx + rowIdx]; 781 | var html = []; 782 | if(firstRow) { // When we add to empty table firstRow is null 783 | for(var colIdx = 0; colIdx < firstRow.cells.length; colIdx++) { 784 | var colContext = colContexts[colIdx]; 785 | 786 | var maps = opts.map(rowContext, colContext, curData); 787 | var reduces = opts.reduce(maps); 788 | var cellHtml = ""; 789 | if(opts.dataCellRenderer) { 790 | cellHtml = opts.dataCellRenderer(reduces, colContext, rowContext, opts); 791 | } 792 | 793 | html.push(''); 794 | } 795 | } 796 | jQuery(row).html(html.join("")); 797 | } 798 | // fillDataValues($table, opts); 799 | } 800 | 801 | function deleteColumns(table, startColumnIdx, columnCount, finalizer) { 802 | for(var rowIdx = 0; rowIdx < table.rows.length; rowIdx++) { 803 | var row = table.rows[rowIdx]; 804 | for(var colCount = 0; colCount < columnCount; colCount++) { 805 | var cell = row.cells[startColumnIdx + colCount]; 806 | // jQuery(cell).addClass("___animation-cypivot"); 807 | row.deleteCell(startColumnIdx); 808 | } 809 | } 810 | 811 | if(finalizer) { 812 | finalizer(); 813 | } 814 | 815 | // Case when closed empty level (no children) 816 | if(columnCount == 0 && finalizer) { 817 | // finalizer(); 818 | } 819 | } 820 | 821 | function deleteRows(table, startRowIdx, rowCount) { 822 | for(var rowIdx = 0; rowIdx < rowCount; rowIdx++) { 823 | table.deleteRow(startRowIdx); 824 | } 825 | } 826 | 827 | function syncRowHeight($table, opts, newContext, $row) { 828 | var selector = '.' + opts.div21Class; 829 | for(var i = 0; i < newContext.length; i++) { 830 | selector = selector + ' .' + opts.levelClassPrefix + i + '.' + opts.pivotIdClassPrefix + newContext[i].id; 831 | } 832 | selector = selector + ' .' + opts.dimLabelClass; 833 | var $dimLabelDiv = $table.find(selector).first(); 834 | $row.resize(function() { 835 | $dimLabelDiv.css({height :($row.height() - 2 )+ "px"}); 836 | }); 837 | $dimLabelDiv.css({height :($row.height() -2) + "px"}); 838 | } 839 | 840 | function syncDimensionsSizes($table, opts) { 841 | if(opts.syncDimensionCellSizes == true) { 842 | syncRowsHeight($table, opts); 843 | syncColumnsWidth($table, opts); 844 | } 845 | } 846 | 847 | function syncColumnsWidth($table, opts) { 848 | var $pivotDataTable = $table.find('.' + opts.div22Class + ' .' + opts.pivotDataTableClass); 849 | var pivotDataTable = $pivotDataTable[0]; 850 | var firstRow = pivotDataTable.rows[0]; 851 | 852 | var i = 0; 853 | if(firstRow) { 854 | // $table.find('.' + opts.dimLabelClass).not('.' + opts.expandedDimLabelClass).each(function(){ 855 | $table.find('.' + opts.dimLabelClass).each(function(){ 856 | 857 | /* 858 | var cell = firstRow.cells[i]; 859 | var $td = jQuery(cell); 860 | var $dimLabelDiv = jQuery(this); 861 | var newWidth = ($td.width())+ "px"; 862 | $dimLabelDiv.css({width :newWidth}); 863 | i++; 864 | //*/ 865 | var $dimLabelDiv = jQuery(this); 866 | if($dimLabelDiv.hasClass(opts.expandedDimLabelClass)) { 867 | $dimLabelDiv.parent().css({width :'auto'}); 868 | } else { 869 | var cell = firstRow.cells[i]; 870 | var $td = jQuery(cell); 871 | // $dimLabelDiv.parent().css('width', ''); 872 | $dimLabelDiv.css('width', 'auto'); 873 | var newWidth = ($td.outerWidth())+ "px"; 874 | 875 | if(i < firstRow.cells.length - 1) { 876 | var nextCell = firstRow.cells[i + 1]; 877 | var $nextTd = jQuery(nextCell); 878 | 879 | var position = $td.position(); 880 | var nextPosition = $nextTd.position(); 881 | newWidth = nextPosition.left - position.left; 882 | // console.log("newWidth = " + newWidth); 883 | } 884 | 885 | $dimLabelDiv.parent().css({width :newWidth}); 886 | i++; 887 | } 888 | }); 889 | } 890 | } 891 | 892 | function syncRowsHeight($table, opts) { 893 | var $pivotDataTable = $table.find('.' + opts.div22Class + ' .' + opts.pivotDataTableClass); 894 | var pivotDataTable = $pivotDataTable[0]; 895 | var firstRow = pivotDataTable.rows[0]; 896 | 897 | var i = 0; 898 | $table.find('.' + opts.div21Class + ' .' + opts.dimLabelClass)/*.not('.' + opts.expandedDimLabelClass)*/.each(function(){ 899 | var row = pivotDataTable.rows[i]; 900 | var $tr = jQuery(row); 901 | var $dimLabelDiv = jQuery(this); 902 | var newHeight = ($tr.innerHeight() - 2)+ "px"; 903 | $dimLabelDiv.css({height:newHeight}); 904 | i++; 905 | }); 906 | } 907 | 908 | function drawDataRows($table, $pivotDataTable, opts, context, curData) { 909 | var dimName = opts.horizontalDimensions[context.length]; 910 | var newContext = clone(context); 911 | 912 | var dim = opts.dimensions[dimName]; 913 | if(dim) { 914 | var onDataLoad = function(values) { 915 | for(var valIdx = 0; valIdx < values.length; valIdx++) { 916 | var value = values[valIdx]; 917 | newContext[context.length] = value; 918 | var pivotDataTable = $pivotDataTable[0]; 919 | var row = pivotDataTable.insertRow(-1); 920 | var $row = jQuery(row); 921 | $row.addClass(opts.dataRowClass + ' ' + 922 | opts.levelClassPrefix + context.length + ' ' + opts.pivotIdClassPrefix + value.id + ' ' + 923 | '" pivotId = "' + value.id); 924 | 925 | drawDataCells($table, $pivotDataTable, $row, opts, newContext, []); 926 | 927 | if(opts.isExpanded(newContext) && newContext.length < opts.horizontalDimensions.length) { 928 | drawDataRows($table, $pivotDataTable, opts, newContext, curData); 929 | if(dim.showTotal && false) { 930 | $pivotDataTable.append('
    ' + 'Total ' + value.label + '
    ') 934 | } 935 | } 936 | } 937 | $table.css({height:'10px'}); 938 | $table.css({height:'auto'}); 939 | syncDimensionsSizes($table, opts); 940 | } 941 | var values; 942 | if(dim.values) { 943 | values = dim.values(context, onDataLoad); 944 | } else { 945 | // curData = opts.dataProvider(opts, [], [opts.verticalDimensions[0], opts.horizontalDimensions[0]]); 946 | values = getDataValues(curData, context, dimName, dim.sortFieldName); 947 | } 948 | if(values != undefined) { 949 | onDataLoad(values); 950 | } 951 | 952 | } 953 | } 954 | 955 | function getDataValues(dimData, context, dimName, sortFieldName) { 956 | var items = []; 957 | // var dimData = opts.dimData ? opts.dimData : opts.data; 958 | for(var i = 0; i < dimData.length; i++) { 959 | var item = dimData[i]; 960 | if(applyDataFilter(item, context)) { 961 | items.push(item); 962 | } 963 | } 964 | var res = []; 965 | var exist = {}; 966 | for(var i = 0; i < items.length; i++) { 967 | var item = items[i]; 968 | var apply = applyDataFilter(item, context); 969 | var value = item[dimName]; 970 | var valueId = typeof(value) == 'object' ? value.id : value; 971 | if(value && value != Number.POSITIVE_INFINITY && exist[valueId] != true) { 972 | exist[valueId] = true; 973 | res.push(value); 974 | } 975 | } 976 | 977 | if(sortFieldName != undefined) { 978 | res = res.sort(function(o1, o2){ 979 | if(o1[sortFieldName] < o2[sortFieldName]) 980 | return -1; 981 | if(o1[sortFieldName] > o2[sortFieldName]) 982 | return 1; 983 | return 0; 984 | }); 985 | } 986 | return res; 987 | } 988 | 989 | function applyDataFilter(test, filter) { 990 | for(var key in filter) { 991 | var filterItem = filter[key]; 992 | var dimName = filterItem.dimName; 993 | var o1 = filterItem.id; 994 | var testItem = test[dimName]; 995 | if(testItem != undefined) { 996 | var o2; 997 | if(typeof testItem === "number") { 998 | o2 = testItem; 999 | } else if(typeof testItem === "string") { 1000 | o2 = testItem; 1001 | } else { 1002 | o2 = testItem.id; 1003 | } 1004 | if(o2 != null && o2 != undefined) { 1005 | if(o1 != o2 && o2 != Number.POSITIVE_INFINITY) { 1006 | return false; 1007 | } 1008 | } 1009 | } 1010 | } 1011 | return true; 1012 | } 1013 | 1014 | function applyDimDataFilter(test, filter) { 1015 | for(var key in filter) { 1016 | var filterItem = filter[key]; 1017 | var dimName = filterItem.dimName; 1018 | var o1 = filterItem.id; 1019 | var testItem = test[dimName]; 1020 | if(testItem != undefined) { 1021 | var o2 = testItem.id; 1022 | if(o2 != null && o2 != undefined) { 1023 | if(o1 != o2 && o2 != -1) { 1024 | return false; 1025 | } 1026 | } 1027 | } 1028 | } 1029 | return true; 1030 | } 1031 | 1032 | 1033 | /** 1034 | * The function adds data cells into certain row in pivot data table 1035 | */ 1036 | function drawDataCells($table, $pivotDataTable, $row, opts, rowContext, colContext, curData) { 1037 | var dimName = opts.verticalDimensions[colContext.length]; 1038 | var newContext = clone(colContext); 1039 | 1040 | var dim = opts.dimensions[dimName]; 1041 | var rowValue = rowContext[rowContext.length - 1]; 1042 | var row = $row[0]; 1043 | if(dim) { 1044 | // var values = dim.values(colContext); 1045 | // var curData = opts.dataProvider(opts, [], [opts.verticalDimensions[0], opts.horizontalDimensions[0]]);// TODO 1046 | // opts.dataProvider()fffffffffff 1047 | var values = dim.values ? dim.values(colContext) : getDataValues(curData, colContext, dimName, dim.sortFieldName); 1048 | for(var valIdx = 0; valIdx < values.length; valIdx++) { 1049 | var value = values[valIdx]; 1050 | newContext[colContext.length] = value; 1051 | 1052 | if(opts.isExpanded(newContext) && newContext.length < opts.verticalDimensions.length) { 1053 | drawDataCells($table, $pivotDataTable, $row, opts, rowContext, newContext); 1054 | if(dim.showTotal) { 1055 | var cell = row.insertCell(row.cells.length); 1056 | var $cell = jQuery(cell); 1057 | cell.className = opts.dataCellClass + " " + opts.dataTotalCellClass + " " + 1058 | opts.levelClassPrefix + colContext.length + " " + opts.pivotIdClassPrefix + value.id; 1059 | $cell.attr('pivotId', value.id); 1060 | 1061 | $cell.html('
    ' + 'Total ' + value.label + '
    '); 1062 | cell.rowContext = rowContext; 1063 | cell.colContext = colContext; 1064 | } 1065 | } else { 1066 | var cell = row.insertCell(row.cells.length); 1067 | var $cell = jQuery(cell); 1068 | $cell.addClass(opts.dataCellClass + " " + opts.levelClassPrefix + colContext.length + 1069 | " " + opts.pivotIdClassPrefix + value.id); 1070 | $cell.attr('pivotId', value.id); 1071 | var rowLabel = typeof(rowValue) == 'object' ? rowValue.label : rowValue; 1072 | $cell.html('
    ' + rowLabel + ' - ' + rowLabel + '
    '); 1073 | } 1074 | } 1075 | } 1076 | } 1077 | 1078 | /** 1079 | * Draws dimensions. 1080 | * @param $div - jQuery html element which is clicked and should be displayed or whole dimensions panel so root dimensions will be displayed 1081 | * @param opts - pivot table options 1082 | * @param dimensions - dimensions 1083 | * @param context - context 1084 | * @param curData - current data array 1085 | * @returns values which were added as dimension labels 1086 | */ 1087 | function drawDimension($div, opts, dimensions, context, curData) { 1088 | var res; 1089 | if(!context) { 1090 | context = $div[0].pivotContext; 1091 | } else { 1092 | $div[0].pivotContext = clone(context); 1093 | } 1094 | var dimName = dimensions[context.length]; 1095 | var newContext = clone(context); 1096 | 1097 | var dim = opts.dimensions[dimName]; 1098 | var $totalDiv = $div.find('.' + opts.dimTotalCellClass); 1099 | if(dim) { 1100 | // var values = dim.values(context); 1101 | // var curData = opts.dataProvider(opts, [], [opts.verticalDimensions[0], opts.horizontalDimensions[0]]);// TODO 1102 | var values = dim.values ? dim.values(context) : getDataValues(curData, context, dimName, dim.sortFieldName); 1103 | res = values; 1104 | if(values.length > 0) { 1105 | $div.children('.' + opts.dimLabelClass).css({width: ''}); 1106 | } 1107 | for(var valIdx = 0; valIdx < values.length; valIdx++) { 1108 | var value = values[valIdx]; 1109 | var valueId = typeof(value) == 'object' ? value.id : value; 1110 | var valueLabel = typeof(value) == 'object' ? value.label : value; 1111 | value = typeof(value) == 'object' ? value : {id:value, label:value}; 1112 | value.dimName = dimName; 1113 | newContext[context.length] = value; 1114 | var dimCellDiv = document.createElement('div'); 1115 | if($totalDiv.size() > 0) { 1116 | dimCellDiv = $div[0].insertBefore(dimCellDiv, $totalDiv[0]); 1117 | } else { 1118 | dimCellDiv = $div[0].appendChild(dimCellDiv); 1119 | } 1120 | var $dimCellDiv = jQuery(dimCellDiv); 1121 | $dimCellDiv.addClass(opts.dimCellClass + " " + opts.dimCellClass + "-" + dimName + " " + opts.levelClassPrefix + context.length + " " + 1122 | opts.pivotIdClassPrefix + valueId); 1123 | $dimCellDiv.attr({ 1124 | 'pivotId' : valueId, 1125 | 'dimName': dimName, 1126 | 'title':dim.label + " : " + unescape(valueLabel) 1127 | }); 1128 | var cellHtml = opts.dimensionCellRenderer ? opts.dimensionCellRenderer(opts, newContext, false) : ""+ "" + valueLabel + ""; 1130 | $dimCellDiv.append('
    ' + cellHtml + 1134 | '
    '); 1135 | 1136 | var $divLabel = $dimCellDiv.children('.' + opts.dimLabelClass); 1137 | dimCellDiv.pivotContext = clone(newContext); 1138 | 1139 | if(opts.isExpanded(newContext) && newContext.length < dimensions.length) { 1140 | $divLabel.addClass(opts.expandedDimLabelClass); 1141 | $divLabel.parent().addClass(opts.expandedDimCellClass); 1142 | var $childDiv = $div.children('.' + opts.pivotIdClassPrefix + valueId); 1143 | drawDimension($childDiv, opts, dimensions, newContext, curData); 1144 | } else { 1145 | if(newContext.length < dimensions.length) { 1146 | $divLabel.addClass(opts.collapsedDimLabelClass); 1147 | $divLabel.parent().addClass(opts.collapsedDimCellClass); 1148 | } 1149 | } 1150 | } 1151 | 1152 | // Show total column if it does not exist, if it's not top level and it's vertical 1153 | if($totalDiv.size() == 0 && context.length > 0 && dimensions == opts.verticalDimensions) { 1154 | var dimCellDiv = document.createElement('div'); 1155 | dimCellDiv = $div[0].appendChild(dimCellDiv); 1156 | var $dimCellDiv = jQuery(dimCellDiv); 1157 | $dimCellDiv.addClass(opts.dimCellClass); 1158 | $dimCellDiv.addClass(opts.dimTotalCellClass); 1159 | $dimCellDiv.addClass(opts.levelClassPrefix + context.length); 1160 | // $dimCellDiv.addClass(opts.pivotIdClassPrefix + value.id); 1161 | // $dimCellDiv.attr('pivotId', value.id); 1162 | $dimCellDiv.attr('dimName', dimName); 1163 | dimCellDiv.pivotContext = clone(context); 1164 | 1165 | var cellHtml = opts.dimensionCellRenderer ? opts.dimensionCellRenderer(opts, newContext, true) : ""; 1166 | $dimCellDiv.append('
    ' + 'Total' + '
    '); 1168 | } 1169 | } 1170 | return res; 1171 | } 1172 | 1173 | /** 1174 | * Synchronizes sizes and scroll position of fixed columns with pivot itself. 1175 | */ 1176 | function syncScrollAndSize($table, opts) { 1177 | var scrollBarWidth = getScrollBarWidth(); 1178 | var $div22 = $table.find('.' + opts.div22Class); 1179 | 1180 | if(!opts.autoSize) { 1181 | $table.find('.' + opts.div12Class).css({ 1182 | 'width' : ($div22.width() - scrollBarWidth) + 'px' 1183 | }); 1184 | } 1185 | 1186 | var newHeight = $div22.height() - scrollBarWidth; 1187 | if(!opts.autoSize) { 1188 | $table.find('.' + opts.div21Class).css({ 1189 | 'height' : newHeight + 'px', 1190 | 'margin-bottom' : scrollBarWidth + 'px' 1191 | }); 1192 | } 1193 | $table.find('.' + opts.div12Class).scrollLeft($div22.scrollLeft()); 1194 | $table.find('.' + opts.div21Class).scrollTop($div22.scrollTop()); 1195 | } 1196 | 1197 | $.fn.cypivot.defaults = { 1198 | 1199 | // Specifies if size of dimension sizes must be synced. Sometimes it should be false, if block sizes are equals 1200 | // and specified in CSS. In this case pivot table works faster. 1201 | syncDimensionCellSizes : true, 1202 | 1203 | //Specifies if pivot table should be resize when window is resized. 1204 | resizable : true, 1205 | resizableWidth : true, 1206 | resizableHeight : true, 1207 | autoSize : false, 1208 | 1209 | // Default classes 1210 | tableClass : 'cypivot', 1211 | topRowClass : 'top-row', 1212 | bottomRowClass : 'bottom-row', 1213 | cell11Class : 'cell11', 1214 | cell12Class : 'cell12', 1215 | cell21Class : 'cell21', 1216 | cell22Class : 'cell22', 1217 | 1218 | div11Class : 'div11', 1219 | div12Class : 'div12', 1220 | div21Class : 'div21', 1221 | div22Class : 'div22', 1222 | 1223 | pivotDataTableClass : 'pivot-data-table', 1224 | dimLabelClass : 'dim-label', 1225 | dimCellIconClass : 'dim-cell-icon', 1226 | 1227 | dimCellClass : 'dim-cell', 1228 | expandableDimLabelClass : 'expandable-dim-label', 1229 | expandedDimLabelClass : 'expanded-dim-label', 1230 | collapsedDimLabelClass : 'collapsed-dim-label', 1231 | expandedDimCellClass : 'expanded-dim-cell', 1232 | collapsedDimCellClass : 'collapsed-dim-cell', 1233 | dimTotalCellClass : 'dim-total-cell', 1234 | dataTotalCellClass : 'data-total-cell', 1235 | levelClassPrefix : 'level-', 1236 | 1237 | dataRowClass : 'data-row', 1238 | dataCellClass : 'data-cell', 1239 | divDataCellClass : 'div-data-cell', 1240 | configDialogClass : 'config-dialog', 1241 | 1242 | dimensionListTitle : 'Dimensions', 1243 | horizontalDimensionListTitle : 'Rows', 1244 | verticalDimensionListTitle : 'Columns', 1245 | filterDimensionListTitle : 'Filters', 1246 | 1247 | configWindowActivatedClass : 'config-window-activated', 1248 | dimensionListClass : 'dimension-list', 1249 | firstDimensionListClass : 'first-dimension-list', 1250 | dimensionListTitleClass : 'dimension-list-title', 1251 | verticalDimensionListClass : 'vertical-dimension-list', 1252 | horizontalDimensionListClass: 'horizontal-dimension-list', 1253 | filterDimensionListClass : 'filter-dimension-list', 1254 | 1255 | pivotIdClassPrefix : 'pivot-id-', 1256 | pivotConfigDialogId : 'pivot-config-dialog', 1257 | 1258 | isExpanded : function(context){ 1259 | return context.length == 0; 1260 | }, 1261 | 1262 | configuration : { 1263 | horizontalDimensions : true, 1264 | verticalDimensions : true, 1265 | filterDimensions : false, 1266 | }, 1267 | 1268 | configLabel : 'Config', 1269 | 1270 | cookiePrefix : 'cy-pivot-', 1271 | storeDimConfig : true, 1272 | 1273 | dataCellRenderer : function(items, colContext, rowContext, opts) { 1274 | 1275 | var value = ""; 1276 | for(var i = 0; i < opts.valueDataFields.length; i++) { 1277 | var valueDataField = opts.valueDataFields[i]; 1278 | value = value + "" + items['default'].sum[valueDataField] + " "; 1279 | } 1280 | return value; 1281 | }, 1282 | 1283 | dataProvider : // null, 1284 | function(opts, context, visibleDims) 1285 | { 1286 | return opts.data; 1287 | }, 1288 | 1289 | dimensionCellRenderer : null // This function(opts, context, isTotal) should render dimension cell 1290 | /* Example 1291 | function(opts, context, isTotal) { 1292 | return '' + context[context.length - 1].label + ''; 1293 | }*/ 1294 | , 1295 | 1296 | dimensions : { 1297 | dim1 : 1298 | { 1299 | label :'Colors', 1300 | values : function(context) { 1301 | return [{id:1, label:'Red'}, {id:2, label:'Blue'}, {id:3, label:'Green'}]; 1302 | }, 1303 | showTotal : true, 1304 | }, 1305 | dim2 : 1306 | { 1307 | label :'Cities', 1308 | values : function(context) { 1309 | return [{id:1, label:'Saint-Petersburg'}, {id:2, label:'Moscow'}, {id:3, label:'London'}, {id:4, label:'Paris'}]; 1310 | }, 1311 | showTotal : true, 1312 | }, 1313 | dim3 : 1314 | { 1315 | label :'Shapes', 1316 | values : function(context) { 1317 | return [{id:1, label:'Round'}, {id:2, label:'Square'}, {id:3, label:'Star'}, {id:4, label:'Ellipse'}]; 1318 | }, 1319 | showTotal : true, 1320 | }, 1321 | dim4 : 1322 | { 1323 | label :'Planets', 1324 | values : function(context) { 1325 | return [{id:1, label:'Earth'}, {id:2, label:'Moon'}, {id:3, label:'Mars'}, {id:4, label:'Neptune'}]; 1326 | }, 1327 | showTotal : true, 1328 | }, 1329 | dim5 : 1330 | { 1331 | label :'Geo', 1332 | values : function(context) { 1333 | return [{id:1, label:'Asia'}, {id:2, label:'Africa'}, {id:3, label:'Europe'}, {id:4, label:'America'}]; 1334 | }, 1335 | showTotal : true, 1336 | }, 1337 | dim6 : 1338 | { 1339 | label :'Lessons', 1340 | values : function(context) { 1341 | return [{id:1, label:'Phisics'}, {id:2, label:'Chemistry'}, {id:3, label:'Math'}, {id:4, label:'Geography'}]; 1342 | }, 1343 | showTotal : true, 1344 | }, 1345 | }, 1346 | 1347 | horizontalDimensions: ['dim1', 'dim3', 'dim5'], 1348 | // verticalDimensions : ['dim2', 'dim4', 'dim6'], 1349 | // horizontalDimensions: ['dim1'], 1350 | verticalDimensions : ['dim2'], 1351 | filterDimensions : [], 1352 | 1353 | data : [ 1354 | { 1355 | dim1 : 'Red', 1356 | dim2 : 'Saint-Petersburg', 1357 | dim3 : 'Round', 1358 | dim4 : 'Earth', 1359 | dim5 : 'Asia', 1360 | dim6 : 'Phisics', 1361 | value : 1, 1362 | }, 1363 | { 1364 | dim1 : 'Red', 1365 | dim2 : 'Saint-Petersburg', 1366 | dim3 : 'Round', 1367 | dim4 : 'Earth', 1368 | dim5 : 'Asia', 1369 | dim6 : 'Chemistry', 1370 | value : 1, 1371 | }, 1372 | { 1373 | dim1 : 'Blue', 1374 | dim2 : 'Saint-Petersburg', 1375 | dim3 : 'Round', 1376 | dim4 : 'Earth', 1377 | dim5 : 'Asia', 1378 | dim6 : 'Chemistry', 1379 | value : 1, 1380 | }, 1381 | { 1382 | dim1 : 'Green', 1383 | dim2 : 'Moscow', 1384 | dim3 : 'Round', 1385 | dim4 : 'Earth', 1386 | dim5 : 'Asia', 1387 | dim6 : 'Chemistry', 1388 | value : 1, 1389 | }, 1390 | ], 1391 | 1392 | map : function(rowContext, colContext, data) { 1393 | var res = []; 1394 | var strictedItems = []; 1395 | /* 1396 | var filter = {}; 1397 | for(var i = 0; i < rowContext.length; i++) { 1398 | var value = rowContext[i]; 1399 | filter[value.dimName] = value; 1400 | } 1401 | for(var i = 0; i < colContext.length; i++) { 1402 | var value = colContext[i]; 1403 | filter[value.dimName] = value; 1404 | } 1405 | */ 1406 | var filter = []; 1407 | for(var i = 0; i < rowContext.length; i++) { 1408 | var value = rowContext[i]; 1409 | filter.push(value); 1410 | } 1411 | for(var i = 0; i < colContext.length; i++) { 1412 | var value = colContext[i]; 1413 | filter.push(value); 1414 | } 1415 | 1416 | for(var i = 0; i < data.length; i++) { 1417 | var item = data[i]; 1418 | if(applyFilter2(item, filter)) { 1419 | res.push(item); 1420 | } 1421 | //* 1422 | if(strictApplyFilter(this, item, filter)) { 1423 | strictedItems[strictedItems.length] = item; 1424 | } 1425 | //*/ 1426 | 1427 | } 1428 | return { 1429 | 'default':res, 1430 | stricted:strictedItems 1431 | }; 1432 | }, 1433 | 1434 | valueDataFields : ['value'], 1435 | 1436 | /* Reduces is an array of objects. Each object specifies what 'map' items it requires and it puts result(s) into reduces map. 1437 | * So the only reduce function can calculate 'sum', 'max', 'min', 'avg', 'count' etc. values. 1438 | */ 1439 | reduce : function(mapItems) { 1440 | var reduces = {}; 1441 | 1442 | for(itemsKey in mapItems) { 1443 | var items = mapItems[itemsKey]; 1444 | var sum = {}; 1445 | var count = {}; 1446 | var max = {}, min = {}; 1447 | 1448 | /* 1449 | for(var i = 0; i < this.valueDataFields.length; i++) { 1450 | var valueDataField = this.valueDataFields[i]; 1451 | sum[valueDataField] = 0; 1452 | count[valueDataField] = items.length; 1453 | } 1454 | */ 1455 | 1456 | for(var j = 0; j < this.valueDataFields.length; j++) { 1457 | var valueDataField = this.valueDataFields[j]; 1458 | var curSum = 0; 1459 | var curMax = Number.NEGATIVE_INFINITY 1460 | var curMin = Number.POSITIVE_INFINITY 1461 | for(var i = 0; i < items.length; i++) { 1462 | var item = items[i]; 1463 | var itemValue = item[valueDataField]; 1464 | 1465 | curSum += itemValue; 1466 | if(curMin > itemValue) { 1467 | curMin = itemValue; 1468 | } 1469 | if(curMax < itemValue) { 1470 | curMax = itemValue; 1471 | } 1472 | } 1473 | sum[valueDataField] = curSum; 1474 | max[valueDataField] = curMax; 1475 | min[valueDataField] = curMin; 1476 | 1477 | } 1478 | /* 1479 | * 1480 | for(var i = 0; i < items.length; i++) { 1481 | var item = items[i]; 1482 | for(var j = 0; j < this.valueDataFields.length; j++) { 1483 | var valueDataField = this.valueDataFields[j]; 1484 | var itemValue = item[valueDataField]; 1485 | if(isNumber(itemValue)) { 1486 | sum[valueDataField] += itemValue; 1487 | var curMin = min[valueDataField]; 1488 | if(curMin) { 1489 | if(curMin > itemValue) { 1490 | min[valueDataField] = itemValue; 1491 | } 1492 | } else { 1493 | min[valueDataField] = itemValue; 1494 | } 1495 | var curMax = max[valueDataField]; 1496 | if(curMax) { 1497 | if(curMax > itemValue) { 1498 | max[valueDataField] = itemValue; 1499 | } 1500 | } else { 1501 | max[valueDataField] = itemValue; 1502 | } 1503 | } 1504 | } 1505 | } 1506 | */ 1507 | reduces[itemsKey] = {}; 1508 | reduces[itemsKey]['sum'] = sum; 1509 | reduces[itemsKey]['count'] = items.length; 1510 | reduces[itemsKey]['avg'] = sum / items.length; 1511 | reduces[itemsKey]['max'] = max; 1512 | reduces[itemsKey]['min'] = min; 1513 | } 1514 | return reduces; 1515 | }, 1516 | 1517 | reduces : [ 1518 | { 1519 | valueDataFields : ['value'], 1520 | map : 'default', 1521 | reduce : function(items, reduces) { 1522 | var sum = {}; 1523 | var count = {}; 1524 | for(var i = 0; i < this.valueDataFields.length; i++) { 1525 | var valueDataField = this.valueDataFields[i]; 1526 | sum[valueDataField] = 0; 1527 | count[valueDataField] = items.length; 1528 | } 1529 | var max = {}, min = {}; 1530 | for(var i = 0; i < items.length; i++) { 1531 | var item = items[i]; 1532 | for(var j = 0; j < this.valueDataFields.length; j++) { 1533 | var valueDataField = this.valueDataFields[j]; 1534 | sum[valueDataField] += item[valueDataField]; 1535 | if(min[valueDataField]) { 1536 | if(min[valueDataField] > item[valueDataField]) { 1537 | min[valueDataField] = item[valueDataField]; 1538 | } 1539 | } else { 1540 | min[valueDataField] = item[valueDataField]; 1541 | } 1542 | if(max[valueDataField]) { 1543 | if(max[valueDataField] > item[valueDataField]) { 1544 | max[valueDataField] = item[valueDataField]; 1545 | } 1546 | } else { 1547 | max[valueDataField] = item[valueDataField]; 1548 | } 1549 | } 1550 | } 1551 | if(reduces) { 1552 | reduces['sum'] = sum; 1553 | reduces['count'] = items.length; 1554 | reduces['avg'] = sum / items.length; 1555 | reduces['max'] = max; 1556 | reduces['min'] = min; 1557 | } 1558 | return sum; 1559 | } 1560 | }, 1561 | ], 1562 | }; 1563 | 1564 | function clone(o) { 1565 | if(!o || 'object' !== typeof o) { 1566 | return o; 1567 | } 1568 | var c = 'function' === typeof o.pop ? [] : {}; 1569 | var p, v; 1570 | for(p in o) { 1571 | if(o.hasOwnProperty(p)) { 1572 | v = o[p]; 1573 | if(v && 'object' === typeof v) { 1574 | c[p] = clone(v); 1575 | } 1576 | else { 1577 | c[p] = v; 1578 | } 1579 | } 1580 | } 1581 | return c; 1582 | } 1583 | 1584 | function applyFilter(test, filter) { 1585 | for(var key in filter) { 1586 | var filterItem = filter[key]; 1587 | // var dimName = filterItem.dimName; 1588 | // var o1 = filterItem.id; 1589 | var o2 = test[filterItem.dimName]; 1590 | //if(o1 != Number.POSITIVE_INFINITY) { 1591 | if(filterItem.id != o2) {// && o2 != -1) { 1592 | return false; 1593 | } 1594 | //} 1595 | } 1596 | return true; 1597 | } 1598 | 1599 | function applyFilter2(test, filter) { 1600 | for(var i = 0; i < filter.length; i++) { 1601 | var filterItem = filter[i]; 1602 | // var dimName = filterItem.dimName; 1603 | // var o1 = filterItem.id; 1604 | var o2 = test[filterItem.dimName]; 1605 | if(typeof(o2) == "object" && o2 != null) { 1606 | o2 = o2.id; 1607 | } 1608 | //if(o1 != Number.POSITIVE_INFINITY) { 1609 | if(filterItem.id != o2) {// && o2 != -1) { 1610 | 1611 | return false; 1612 | } 1613 | //} 1614 | } 1615 | return true; 1616 | } 1617 | 1618 | /** 1619 | * This function check if the test value contain ONLY filter values (so it's not on sublevel- it's on the same level) 1620 | * @param test 1621 | * @param filter 1622 | * @returns {Boolean} 1623 | */ 1624 | function strictApplyFilter(opts, test, filter) { 1625 | var testFilter = {}; 1626 | for(var key in filter) { 1627 | var filterItem = filter[key]; 1628 | var dimName = filterItem.dimName; 1629 | var o1 = filterItem.id; 1630 | var o2 = test[dimName]; 1631 | if(o1 != o2) { 1632 | return false; 1633 | } 1634 | testFilter[dimName] = o1; 1635 | } 1636 | for(var key in opts.dimensions) { 1637 | if(testFilter[key] == undefined) { 1638 | if(test[key] != undefined) 1639 | return false; 1640 | } 1641 | } 1642 | return true; 1643 | } 1644 | 1645 | /** 1646 | * Calculate width of scroll bar 1647 | */ 1648 | function getScrollBarWidth () { 1649 | var inner = document.createElement('p'); 1650 | inner.style.width = "100%"; 1651 | inner.style.height = "200px"; 1652 | 1653 | var outer = document.createElement('div'); 1654 | outer.style.position = "absolute"; 1655 | outer.style.top = "0px"; 1656 | outer.style.left = "0px"; 1657 | outer.style.visibility = "hidden"; 1658 | outer.style.width = "200px"; 1659 | outer.style.height = "150px"; 1660 | outer.style.overflow = "hidden"; 1661 | outer.appendChild (inner); 1662 | 1663 | document.body.appendChild (outer); 1664 | var w1 = inner.offsetWidth; 1665 | outer.style.overflow = 'scroll'; 1666 | var w2 = inner.offsetWidth; 1667 | if (w1 == w2) 1668 | w2 = outer.clientWidth; 1669 | 1670 | document.body.removeChild (outer); 1671 | 1672 | return (w1 - w2); 1673 | } 1674 | 1675 | function doResizeWidth($table) { 1676 | var left = $table.find('.div22').offset().left; 1677 | $table.find('.div22').css({ 1678 | width:(window.innerWidth - left) + 'px', 1679 | }); 1680 | $table.find('.div12').css({ 1681 | width:(window.innerWidth - left ) + 'px', 1682 | }); 1683 | } 1684 | 1685 | function doResizeHeight($table) { 1686 | var top = $table.find('.div22').offset().top; 1687 | $table.find('.div22').css({ 1688 | height:(window.innerHeight - top) + 'px', 1689 | }); 1690 | $table.find('.div21').css({ 1691 | height:(window.innerHeight - top) + 'px', 1692 | }); 1693 | } 1694 | 1695 | function isNumber(o) { 1696 | return typeof o === 'number' && isFinite(o); 1697 | } 1698 | 1699 | // Checks if c2 is subcontext for c1 (all fields from c1 are equals to fields in c2) 1700 | function isSubContext(c1, c2) { 1701 | if(c2.length < c1.length) { 1702 | return false; 1703 | } 1704 | for(var i = 0; i < c1.length; i++) { 1705 | if(c1[i].id != c2[i].id) 1706 | return false; 1707 | } 1708 | return true; 1709 | } 1710 | 1711 | function hasAllDims(allDims, dims) { 1712 | dims = dims.split(";"); 1713 | for(var i = 0; i < dims.length; i++) { 1714 | var dimName = dims[i]; //.trim(); 1715 | var value = allDims[dimName]; 1716 | if(value == undefined) { 1717 | return false; 1718 | } 1719 | } 1720 | return true; 1721 | } 1722 | 1723 | })( jQuery ); -------------------------------------------------------------------------------- /js/jquery.cy-pivot.min.js: -------------------------------------------------------------------------------- 1 | (function(B){var C={init:function(W){var Y=B.extend({},B.fn.cypivot.defaults,W);var V=this;if(Y.resizable){B(window).resize(function(){if(Y.resizableWidth){d(V)}if(Y.resizableHeight){U(V)}})}var X=this.each(function(){var ad=B(this);ad.data("cypivot",{target:ad,options:Y});if(Y.cookiePrefix&&Y.storeDimConfig){var ac=B.cookie(Y.cookiePrefix+"horizontalDims");var aa=B.cookie(Y.cookiePrefix+"verticalDims");var ab=B.cookie(Y.cookiePrefix+"filterDims");if(ac&&m(Y.dimensions,ac)&&m(Y.dimensions,aa)){Y.horizontalDimensions=ac.split(";")}if(aa&&m(Y.dimensions,ac)&&m(Y.dimensions,aa)){Y.verticalDimensions=aa.split(";")}if(ab&&m(Y.dimensions,ab)&&m(Y.dimensions,ab)){Y.filterDimensions=ab.split(";")}}var Z='
    '+Y.configLabel+"
    ";if(!Y.configuration){Z=""}ad.html('
    ' + cellHtml + '
    '+Z+'
    ');ad.find("."+Y.div22Class).on("scroll",function(){M(ad,Y)});ad.find("."+Y.cell22Class).resize(function(){M(ad,Y)});ad.find("."+Y.div11Class).on("click",function(){h(ad,Y)});H(ad,Y);ad.find("#"+Y.pivotConfigDialogId).dialog({modal:false,title:"Pivot Table Configuration",position:["right","50px"],beforeClose:function(){var ae=ad.find("."+Y.div11Class);ae.removeClass(Y.configWindowActivatedClass)}});jQuery("#"+Y.pivotConfigDialogId).dialog("close");M(ad,Y);o(ad,[],[Y.verticalDimensions[0],Y.horizontalDimensions[0]],0,0,Y);w(ad,Y)});if(Y.resizable){if(Y.resizableWidth){d(V)}if(Y.resizableHeight){U(V)}}return X},options:function(W,V){var Y=B(this);var X=Y.data("cypivot");return X.options},reload:function(W,V){return this.each(function(){var ac=B(this),ab=ac.data("cypivot");if(W){ab.options.data=W}if(V){var X=ac.find("."+ab.options.div22Class+" ."+ab.options.pivotDataTableClass);var Z=X[0];var ad;for(var Y=0;Y'+W.verticalDimensionListTitle+"";for(var ab=0;ab'+ac.label+""}if(!W.configuration||!W.configuration.verticalDimensions){ai=""}var ad='
  • '+W.horizontalDimensionListTitle+"
  • ";for(var ab=0;ab'+ac.label+""}if(!W.configuration||!W.configuration.horizontalDimensions){var ad=""}var V='
  • '+W.filterDimensionListTitle+"
  • ";for(var ab=0;ab"+ae.label+""}Z=Z+"";V=V+'
  • '+ac.label+Z+"
  • "}if(!W.configuration||!W.configuration.filterDimensions){V=""}var aa="";for(var ah in W.dimensions){if(ag[ah]!=true){var ac=W.dimensions[ah];aa=aa+'
  • '+ac.label+"
  • "}}X.append('
    • '+W.dimensionListTitle+'
      '+aa+ad+ai+V+"
    ");X.find("ul."+W.dimensionListClass).sortable({cancel:"."+W.dimensionListTitleClass,update:function(){var an=[],ap=[],ao=[],al=[];X.find("."+W.dimensionListClass+" li").each(function(){var ar=jQuery(this).attr("dim");if(ar){al[al.length]=ar}if(jQuery(this).attr("vdims")=="true"){al=an}if(jQuery(this).attr("hdims")=="true"){al=ap}if(jQuery(this).attr("fdims")=="true"){al=ao}});if(W.configuration&&W.configuration.verticalDimensions==true){W.verticalDimensions=an}if(W.configuration&&W.configuration.horizontalDimensions==true){W.horizontalDimensions=ap}if(W.configuration&&W.configuration.filterDimensions==true){W.filterDimensions=ao}if(W.cookiePrefix&&W.storeDimConfig){var aq=W.horizontalDimensions.join(";");var ak=W.verticalDimensions.join(";");var am=W.filterDimensions.join(";");B.cookie(W.cookiePrefix+"horizontalDims",aq,{expires:15});B.cookie(W.cookiePrefix+"verticalDims",ak,{expires:15});B.cookie(W.cookiePrefix+"filterDims",am,{expires:15})}f(aj,W)}})}function h(W,X){if(X.configuration){var V=W.find("."+X.div11Class);V.toggleClass(X.configWindowActivatedClass);if(V.hasClass(X.configWindowActivatedClass)){jQuery("#"+X.pivotConfigDialogId).dialog("open")}else{var Y=jQuery("#"+X.pivotConfigDialogId);Y.dialog("close")}}}function b(V,X){var Z;if(V.hasClass(X.levelClassPrefix+"0")){Z=V.parent()}else{Z=V.parents("."+X.levelClassPrefix+"0").parent()}var W=0;var Y=false;Z.find("."+X.dimLabelClass).each(function(){if(!Y){var aa=V[0];if(this==aa){Y=true;return false}else{var ab=B(this);if(ab.parent().children("."+X.dimCellClass).size()==0){W++}}}});return W}function P(V,X){var Z;if(V.hasClass(X.levelClassPrefix+"0")){Z=V.parent()}else{Z=V.parents("."+X.levelClassPrefix+"0").parent()}var W=0;var Y=false;Z.find("."+X.dimLabelClass).each(function(){if(!Y){var aa=V[0];if(this==aa){Y=true;return false}else{var ab=B(this);W++}}});return W}function c(V,W){var X=[];V.find("."+W.div21Class+" ."+W.dimCellClass).each(function(){X[X.length]=this.pivotContext});return X}function L(V,W){var X=[];V.find("."+W.div12Class+" ."+W.dimCellClass).not("."+W.expandedDimCellClass).each(function(){X[X.length]=this.pivotContext});return X}function p(V,Y){var Z=V.parent();var X=0;var W=V[0];Z.find("."+Y.dimLabelClass).each(function(){var aa=B(this);if(aa.parent().children("."+Y.dimCellClass).size()==0){X++}});return X}function I(V,Y){var Z=V.parent();var X=0;var W=V[0];X=Z.find("."+Y.dimLabelClass).not(W).size();return X}function f(X,Y){var V=X.find("."+Y.div21Class);V.html("");var Z=X.find("."+Y.div12Class);Z.html("");var W=X.find("."+Y.div22Class);W.html('');o(X,[],[Y.verticalDimensions[0],Y.horizontalDimensions[0]],0,0,Y)}function F(V,X,W){$pivotDataTable=V.find("."+X.div22Class+" ."+X.pivotDataTableClass);$pivotDataTable.html("");i(V,$pivotDataTable,X,[],W);A(V,X);n(V,X)}function w(V,W){V.on("click","."+W.div12Class+" ."+W.dimLabelClass,function(aj){var ae=V.find("."+W.div22Class+" ."+W.pivotDataTableClass);var ac=new Date();var ab,an;var aa=B(this);var ai=false;if(aa.hasClass(W.expandedDimLabelClass)){var al=p(aa,W);if(al>0){var ao=b(aa,W);var am=ae[0];var Y=N(am,ao,al-1);k(am,ao,al-1,function(){aa.parent().find("."+W.dimCellClass).remove();aa.width(Y+"px");var aq=-1;for(var ar=0;ar0){aa.addClass(W.expandedDimLabelClass).removeClass(W.collapsedDimLabelClass);aa.parent().addClass(W.expandedDimCellClass).removeClass(W.collapsedDimCellClass);if(aq>0){R(ae[0],W,af,aq-1,ar)}ai=true}n(V,W);ab=W.onExpand};var X=a(ah,W.verticalDimensions);var ak=q(V,W);var Z=G(V,W);if(ah.length==Z){ak.push(W.verticalDimensions[ah.length])}var ap=W.dataProvider(W,X,ak,ad);if(ap){ad(ap)}}}if(ai){A(V,W);if(W.resizable){if(W.resizableWidth){d(V)}if(W.resizableHeight){U(V)}}if(an){an(this)}if(ab){ab(this)}n(V,W)}return false});V.on("click","."+W.div21Class+" ."+W.dimLabelClass,function(aj){var ae=new Date();var ac,an;if(aj.isPropagationStopped()){return}var ab=B(this);var ag=ab.parent();var ai=false;if(ab.hasClass(W.expandedDimLabelClass)){var al=I(ab,W);var X=P(ab,W);var am=$pivotDataTable[0];if(al>0){x(am,X+1,al);ag.find("."+W.dimCellClass).remove();ab.removeClass(W.expandedDimLabelClass);ab.addClass(W.collapsedDimLabelClass);ag.removeClass(W.expandedDimCellClass);ag.addClass(W.collapsedDimCellClass);ai=true}an=W.onCollapse}else{if(ab.hasClass(W.collapsedDimLabelClass)){var ah=this.parentElement.pivotContext;var Y=P(ab,W);var af=function(aq){J(ag,W,W.horizontalDimensions,null,aq);ab.addClass(W.expandedDimLabelClass).removeClass(W.collapsedDimLabelClass);ag.addClass(W.expandedDimCellClass).removeClass(W.collapsedDimCellClass);var ap=I(ab,W);if(ap>0){K($pivotDataTable[0],W,Y+1,ap,aq);ai=true}n(V,W);ac=W.onExpand};var Z=a(ah,W.horizontalDimensions);var ak=q(V,W);var aa=g(V,W);if(ah.length==aa){ak.push(W.horizontalDimensions[ah.length])}var ao=W.dataProvider(W,Z,ak,af);if(ao){af(ao)}ac=W.onExpand}}if(ai){if(W.resizable){if(W.resizableWidth){d(V)}if(W.resizableHeight){U(V)}}A(V,W);if(an){an(this)}if(ac){ac(this)}n(V,W)}var ad=new Date();return false})}function a(Y,X){var W={};for(var V=0;V0){var am=B("#cellTemplate").html();ad=_.template(am,{items:ab,rowContext:Y,colContext:af,options:ah})}}jQuery(V).html('
    '+ad+"
    ");V.isCalculated=true}}}}function j(W){var X="[";for(var V=0;V0){X=X+", "}X=X+Y.label}X=X+"]";return X}function R(ak,ag,al,am,W){var ad=B(ak);var aj=c(ad.parents(".cypivot"),ag);var aa=L(ad.parents(".cypivot"),ag);for(var Y=0;Y0?al.rows[0]:null;for(var X=0;X
    '+am+"
    ")}}jQuery(aa).html(ae.join(""))}}function k(Z,ac,aa,Y){for(var X=0;X
    Total '+aj.label+"
    ")}}}ae.css({height:"10px"});ae.css({height:"auto"});n(ae,V)};var ad;if(aa.values){ad=aa.values(X,W)}else{ad=y(ac,X,Z,aa.sortFieldName)}if(ad!=undefined){W(ad)}}}function y(ad,W,Y,V){var ab=[];for(var Z=0;Zah[V]){return 1}return 0})}return aa}function v(ac,X){for(var W in X){var aa=X[W];var ab=aa.dimName;var Z=aa.id;var V=ac[ab];if(V!=undefined){var Y;if(typeof V==="number"){Y=V}else{if(typeof V==="string"){Y=V}else{Y=V.id}}if(Y!=null&&Y!=undefined){if(Z!=Y&&Y!=Number.POSITIVE_INFINITY){return false}}}}return true}function t(ac,X){for(var W in X){var aa=X[W];var ab=aa.dimName;var Z=aa.id;var V=ac[ab];if(V!=undefined){var Y=V.id;if(Y!=null&&Y!=undefined){if(Z!=Y&&Y!=-1){return false}}}}return true}function D(ae,ah,ak,ai,Y,ag,V){var ad=ai.verticalDimensions[ag.length];var Z=s(ag);var al=ai.dimensions[ad];var am=Y[Y.length-1];var ab=ak[0];if(al){var X=al.values?al.values(ag):y(V,ag,ad,al.sortFieldName);for(var ac=0;acTotal '+aj.label+"");W.rowContext=Y;W.colContext=ag}}else{var W=ab.insertCell(ab.cells.length);var aa=jQuery(W);aa.addClass(ai.dataCellClass+" "+ai.levelClassPrefix+ag.length+" "+ai.pivotIdClassPrefix+aj.id);aa.attr("pivotId",aj.id);var af=typeof(am)=="object"?am.label:am;aa.html('
    '+af+" - "+af+"
    ")}}}}function J(am,ah,ac,Z,V){var ao;if(!Z){Z=am[0].pivotContext}else{am[0].pivotContext=s(Z)}var ag=ac[Z.length];var aa=s(Z);var ak=ah.dimensions[ag];var Y=am.find("."+ah.dimTotalCellClass);if(ak){var X=ak.values?ak.values(Z):y(V,Z,ag,ak.sortFieldName);ao=X;if(X.length>0){am.children("."+ah.dimLabelClass).css({width:""})}for(var ae=0;ae0){al=am[0].insertBefore(al,Y[0])}else{al=am[0].appendChild(al)}var W=jQuery(al);W.addClass(ah.dimCellClass+" "+ah.dimCellClass+"-"+ag+" "+ah.levelClassPrefix+Z.length+" "+ah.pivotIdClassPrefix+af);W.attr({pivotId:af,dimName:ag,title:ak.label+" : "+unescape(ad)});var an=ah.dimensionCellRenderer?ah.dimensionCellRenderer(ah,aa,false):""+ad+"";W.append('
    '+an+"
    ");var ai=W.children("."+ah.dimLabelClass);al.pivotContext=s(aa);if(ah.isExpanded(aa)&&aa.length0&&ac==ah.verticalDimensions){var al=document.createElement("div");al=am[0].appendChild(al);var W=jQuery(al);W.addClass(ah.dimCellClass);W.addClass(ah.dimTotalCellClass);W.addClass(ah.levelClassPrefix+Z.length);W.attr("dimName",ag);al.pivotContext=s(Z);var an=ah.dimensionCellRenderer?ah.dimensionCellRenderer(ah,aa,true):"";W.append('
    Total
    ')}}return ao}function M(X,Y){var Z=u();var W=X.find("."+Y.div22Class);if(!Y.autoSize){X.find("."+Y.div12Class).css({width:(W.width()-Z)+"px"})}var V=W.height()-Z;if(!Y.autoSize){X.find("."+Y.div21Class).css({height:V+"px","margin-bottom":Z+"px"})}X.find("."+Y.div12Class).scrollLeft(W.scrollLeft());X.find("."+Y.div21Class).scrollTop(W.scrollTop())}B.fn.cypivot.defaults={syncDimensionCellSizes:true,resizable:true,resizableWidth:true,resizableHeight:true,autoSize:false,tableClass:"cypivot",topRowClass:"top-row",bottomRowClass:"bottom-row",cell11Class:"cell11",cell12Class:"cell12",cell21Class:"cell21",cell22Class:"cell22",div11Class:"div11",div12Class:"div12",div21Class:"div21",div22Class:"div22",pivotDataTableClass:"pivot-data-table",dimLabelClass:"dim-label",dimCellIconClass:"dim-cell-icon",dimCellClass:"dim-cell",expandableDimLabelClass:"expandable-dim-label",expandedDimLabelClass:"expanded-dim-label",collapsedDimLabelClass:"collapsed-dim-label",expandedDimCellClass:"expanded-dim-cell",collapsedDimCellClass:"collapsed-dim-cell",dimTotalCellClass:"dim-total-cell",dataTotalCellClass:"data-total-cell",levelClassPrefix:"level-",dataRowClass:"data-row",dataCellClass:"data-cell",divDataCellClass:"div-data-cell",configDialogClass:"config-dialog",dimensionListTitle:"Dimensions",horizontalDimensionListTitle:"Rows",verticalDimensionListTitle:"Columns",filterDimensionListTitle:"Filters",configWindowActivatedClass:"config-window-activated",dimensionListClass:"dimension-list",firstDimensionListClass:"first-dimension-list",dimensionListTitleClass:"dimension-list-title",verticalDimensionListClass:"vertical-dimension-list",horizontalDimensionListClass:"horizontal-dimension-list",filterDimensionListClass:"filter-dimension-list",pivotIdClassPrefix:"pivot-id-",pivotConfigDialogId:"pivot-config-dialog",isExpanded:function(V){return V.length==0},configuration:{horizontalDimensions:true,verticalDimensions:true,filterDimensions:false},configLabel:"Config",cookiePrefix:"cy-pivot-",storeDimConfig:true,dataCellRenderer:function(V,ab,Z,Y){var aa="";for(var W=0;W"+V["default"].sum[X]+" "}return aa},dataProvider:function(X,W,V){return X.data},dimensionCellRenderer:null,dimensions:{dim1:{label:"Colors",values:function(V){return[{id:1,label:"Red"},{id:2,label:"Blue"},{id:3,label:"Green"}]},showTotal:true},dim2:{label:"Cities",values:function(V){return[{id:1,label:"Saint-Petersburg"},{id:2,label:"Moscow"},{id:3,label:"London"},{id:4,label:"Paris"}]},showTotal:true},dim3:{label:"Shapes",values:function(V){return[{id:1,label:"Round"},{id:2,label:"Square"},{id:3,label:"Star"},{id:4,label:"Ellipse"}]},showTotal:true},dim4:{label:"Planets",values:function(V){return[{id:1,label:"Earth"},{id:2,label:"Moon"},{id:3,label:"Mars"},{id:4,label:"Neptune"}]},showTotal:true},dim5:{label:"Geo",values:function(V){return[{id:1,label:"Asia"},{id:2,label:"Africa"},{id:3,label:"Europe"},{id:4,label:"America"}]},showTotal:true},dim6:{label:"Lessons",values:function(V){return[{id:1,label:"Phisics"},{id:2,label:"Chemistry"},{id:3,label:"Math"},{id:4,label:"Geography"}]},showTotal:true}},horizontalDimensions:["dim1","dim3","dim5"],verticalDimensions:["dim2"],filterDimensions:[],data:[{dim1:"Red",dim2:"Saint-Petersburg",dim3:"Round",dim4:"Earth",dim5:"Asia",dim6:"Phisics",value:1},{dim1:"Red",dim2:"Saint-Petersburg",dim3:"Round",dim4:"Earth",dim5:"Asia",dim6:"Chemistry",value:1},{dim1:"Blue",dim2:"Saint-Petersburg",dim3:"Round",dim4:"Earth",dim5:"Asia",dim6:"Chemistry",value:1},{dim1:"Green",dim2:"Moscow",dim3:"Round",dim4:"Earth",dim5:"Asia",dim6:"Chemistry",value:1}],map:function(W,V,Y){var aa=[];var ac=[];var X=[];for(var Z=0;ZX){ag=X}if(Wad[ac]){W[ac]=ad[ac]}}else{W[ac]=ad[ac]}if(ab[ac]){if(ab[ac]>ad[ac]){ab[ac]=ad[ac]}}else{ab[ac]=ad[ac]}}}if(ae){ae.sum=Z;ae.count=aa.length;ae.avg=Z/aa.length;ae.max=ab;ae.min=W}return Z}}]};function s(X){if(!X||"object"!==typeof X){return X}var Y="function"===typeof X.pop?[]:{};var W,V;for(W in X){if(X.hasOwnProperty(W)){V=X[W];if(V&&"object"===typeof V){Y[W]=s(V)}else{Y[W]=V}}}return Y}function l(Z,W){for(var V in W){var Y=W[V];var X=Z[Y.dimName];if(Y.id!=X){return false}}return true}function Q(Z,W){for(var V=0;V