/*jshint multistr:true */ this.recline = this.recline || {}; this.recline.View = this.recline.View || {}; (function($, my) { // ## SlickGrid Dataset View // // Provides a tabular view on a Dataset, based on SlickGrid. // // https://github.com/mleibman/SlickGrid // // Initialize it with a `recline.Model.Dataset`. my.SlickGrid = Backbone.View.extend({ tagName: "div", className: "recline-slickgrid", initialize: function(modelEtc) { var self = this; this.el = $(this.el); _.bindAll(this, 'render'); this.model.currentDocuments.bind('add', this.render); this.model.currentDocuments.bind('reset', this.render); this.model.currentDocuments.bind('remove', this.render); var state = _.extend({ hiddenColumns: [], columnsOrder: [], columnsSort: {}, columnsWidth: [], fitColumns: false }, modelEtc.state ); this.state = new recline.Model.ObjectState(state); this.bind('view:show',function(){ // If the div is hidden, SlickGrid will calculate wrongly some // sizes so we must render it explicitly when the view is visible if (!self.rendered){ if (!self.grid){ self.render(); } self.grid.init(); self.rendered = true; } self.visible = true; }); this.bind('view:hide',function(){ self.visible = false; }); }, events: { }, render: function() { var self = this; this.el = $(this.el); var options = { enableCellNavigation: true, enableColumnReorder: true, explicitInitialization: true, syncColumnCellResize: true, forceFitColumns: this.state.get('fitColumns') }; // We need all columns, even the hidden ones, to show on the column picker var columns = []; _.each(this.model.fields.toJSON(),function(field){ var column = {id:field['id'], name:field['label'], field:field['id'], sortable: true, minWidth: 80}; var widthInfo = _.find(self.state.get('columnsWidth'),function(c){return c.column == field.id}); if (widthInfo){ column['width'] = widthInfo.width; } columns.push(column); }); // Restrict the visible columns var visibleColumns = columns.filter(function(column) { return _.indexOf(self.state.get('hiddenColumns'), column.id) == -1; }); // Order them if there is ordering info on the state if (this.state.get('columnsOrder')){ visibleColumns = visibleColumns.sort(function(a,b){ return _.indexOf(self.state.get('columnsOrder'),a.id) > _.indexOf(self.state.get('columnsOrder'),b.id); }); columns = columns.sort(function(a,b){ return _.indexOf(self.state.get('columnsOrder'),a.id) > _.indexOf(self.state.get('columnsOrder'),b.id); }); } // Move hidden columns to the end, so they appear at the bottom of the // column picker var tempHiddenColumns = []; for (var i = columns.length -1; i >= 0; i--){ if (_.indexOf(_.pluck(visibleColumns,'id'),columns[i].id) == -1){ tempHiddenColumns.push(columns.splice(i,1)[0]); } } columns = columns.concat(tempHiddenColumns); var data = this.model.currentDocuments.toJSON(); this.grid = new Slick.Grid(this.el, data, visibleColumns, options); // Column sorting var gridSorter = function(field, ascending, grid, data){ data.sort(function(a, b){ var result = a[field] > b[field] ? 1 : a[field] < b[field] ? -1 : 0 ; return ascending ? result : -result; }); grid.setData(data); grid.updateRowCount(); grid.render(); } var sortInfo = this.state.get('columnsSort'); if (sortInfo){ var sortAsc = !(sortInfo['direction'] == 'desc'); gridSorter(sortInfo.column, sortAsc, self.grid, data); this.grid.setSortColumn(sortInfo.column, sortAsc); } this.grid.onSort.subscribe(function(e, args){ gridSorter(args.sortCol.field,args.sortAsc,self.grid,data); self.state.set({columnsSort:{ column:args.sortCol, direction: (args.sortAsc) ? 'asc':'desc' }}); }); this.grid.onColumnsReordered.subscribe(function(e, args){ self.state.set({columnsOrder: _.pluck(self.grid.getColumns(),'id')}); }); this.grid.onColumnsResized.subscribe(function(e, args){ var columns = args.grid.getColumns(); var defaultColumnWidth = args.grid.getOptions().defaultColumnWidth; var columnsWidth = []; _.each(columns,function(column){ if (column.width != defaultColumnWidth){ columnsWidth.push({column:column.id,width:column.width}); } }); self.state.set({columnsWidth:columnsWidth}); }); var columnpicker = new Slick.Controls.ColumnPicker(columns, this.grid, _.extend(options,{state:this.state})); if (self.visible){ self.grid.init(); self.rendered = true; } else { // Defer rendering until the view is visible self.rendered = false; } return this; } }); })(jQuery, recline.View); /* * Context menu for the column picker, adapted from * http://mleibman.github.com/SlickGrid/examples/example-grouping * */ (function ($) { function SlickColumnPicker(columns, grid, options) { var $menu; var columnCheckboxes; var defaults = { fadeSpeed:250 }; function init() { grid.onHeaderContextMenu.subscribe(handleHeaderContextMenu); options = $.extend({}, defaults, options); $menu = $('
').appendTo(document.body); $menu.bind('mouseleave', function (e) { $(this).fadeOut(options.fadeSpeed) }); $menu.bind('click', updateColumn); } function handleHeaderContextMenu(e, args) { e.preventDefault(); $menu.empty(); columnCheckboxes = []; var $li, $input; for (var i = 0; i < columns.length; i++) { $li = $('').appendTo($menu); $input = $('').data('column-id', columns[i].id).attr('id','slick-column-vis-'+columns[i].id); columnCheckboxes.push($input); if (grid.getColumnIndex(columns[i].id) != null) { $input.attr('checked', 'checked'); } $input.appendTo($li); $('') .text(columns[i].name) .attr('for','slick-column-vis-'+columns[i].id) .appendTo($li); } $('').addClass('divider').appendTo($menu); $li = $('').data('option', 'autoresize').appendTo($menu); $input = $('').data('option', 'autoresize').attr('id','slick-option-autoresize'); $input.appendTo($li); $('') .text('Force fit columns') .attr('for','slick-option-autoresize') .appendTo($li); if (grid.getOptions().forceFitColumns) { $input.attr('checked', 'checked'); } $menu.css('top', e.pageY - 10) .css('left', e.pageX - 10) .fadeIn(options.fadeSpeed); } function updateColumn(e) { if ($(e.target).data('option') == 'autoresize') { var checked; if ($(e.target).is('li')){ var checkbox = $(e.target).find('input').first(); checked = !checkbox.is(':checked'); checkbox.attr('checked',checked); } else { checked = e.target.checked; } if (checked) { grid.setOptions({forceFitColumns:true}); grid.autosizeColumns(); } else { grid.setOptions({forceFitColumns:false}); } options.state.set({fitColumns:checked}); return; } if (($(e.target).is('li') && !$(e.target).hasClass('divider')) || $(e.target).is('input')) { if ($(e.target).is('li')){ var checkbox = $(e.target).find('input').first(); checkbox.attr('checked',!checkbox.is(':checked')); } var visibleColumns = []; var hiddenColumnsIds = []; $.each(columnCheckboxes, function (i, e) { if ($(this).is(':checked')) { visibleColumns.push(columns[i]); } else { hiddenColumnsIds.push(columns[i].id); } }); if (!visibleColumns.length) { $(e.target).attr('checked', 'checked'); return; } grid.setColumns(visibleColumns); options.state.set({hiddenColumns:hiddenColumnsIds}); } } init(); } // Slick.Controls.ColumnPicker $.extend(true, window, { Slick:{ Controls:{ ColumnPicker:SlickColumnPicker }}}); })(jQuery);