diff --git a/dist/lib/cellNavigationService.d.ts b/dist/lib/cellNavigationService.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..df3f13dac7894ef62547554e8877da398206591a
--- /dev/null
+++ b/dist/lib/cellNavigationService.d.ts
@@ -0,0 +1,23 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { GridRow } from "./entities/gridRow";
+import { GridCell } from "./entities/gridCell";
+export declare class CellNavigationService {
+    private columnController;
+    private rowModel;
+    private floatingRowModel;
+    getNextCellToFocus(key: any, lastCellToFocus: GridCell): GridCell;
+    private getCellToLeft(lastCell);
+    private getCellToRight(lastCell);
+    getRowBelow(lastRow: GridRow): GridRow;
+    private getCellBelow(lastCell);
+    private isLastRowInContainer(gridRow);
+    private getRowAbove(lastRow);
+    private getCellAbove(lastCell);
+    private getLastBodyCell();
+    private getLastFloatingTopRow();
+    getNextTabbedCellForwards(gridCell: GridCell): GridCell;
+    getNextTabbedCellBackwards(gridCell: GridCell): GridCell;
+}
diff --git a/dist/lib/cellNavigationService.js b/dist/lib/cellNavigationService.js
new file mode 100644
index 0000000000000000000000000000000000000000..4bc44ff1af697f3b2152d6b35f42812179f7950e
--- /dev/null
+++ b/dist/lib/cellNavigationService.js
@@ -0,0 +1,210 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var context_1 = require("./context/context");
+var constants_1 = require("./constants");
+var context_2 = require("./context/context");
+var columnController_1 = require("./columnController/columnController");
+var floatingRowModel_1 = require("./rowControllers/floatingRowModel");
+var utils_1 = require('./utils');
+var gridRow_1 = require("./entities/gridRow");
+var gridCell_1 = require("./entities/gridCell");
+var CellNavigationService = (function () {
+    function CellNavigationService() {
+    }
+    CellNavigationService.prototype.getNextCellToFocus = function (key, lastCellToFocus) {
+        switch (key) {
+            case constants_1.Constants.KEY_UP: return this.getCellAbove(lastCellToFocus);
+            case constants_1.Constants.KEY_DOWN: return this.getCellBelow(lastCellToFocus);
+            case constants_1.Constants.KEY_RIGHT: return this.getCellToRight(lastCellToFocus);
+            case constants_1.Constants.KEY_LEFT: return this.getCellToLeft(lastCellToFocus);
+            default: console.log('ag-Grid: unknown key for navigation ' + key);
+        }
+    };
+    CellNavigationService.prototype.getCellToLeft = function (lastCell) {
+        var colToLeft = this.columnController.getDisplayedColBefore(lastCell.column);
+        if (!colToLeft) {
+            return null;
+        }
+        else {
+            return new gridCell_1.GridCell(lastCell.rowIndex, lastCell.floating, colToLeft);
+        }
+    };
+    CellNavigationService.prototype.getCellToRight = function (lastCell) {
+        var colToRight = this.columnController.getDisplayedColAfter(lastCell.column);
+        // if already on right, do nothing
+        if (!colToRight) {
+            return null;
+        }
+        else {
+            return new gridCell_1.GridCell(lastCell.rowIndex, lastCell.floating, colToRight);
+        }
+    };
+    CellNavigationService.prototype.getRowBelow = function (lastRow) {
+        // if already on top row, do nothing
+        if (this.isLastRowInContainer(lastRow)) {
+            if (lastRow.isFloatingBottom()) {
+                return null;
+            }
+            else if (lastRow.isNotFloating()) {
+                if (this.floatingRowModel.isRowsToRender(constants_1.Constants.FLOATING_BOTTOM)) {
+                    return new gridRow_1.GridRow(0, constants_1.Constants.FLOATING_BOTTOM);
+                }
+                else {
+                    return null;
+                }
+            }
+            else {
+                if (this.rowModel.isRowsToRender()) {
+                    return new gridRow_1.GridRow(0, null);
+                }
+                else if (this.floatingRowModel.isRowsToRender(constants_1.Constants.FLOATING_BOTTOM)) {
+                    return new gridRow_1.GridRow(0, constants_1.Constants.FLOATING_BOTTOM);
+                }
+                else {
+                    return null;
+                }
+            }
+        }
+        else {
+            return new gridRow_1.GridRow(lastRow.rowIndex + 1, lastRow.floating);
+        }
+    };
+    CellNavigationService.prototype.getCellBelow = function (lastCell) {
+        var rowBelow = this.getRowBelow(lastCell.getGridRow());
+        if (rowBelow) {
+            return new gridCell_1.GridCell(rowBelow.rowIndex, rowBelow.floating, lastCell.column);
+        }
+        else {
+            return null;
+        }
+    };
+    CellNavigationService.prototype.isLastRowInContainer = function (gridRow) {
+        if (gridRow.isFloatingTop()) {
+            var lastTopIndex = this.floatingRowModel.getFloatingTopRowData().length - 1;
+            return lastTopIndex === gridRow.rowIndex;
+        }
+        else if (gridRow.isFloatingBottom()) {
+            var lastBottomIndex = this.floatingRowModel.getFloatingBottomRowData().length - 1;
+            return lastBottomIndex === gridRow.rowIndex;
+        }
+        else {
+            var lastBodyIndex = this.rowModel.getRowCount() - 1;
+            return lastBodyIndex === gridRow.rowIndex;
+        }
+    };
+    CellNavigationService.prototype.getRowAbove = function (lastRow) {
+        // if already on top row, do nothing
+        if (lastRow.rowIndex === 0) {
+            if (lastRow.isFloatingTop()) {
+                return null;
+            }
+            else if (lastRow.isNotFloating()) {
+                if (this.floatingRowModel.isRowsToRender(constants_1.Constants.FLOATING_TOP)) {
+                    return this.getLastFloatingTopRow();
+                }
+                else {
+                    return null;
+                }
+            }
+            else {
+                // last floating bottom
+                if (this.rowModel.isRowsToRender()) {
+                    return this.getLastBodyCell();
+                }
+                else if (this.floatingRowModel.isRowsToRender(constants_1.Constants.FLOATING_TOP)) {
+                    return this.getLastFloatingTopRow();
+                }
+                else {
+                    return null;
+                }
+            }
+        }
+        else {
+            return new gridRow_1.GridRow(lastRow.rowIndex - 1, lastRow.floating);
+        }
+    };
+    CellNavigationService.prototype.getCellAbove = function (lastCell) {
+        var rowAbove = this.getRowAbove(lastCell.getGridRow());
+        if (rowAbove) {
+            return new gridCell_1.GridCell(rowAbove.rowIndex, rowAbove.floating, lastCell.column);
+        }
+        else {
+            return null;
+        }
+    };
+    CellNavigationService.prototype.getLastBodyCell = function () {
+        var lastBodyRow = this.rowModel.getRowCount() - 1;
+        return new gridRow_1.GridRow(lastBodyRow, null);
+    };
+    CellNavigationService.prototype.getLastFloatingTopRow = function () {
+        var lastFloatingRow = this.floatingRowModel.getFloatingTopRowData().length - 1;
+        return new gridRow_1.GridRow(lastFloatingRow, constants_1.Constants.FLOATING_TOP);
+    };
+    CellNavigationService.prototype.getNextTabbedCellForwards = function (gridCell) {
+        var displayedColumns = this.columnController.getAllDisplayedColumns();
+        var newRowIndex = gridCell.rowIndex;
+        var newFloating = gridCell.floating;
+        // move along to the next cell
+        var newColumn = this.columnController.getDisplayedColAfter(gridCell.column);
+        // check if end of the row, and if so, go forward a row
+        if (!newColumn) {
+            newColumn = displayedColumns[0];
+            var rowBelow = this.getRowBelow(gridCell.getGridRow());
+            if (utils_1.Utils.missing(rowBelow)) {
+                return;
+            }
+            newRowIndex = rowBelow.rowIndex;
+            newFloating = rowBelow.floating;
+        }
+        return new gridCell_1.GridCell(newRowIndex, newFloating, newColumn);
+    };
+    CellNavigationService.prototype.getNextTabbedCellBackwards = function (gridCell) {
+        var displayedColumns = this.columnController.getAllDisplayedColumns();
+        var newRowIndex = gridCell.rowIndex;
+        var newFloating = gridCell.floating;
+        // move along to the next cell
+        var newColumn = this.columnController.getDisplayedColBefore(gridCell.column);
+        // check if end of the row, and if so, go forward a row
+        if (!newColumn) {
+            newColumn = displayedColumns[displayedColumns.length - 1];
+            var rowAbove = this.getRowAbove(gridCell.getGridRow());
+            if (utils_1.Utils.missing(rowAbove)) {
+                return;
+            }
+            newRowIndex = rowAbove.rowIndex;
+            newFloating = rowAbove.floating;
+        }
+        return new gridCell_1.GridCell(newRowIndex, newFloating, newColumn);
+    };
+    __decorate([
+        context_2.Autowired('columnController'), 
+        __metadata('design:type', columnController_1.ColumnController)
+    ], CellNavigationService.prototype, "columnController", void 0);
+    __decorate([
+        context_2.Autowired('rowModel'), 
+        __metadata('design:type', Object)
+    ], CellNavigationService.prototype, "rowModel", void 0);
+    __decorate([
+        context_2.Autowired('floatingRowModel'), 
+        __metadata('design:type', floatingRowModel_1.FloatingRowModel)
+    ], CellNavigationService.prototype, "floatingRowModel", void 0);
+    CellNavigationService = __decorate([
+        context_1.Bean('cellNavigationService'), 
+        __metadata('design:paramtypes', [])
+    ], CellNavigationService);
+    return CellNavigationService;
+})();
+exports.CellNavigationService = CellNavigationService;
diff --git a/dist/lib/clientExports.d.ts b/dist/lib/clientExports.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9837489066d8795c3efdf4643ec3031fb24e986e
--- /dev/null
+++ b/dist/lib/clientExports.d.ts
@@ -0,0 +1,5 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+export declare function populateClientExports(exports: any): void;
diff --git a/dist/lib/clientExports.js b/dist/lib/clientExports.js
new file mode 100644
index 0000000000000000000000000000000000000000..68c16dd43c71e6dc8cbb91a7cb65da57d21bef1c
--- /dev/null
+++ b/dist/lib/clientExports.js
@@ -0,0 +1,179 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var grid_1 = require("./grid");
+var gridApi_1 = require("./gridApi");
+var events_1 = require("./events");
+var componentUtil_1 = require("./components/componentUtil");
+var columnController_1 = require("./columnController/columnController");
+var agGridNg1_1 = require("./components/agGridNg1");
+var agGridWebComponent_1 = require("./components/agGridWebComponent");
+var gridCell_1 = require("./entities/gridCell");
+var rowNode_1 = require("./entities/rowNode");
+var originalColumnGroup_1 = require("./entities/originalColumnGroup");
+var columnGroup_1 = require("./entities/columnGroup");
+var column_1 = require("./entities/column");
+var focusedCellController_1 = require("./focusedCellController");
+var functions_1 = require("./functions");
+var gridOptionsWrapper_1 = require("./gridOptionsWrapper");
+var groupCellRendererFactory_1 = require("./cellRenderers/groupCellRendererFactory");
+var balancedColumnTreeBuilder_1 = require("./columnController/balancedColumnTreeBuilder");
+var columnKeyCreator_1 = require("./columnController/columnKeyCreator");
+var columnUtils_1 = require("./columnController/columnUtils");
+var displayedGroupCreator_1 = require("./columnController/displayedGroupCreator");
+var groupInstanceIdCreator_1 = require("./columnController/groupInstanceIdCreator");
+var context_1 = require("./context/context");
+var dragAndDropService_1 = require("./dragAndDrop/dragAndDropService");
+var dragService_1 = require("./dragAndDrop/dragService");
+var filterManager_1 = require("./filter/filterManager");
+var numberFilter_1 = require("./filter/numberFilter");
+var textFilter_1 = require("./filter/textFilter");
+var gridPanel_1 = require("./gridPanel/gridPanel");
+var mouseEventService_1 = require("./gridPanel/mouseEventService");
+var cssClassApplier_1 = require("./headerRendering/cssClassApplier");
+var headerContainer_1 = require("./headerRendering/headerContainer");
+var headerRenderer_1 = require("./headerRendering/headerRenderer");
+var headerTemplateLoader_1 = require("./headerRendering/headerTemplateLoader");
+var horizontalDragService_1 = require("./headerRendering/horizontalDragService");
+var moveColumnController_1 = require("./headerRendering/moveColumnController");
+var renderedHeaderCell_1 = require("./headerRendering/renderedHeaderCell");
+var renderedHeaderGroupCell_1 = require("./headerRendering/renderedHeaderGroupCell");
+var standardMenu_1 = require("./headerRendering/standardMenu");
+var borderLayout_1 = require("./layout/borderLayout");
+var tabbedLayout_1 = require("./layout/tabbedLayout");
+var verticalStack_1 = require("./layout/verticalStack");
+var autoWidthCalculator_1 = require("./rendering/autoWidthCalculator");
+var renderedRow_1 = require("./rendering/renderedRow");
+var rowRenderer_1 = require("./rendering/rowRenderer");
+var fillterStage_1 = require("./rowControllers/inMemory/fillterStage");
+var flattenStage_1 = require("./rowControllers/inMemory/flattenStage");
+var inMemoryRowController_1 = require("./rowControllers/inMemory/inMemoryRowController");
+var sortStage_1 = require("./rowControllers/inMemory/sortStage");
+var floatingRowModel_1 = require("./rowControllers/floatingRowModel");
+var paginationController_1 = require("./rowControllers/paginationController");
+var virtualPageRowController_1 = require("./rowControllers/virtualPageRowController");
+var cMenuItem_1 = require("./widgets/cMenuItem");
+var component_1 = require("./widgets/component");
+var menuList_1 = require("./widgets/menuList");
+var cellNavigationService_1 = require("./cellNavigationService");
+var columnChangeEvent_1 = require("./columnChangeEvent");
+var constants_1 = require("./constants");
+var csvCreator_1 = require("./csvCreator");
+var eventService_1 = require("./eventService");
+var expressionService_1 = require("./expressionService");
+var gridCore_1 = require("./gridCore");
+var logger_1 = require("./logger");
+var masterSlaveService_1 = require("./masterSlaveService");
+var selectionController_1 = require("./selectionController");
+var selectionRendererFactory_1 = require("./selectionRendererFactory");
+var sortController_1 = require("./sortController");
+var svgFactory_1 = require("./svgFactory");
+var templateService_1 = require("./templateService");
+var utils_1 = require("./utils");
+var valueService_1 = require("./valueService");
+var popupService_1 = require("./widgets/popupService");
+var context_2 = require("./context/context");
+var context_3 = require("./context/context");
+var context_4 = require("./context/context");
+var context_5 = require("./context/context");
+var context_6 = require("./context/context");
+var gridRow_1 = require("./entities/gridRow");
+function populateClientExports(exports) {
+    // cellRenderers
+    exports.groupCellRendererFactory = groupCellRendererFactory_1.groupCellRendererFactory;
+    // columnController
+    exports.BalancedColumnTreeBuilder = balancedColumnTreeBuilder_1.BalancedColumnTreeBuilder;
+    exports.ColumnController = columnController_1.ColumnController;
+    exports.ColumnKeyCreator = columnKeyCreator_1.ColumnKeyCreator;
+    exports.ColumnUtils = columnUtils_1.ColumnUtils;
+    exports.DisplayedGroupCreator = displayedGroupCreator_1.DisplayedGroupCreator;
+    exports.GroupInstanceIdCreator = groupInstanceIdCreator_1.GroupInstanceIdCreator;
+    // components
+    exports.ComponentUtil = componentUtil_1.ComponentUtil;
+    exports.initialiseAgGridWithAngular1 = agGridNg1_1.initialiseAgGridWithAngular1;
+    exports.initialiseAgGridWithWebComponents = agGridWebComponent_1.initialiseAgGridWithWebComponents;
+    // context
+    exports.Context = context_1.Context;
+    exports.Autowired = context_2.Autowired;
+    exports.PostConstruct = context_3.PostConstruct;
+    exports.Optional = context_4.Optional;
+    exports.Bean = context_5.Bean;
+    exports.Qualifier = context_6.Qualifier;
+    // dragAndDrop
+    exports.DragAndDropService = dragAndDropService_1.DragAndDropService;
+    exports.DragService = dragService_1.DragService;
+    // entities
+    exports.Column = column_1.Column;
+    exports.ColumnGroup = columnGroup_1.ColumnGroup;
+    exports.GridCell = gridCell_1.GridCell;
+    exports.GridRow = gridRow_1.GridRow;
+    exports.OriginalColumnGroup = originalColumnGroup_1.OriginalColumnGroup;
+    exports.RowNode = rowNode_1.RowNode;
+    // filter
+    exports.FilterManager = filterManager_1.FilterManager;
+    exports.NumberFilter = numberFilter_1.NumberFilter;
+    exports.TextFilter = textFilter_1.TextFilter;
+    // gridPanel
+    exports.GridPanel = gridPanel_1.GridPanel;
+    exports.MouseEventService = mouseEventService_1.MouseEventService;
+    // headerRendering
+    exports.CssClassApplier = cssClassApplier_1.CssClassApplier;
+    exports.HeaderContainer = headerContainer_1.HeaderContainer;
+    exports.HeaderRenderer = headerRenderer_1.HeaderRenderer;
+    exports.HeaderTemplateLoader = headerTemplateLoader_1.HeaderTemplateLoader;
+    exports.HorizontalDragService = horizontalDragService_1.HorizontalDragService;
+    exports.MoveColumnController = moveColumnController_1.MoveColumnController;
+    exports.RenderedHeaderCell = renderedHeaderCell_1.RenderedHeaderCell;
+    exports.RenderedHeaderGroupCell = renderedHeaderGroupCell_1.RenderedHeaderGroupCell;
+    exports.StandardMenuFactory = standardMenu_1.StandardMenuFactory;
+    // layout
+    exports.BorderLayout = borderLayout_1.BorderLayout;
+    exports.TabbedLayout = tabbedLayout_1.TabbedLayout;
+    exports.VerticalStack = verticalStack_1.VerticalStack;
+    // rendering
+    exports.AutoWidthCalculator = autoWidthCalculator_1.AutoWidthCalculator;
+    exports.RenderedHeaderCell = renderedHeaderCell_1.RenderedHeaderCell;
+    exports.RenderedRow = renderedRow_1.RenderedRow;
+    exports.RowRenderer = rowRenderer_1.RowRenderer;
+    // rowControllers/inMemory
+    exports.FilterStage = fillterStage_1.FilterStage;
+    exports.FlattenStage = flattenStage_1.FlattenStage;
+    exports.InMemoryRowController = inMemoryRowController_1.InMemoryRowController;
+    exports.SortStage = sortStage_1.SortStage;
+    // rowControllers
+    exports.FloatingRowModel = floatingRowModel_1.FloatingRowModel;
+    exports.PaginationController = paginationController_1.PaginationController;
+    exports.VirtualPageRowController = virtualPageRowController_1.VirtualPageRowController;
+    // widgets
+    exports.PopupService = popupService_1.PopupService;
+    exports.CMenuItem = cMenuItem_1.CMenuItem;
+    exports.Component = component_1.Component;
+    exports.MenuList = menuList_1.MenuList;
+    // root
+    exports.CellNavigationService = cellNavigationService_1.CellNavigationService;
+    exports.ColumnChangeEvent = columnChangeEvent_1.ColumnChangeEvent;
+    exports.Constants = constants_1.Constants;
+    exports.CsvCreator = csvCreator_1.CsvCreator;
+    exports.Events = events_1.Events;
+    exports.EventService = eventService_1.EventService;
+    exports.ExpressionService = expressionService_1.ExpressionService;
+    exports.FocusedCellController = focusedCellController_1.FocusedCellController;
+    exports.defaultGroupComparator = functions_1.defaultGroupComparator;
+    exports.Grid = grid_1.Grid;
+    exports.GridApi = gridApi_1.GridApi;
+    exports.GridCore = gridCore_1.GridCore;
+    exports.GridOptionsWrapper = gridOptionsWrapper_1.GridOptionsWrapper;
+    exports.Logger = logger_1.Logger;
+    exports.MasterSlaveService = masterSlaveService_1.MasterSlaveService;
+    exports.SelectionController = selectionController_1.SelectionController;
+    exports.SelectionRendererFactory = selectionRendererFactory_1.SelectionRendererFactory;
+    exports.SortController = sortController_1.SortController;
+    exports.SvgFactory = svgFactory_1.SvgFactory;
+    exports.TemplateService = templateService_1.TemplateService;
+    exports.Utils = utils_1.Utils;
+    exports.ValueService = valueService_1.ValueService;
+}
+exports.populateClientExports = populateClientExports;
diff --git a/dist/lib/context/context.d.ts b/dist/lib/context/context.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c314f96fd58b579c5dfe57aa628228b8d7da7773
--- /dev/null
+++ b/dist/lib/context/context.d.ts
@@ -0,0 +1,36 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+export interface ContextParams {
+    seed: any;
+    beans: any[];
+    overrideBeans: any[];
+    debug: boolean;
+}
+export declare class Context {
+    private beans;
+    private contextParams;
+    private logger;
+    private destroyed;
+    constructor(params: ContextParams);
+    wireBean(bean: any): void;
+    private wireBeans(beans);
+    private createBeans();
+    private createBeanEntry(Bean);
+    private autoWireBeans(beans);
+    private methodWireBeans(beans);
+    private autoWireBean(bean);
+    private getBeanName(bean);
+    private methodWireBean(bean);
+    private getBeansForParameters(parameters, beanName);
+    private lookupBeanInstance(wiringBean, beanName, optional?);
+    private postWire(beans);
+    private wireCompleteBeans(beans);
+    destroy(): void;
+}
+export declare function PostConstruct(target: Object, methodName: string, descriptor: TypedPropertyDescriptor<any>): void;
+export declare function Bean(beanName: string): Function;
+export declare function Autowired(name?: string): Function;
+export declare function Optional(name?: string): Function;
+export declare function Qualifier(name: string): Function;
diff --git a/dist/lib/context/context.js b/dist/lib/context/context.js
new file mode 100644
index 0000000000000000000000000000000000000000..1fa5c27df4ee818a6f1d0f85fa0d7b77108e1c74
--- /dev/null
+++ b/dist/lib/context/context.js
@@ -0,0 +1,265 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var utils_1 = require('../utils');
+var logger_1 = require("../logger");
+var Context = (function () {
+    function Context(params) {
+        this.beans = {};
+        this.destroyed = false;
+        if (!params || !params.beans) {
+            return;
+        }
+        this.contextParams = params;
+        this.logger = new logger_1.Logger('Context', this.contextParams.debug);
+        this.logger.log('>> creating ag-Application Context');
+        this.createBeans();
+        var beans = utils_1.Utils.mapObject(this.beans, function (beanEntry) { return beanEntry.beanInstance; });
+        this.wireBeans(beans);
+        this.logger.log('>> ag-Application Context ready - component is alive');
+    }
+    Context.prototype.wireBean = function (bean) {
+        this.wireBeans([bean]);
+    };
+    Context.prototype.wireBeans = function (beans) {
+        this.autoWireBeans(beans);
+        this.methodWireBeans(beans);
+        this.postWire(beans);
+        this.wireCompleteBeans(beans);
+    };
+    Context.prototype.createBeans = function () {
+        var _this = this;
+        // register all normal beans
+        this.contextParams.beans.forEach(this.createBeanEntry.bind(this));
+        // register override beans, these will overwrite beans above of same name
+        if (this.contextParams.overrideBeans) {
+            this.contextParams.overrideBeans.forEach(this.createBeanEntry.bind(this));
+        }
+        // instantiate all beans - overridden beans will be left out
+        utils_1.Utils.iterateObject(this.beans, function (key, beanEntry) {
+            var constructorParamsMeta;
+            if (beanEntry.bean.prototype.__agBeanMetaData
+                && beanEntry.bean.prototype.__agBeanMetaData.agConstructor) {
+                constructorParamsMeta = beanEntry.bean.prototype.__agBeanMetaData.agConstructor;
+            }
+            var constructorParams = _this.getBeansForParameters(constructorParamsMeta, beanEntry.beanName);
+            var newInstance = applyToConstructor(beanEntry.bean, constructorParams);
+            beanEntry.beanInstance = newInstance;
+            _this.logger.log('bean ' + _this.getBeanName(newInstance) + ' created');
+        });
+    };
+    Context.prototype.createBeanEntry = function (Bean) {
+        var metaData = Bean.prototype.__agBeanMetaData;
+        if (!metaData) {
+            var beanName;
+            if (Bean.prototype.constructor) {
+                beanName = Bean.prototype.constructor.name;
+            }
+            else {
+                beanName = '' + Bean;
+            }
+            console.error('context item ' + beanName + ' is not a bean');
+            return;
+        }
+        var beanEntry = {
+            bean: Bean,
+            beanInstance: null,
+            beanName: metaData.beanName
+        };
+        this.beans[metaData.beanName] = beanEntry;
+    };
+    Context.prototype.autoWireBeans = function (beans) {
+        var _this = this;
+        beans.forEach(function (bean) { return _this.autoWireBean(bean); });
+    };
+    Context.prototype.methodWireBeans = function (beans) {
+        var _this = this;
+        beans.forEach(function (bean) { return _this.methodWireBean(bean); });
+    };
+    Context.prototype.autoWireBean = function (bean) {
+        var _this = this;
+        if (!bean
+            || !bean.__agBeanMetaData
+            || !bean.__agBeanMetaData.agClassAttributes) {
+            return;
+        }
+        var attributes = bean.__agBeanMetaData.agClassAttributes;
+        if (!attributes) {
+            return;
+        }
+        var beanName = this.getBeanName(bean);
+        attributes.forEach(function (attribute) {
+            var otherBean = _this.lookupBeanInstance(beanName, attribute.beanName, attribute.optional);
+            bean[attribute.attributeName] = otherBean;
+        });
+    };
+    Context.prototype.getBeanName = function (bean) {
+        var constructorString = bean.constructor.toString();
+        var beanName = constructorString.substring(9, constructorString.indexOf('('));
+        return beanName;
+    };
+    Context.prototype.methodWireBean = function (bean) {
+        var beanName = this.getBeanName(bean);
+        // if no init method, skip he bean
+        if (!bean.agWire) {
+            return;
+        }
+        var wireParams;
+        if (bean.__agBeanMetaData
+            && bean.__agBeanMetaData.agWire) {
+            wireParams = bean.__agBeanMetaData.agWire;
+        }
+        var initParams = this.getBeansForParameters(wireParams, beanName);
+        bean.agWire.apply(bean, initParams);
+    };
+    Context.prototype.getBeansForParameters = function (parameters, beanName) {
+        var _this = this;
+        var beansList = [];
+        if (parameters) {
+            utils_1.Utils.iterateObject(parameters, function (paramIndex, otherBeanName) {
+                var otherBean = _this.lookupBeanInstance(beanName, otherBeanName);
+                beansList[Number(paramIndex)] = otherBean;
+            });
+        }
+        return beansList;
+    };
+    Context.prototype.lookupBeanInstance = function (wiringBean, beanName, optional) {
+        if (optional === void 0) { optional = false; }
+        if (beanName === 'context') {
+            return this;
+        }
+        else if (this.contextParams.seed && this.contextParams.seed.hasOwnProperty(beanName)) {
+            return this.contextParams.seed[beanName];
+        }
+        else {
+            var beanEntry = this.beans[beanName];
+            if (beanEntry) {
+                return beanEntry.beanInstance;
+            }
+            if (!optional) {
+                console.error('ag-Grid: unable to find bean reference ' + beanName + ' while initialising ' + wiringBean);
+            }
+            return null;
+        }
+    };
+    Context.prototype.postWire = function (beans) {
+        beans.forEach(function (bean) {
+            // try calling init methods
+            if (bean.__agBeanMetaData && bean.__agBeanMetaData.postConstructMethods) {
+                bean.__agBeanMetaData.postConstructMethods.forEach(function (methodName) { return bean[methodName](); });
+            }
+        });
+    };
+    Context.prototype.wireCompleteBeans = function (beans) {
+        beans.forEach(function (bean) {
+            if (bean.agApplicationBoot) {
+                bean.agApplicationBoot();
+            }
+        });
+    };
+    Context.prototype.destroy = function () {
+        var _this = this;
+        // should only be able to destroy once
+        if (this.destroyed) {
+            return;
+        }
+        this.logger.log('>> Shutting down ag-Application Context');
+        utils_1.Utils.iterateObject(this.beans, function (key, beanEntry) {
+            if (beanEntry.beanInstance.agDestroy) {
+                if (_this.contextParams.debug) {
+                    console.log('ag-Grid: destroying ' + beanEntry.beanName);
+                }
+                beanEntry.beanInstance.agDestroy();
+            }
+            _this.logger.log('bean ' + _this.getBeanName(beanEntry.beanInstance) + ' destroyed');
+        });
+        this.destroyed = true;
+        this.logger.log('>> ag-Application Context shut down - component is dead');
+    };
+    return Context;
+})();
+exports.Context = Context;
+// taken from: http://stackoverflow.com/questions/3362471/how-can-i-call-a-javascript-constructor-using-call-or-apply
+// allows calling 'apply' on a constructor
+function applyToConstructor(constructor, argArray) {
+    var args = [null].concat(argArray);
+    var factoryFunction = constructor.bind.apply(constructor, args);
+    return new factoryFunction();
+}
+function PostConstruct(target, methodName, descriptor) {
+    // it's an attribute on the class
+    var props = getOrCreateProps(target);
+    if (!props.postConstructMethods) {
+        props.postConstructMethods = [];
+    }
+    props.postConstructMethods.push(methodName);
+}
+exports.PostConstruct = PostConstruct;
+function Bean(beanName) {
+    return function (classConstructor) {
+        var props = getOrCreateProps(classConstructor.prototype);
+        props.beanName = beanName;
+    };
+}
+exports.Bean = Bean;
+function Autowired(name) {
+    return autowiredFunc.bind(this, name, false);
+}
+exports.Autowired = Autowired;
+function Optional(name) {
+    return autowiredFunc.bind(this, name, true);
+}
+exports.Optional = Optional;
+function autowiredFunc(name, optional, classPrototype, methodOrAttributeName, index) {
+    if (name === null) {
+        console.error('ag-Grid: Autowired name should not be null');
+        return;
+    }
+    if (typeof index === 'number') {
+        console.error('ag-Grid: Autowired should be on an attribute');
+        return;
+    }
+    // it's an attribute on the class
+    var props = getOrCreateProps(classPrototype);
+    if (!props.agClassAttributes) {
+        props.agClassAttributes = [];
+    }
+    props.agClassAttributes.push({
+        attributeName: methodOrAttributeName,
+        beanName: name,
+        optional: optional
+    });
+}
+function Qualifier(name) {
+    return function (classPrototype, methodOrAttributeName, index) {
+        var props;
+        if (typeof index === 'number') {
+            // it's a parameter on a method
+            var methodName;
+            if (methodOrAttributeName) {
+                props = getOrCreateProps(classPrototype);
+                methodName = methodOrAttributeName;
+            }
+            else {
+                props = getOrCreateProps(classPrototype.prototype);
+                methodName = 'agConstructor';
+            }
+            if (!props[methodName]) {
+                props[methodName] = {};
+            }
+            props[methodName][index] = name;
+        }
+    };
+}
+exports.Qualifier = Qualifier;
+function getOrCreateProps(target) {
+    var props = target.__agBeanMetaData;
+    if (!props) {
+        props = {};
+        target.__agBeanMetaData = props;
+    }
+    return props;
+}
diff --git a/dist/lib/dragAndDrop/dragService.d.ts b/dist/lib/dragAndDrop/dragService.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2b6a86280cd9977c858e78a3fe7c7117b8ce5ef8
--- /dev/null
+++ b/dist/lib/dragAndDrop/dragService.d.ts
@@ -0,0 +1,27 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+export declare class DragService {
+    private loggerFactory;
+    private currentDragParams;
+    private dragging;
+    private eventLastTime;
+    private dragStartEvent;
+    private onMouseUpListener;
+    private onMouseMoveListener;
+    private logger;
+    private init();
+    addDragSource(params: DragListenerParams): void;
+    private onMouseDown(params, mouseEvent);
+    private isEventNearStartEvent(event);
+    private onMouseMove(mouseEvent);
+    onMouseUp(mouseEvent: MouseEvent): void;
+}
+export interface DragListenerParams {
+    dragStartPixels?: number;
+    eElement: HTMLElement;
+    onDragStart: (mouseEvent: MouseEvent) => void;
+    onDragStop: (mouseEvent: MouseEvent) => void;
+    onDragging: (mouseEvent: MouseEvent) => void;
+}
diff --git a/dist/lib/dragAndDrop/dragService.js b/dist/lib/dragAndDrop/dragService.js
new file mode 100644
index 0000000000000000000000000000000000000000..daa85a2702f8d350f0092f7aa64314521d6ab467
--- /dev/null
+++ b/dist/lib/dragAndDrop/dragService.js
@@ -0,0 +1,98 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var context_1 = require("../context/context");
+var context_2 = require("../context/context");
+var logger_1 = require("../logger");
+var context_3 = require("../context/context");
+var utils_1 = require('../utils');
+var DragService = (function () {
+    function DragService() {
+        this.onMouseUpListener = this.onMouseUp.bind(this);
+        this.onMouseMoveListener = this.onMouseMove.bind(this);
+    }
+    DragService.prototype.init = function () {
+        this.logger = this.loggerFactory.create('HorizontalDragService');
+    };
+    DragService.prototype.addDragSource = function (params) {
+        params.eElement.addEventListener('mousedown', this.onMouseDown.bind(this, params));
+    };
+    DragService.prototype.onMouseDown = function (params, mouseEvent) {
+        // only interested in left button clicks
+        if (mouseEvent.button !== 0) {
+            return;
+        }
+        this.currentDragParams = params;
+        this.dragging = false;
+        this.eventLastTime = mouseEvent;
+        this.dragStartEvent = mouseEvent;
+        document.addEventListener('mousemove', this.onMouseMoveListener);
+        document.addEventListener('mouseup', this.onMouseUpListener);
+        // see if we want to start dragging straight away
+        if (params.dragStartPixels === 0) {
+            this.onMouseMove(mouseEvent);
+        }
+    };
+    DragService.prototype.isEventNearStartEvent = function (event) {
+        // by default, we wait 4 pixels before starting the drag
+        var requiredPixelDiff = utils_1.Utils.exists(this.currentDragParams.dragStartPixels) ? this.currentDragParams.dragStartPixels : 4;
+        if (requiredPixelDiff === 0) {
+            return false;
+        }
+        var diffX = Math.abs(event.clientX - this.dragStartEvent.clientX);
+        var diffY = Math.abs(event.clientY - this.dragStartEvent.clientY);
+        return Math.max(diffX, diffY) <= requiredPixelDiff;
+    };
+    DragService.prototype.onMouseMove = function (mouseEvent) {
+        if (!this.dragging) {
+            // we want to have moved at least 4px before the drag starts
+            if (this.isEventNearStartEvent(mouseEvent)) {
+                return;
+            }
+            else {
+                this.dragging = true;
+                this.currentDragParams.onDragStart(this.dragStartEvent);
+            }
+        }
+        this.currentDragParams.onDragging(mouseEvent);
+    };
+    DragService.prototype.onMouseUp = function (mouseEvent) {
+        this.logger.log('onMouseUp');
+        document.removeEventListener('mouseup', this.onMouseUpListener);
+        document.removeEventListener('mousemove', this.onMouseMoveListener);
+        if (this.dragging) {
+            this.currentDragParams.onDragStop(mouseEvent);
+        }
+        this.dragStartEvent = null;
+        this.eventLastTime = null;
+        this.dragging = false;
+    };
+    __decorate([
+        context_2.Autowired('loggerFactory'), 
+        __metadata('design:type', logger_1.LoggerFactory)
+    ], DragService.prototype, "loggerFactory", void 0);
+    __decorate([
+        context_3.PostConstruct, 
+        __metadata('design:type', Function), 
+        __metadata('design:paramtypes', []), 
+        __metadata('design:returntype', void 0)
+    ], DragService.prototype, "init", null);
+    DragService = __decorate([
+        context_1.Bean('dragService'), 
+        __metadata('design:paramtypes', [])
+    ], DragService);
+    return DragService;
+})();
+exports.DragService = DragService;
diff --git a/dist/lib/dragAndDrop/oldToolPanelDragAndDropService.d.ts b/dist/lib/dragAndDrop/oldToolPanelDragAndDropService.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2d8f3f48511d2a1db39f47e35eeda79744ec36ab
--- /dev/null
+++ b/dist/lib/dragAndDrop/oldToolPanelDragAndDropService.d.ts
@@ -0,0 +1,21 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { LoggerFactory } from "../logger";
+/** Functionality for internal DnD functionality between GUI widgets. Eg this service is used to drag columns
+ * from the 'available columns' list and putting them into the 'grouped columns' in the tool panel.
+ * This service is NOT used by the column headers for resizing and moving, that is a different use case. */
+export declare class OldToolPanelDragAndDropService {
+    private dragItem;
+    private mouseUpEventListener;
+    private logger;
+    private destroyFunctions;
+    agWire(loggerFactory: LoggerFactory): void;
+    agDestroy(): void;
+    private stopDragging();
+    private setDragCssClasses(eListItem, dragging);
+    addDragSource(eDragSource: any, dragSourceCallback: any): void;
+    private onMouseDownDragSource(eDragSource, dragSourceCallback);
+    addDropTarget(eDropTarget: any, dropTargetCallback: any): void;
+}
diff --git a/dist/lib/dragAndDrop/oldToolPanelDragAndDropService.js b/dist/lib/dragAndDrop/oldToolPanelDragAndDropService.js
new file mode 100644
index 0000000000000000000000000000000000000000..c2980df2b526fbfad61cdc99ac63d73ceae5bdd1
--- /dev/null
+++ b/dist/lib/dragAndDrop/oldToolPanelDragAndDropService.js
@@ -0,0 +1,115 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var __param = (this && this.__param) || function (paramIndex, decorator) {
+    return function (target, key) { decorator(target, key, paramIndex); }
+};
+var utils_1 = require('../utils');
+var logger_1 = require("../logger");
+var context_1 = require("../context/context");
+var context_2 = require("../context/context");
+/** Functionality for internal DnD functionality between GUI widgets. Eg this service is used to drag columns
+ * from the 'available columns' list and putting them into the 'grouped columns' in the tool panel.
+ * This service is NOT used by the column headers for resizing and moving, that is a different use case. */
+var OldToolPanelDragAndDropService = (function () {
+    function OldToolPanelDragAndDropService() {
+        this.destroyFunctions = [];
+    }
+    OldToolPanelDragAndDropService.prototype.agWire = function (loggerFactory) {
+        this.logger = loggerFactory.create('OldToolPanelDragAndDropService');
+        // need to clean this up, add to 'finished' logic in grid
+        var mouseUpListener = this.stopDragging.bind(this);
+        document.addEventListener('mouseup', mouseUpListener);
+        this.destroyFunctions.push(function () { document.removeEventListener('mouseup', mouseUpListener); });
+    };
+    OldToolPanelDragAndDropService.prototype.agDestroy = function () {
+        this.destroyFunctions.forEach(function (func) { return func(); });
+        document.removeEventListener('mouseup', this.mouseUpEventListener);
+    };
+    OldToolPanelDragAndDropService.prototype.stopDragging = function () {
+        if (this.dragItem) {
+            this.setDragCssClasses(this.dragItem.eDragSource, false);
+            this.dragItem = null;
+        }
+    };
+    OldToolPanelDragAndDropService.prototype.setDragCssClasses = function (eListItem, dragging) {
+        utils_1.Utils.addOrRemoveCssClass(eListItem, 'ag-dragging', dragging);
+        utils_1.Utils.addOrRemoveCssClass(eListItem, 'ag-not-dragging', !dragging);
+    };
+    OldToolPanelDragAndDropService.prototype.addDragSource = function (eDragSource, dragSourceCallback) {
+        this.setDragCssClasses(eDragSource, false);
+        eDragSource.addEventListener('mousedown', this.onMouseDownDragSource.bind(this, eDragSource, dragSourceCallback));
+    };
+    OldToolPanelDragAndDropService.prototype.onMouseDownDragSource = function (eDragSource, dragSourceCallback) {
+        if (this.dragItem) {
+            this.stopDragging();
+        }
+        var data;
+        if (dragSourceCallback.getData) {
+            data = dragSourceCallback.getData();
+        }
+        var containerId;
+        if (dragSourceCallback.getContainerId) {
+            containerId = dragSourceCallback.getContainerId();
+        }
+        this.dragItem = {
+            eDragSource: eDragSource,
+            data: data,
+            containerId: containerId
+        };
+        this.setDragCssClasses(this.dragItem.eDragSource, true);
+    };
+    OldToolPanelDragAndDropService.prototype.addDropTarget = function (eDropTarget, dropTargetCallback) {
+        var _this = this;
+        var mouseIn = false;
+        var acceptDrag = false;
+        eDropTarget.addEventListener('mouseover', function () {
+            if (!mouseIn) {
+                mouseIn = true;
+                if (_this.dragItem) {
+                    acceptDrag = dropTargetCallback.acceptDrag(_this.dragItem);
+                }
+                else {
+                    acceptDrag = false;
+                }
+            }
+        });
+        eDropTarget.addEventListener('mouseout', function () {
+            if (acceptDrag) {
+                dropTargetCallback.noDrop();
+            }
+            mouseIn = false;
+            acceptDrag = false;
+        });
+        eDropTarget.addEventListener('mouseup', function () {
+            // dragItem should never be null, checking just in case
+            if (acceptDrag && _this.dragItem) {
+                dropTargetCallback.drop(_this.dragItem);
+            }
+        });
+    };
+    __decorate([
+        __param(0, context_2.Qualifier('loggerFactory')), 
+        __metadata('design:type', Function), 
+        __metadata('design:paramtypes', [logger_1.LoggerFactory]), 
+        __metadata('design:returntype', void 0)
+    ], OldToolPanelDragAndDropService.prototype, "agWire", null);
+    OldToolPanelDragAndDropService = __decorate([
+        context_1.Bean('oldToolPanelDragAndDropService'), 
+        __metadata('design:paramtypes', [])
+    ], OldToolPanelDragAndDropService);
+    return OldToolPanelDragAndDropService;
+})();
+exports.OldToolPanelDragAndDropService = OldToolPanelDragAndDropService;
diff --git a/dist/lib/entities/gridCell.d.ts b/dist/lib/entities/gridCell.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b1457591fa81fc26612d2f4c9e4ea76eb7c657e9
--- /dev/null
+++ b/dist/lib/entities/gridCell.d.ts
@@ -0,0 +1,15 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { Column } from "./column";
+import { GridRow } from "./gridRow";
+export declare class GridCell {
+    floating: string;
+    rowIndex: number;
+    column: Column;
+    constructor(rowIndex: number, floating: string, column: Column);
+    getGridRow(): GridRow;
+    toString(): string;
+    createId(): string;
+}
diff --git a/dist/lib/entities/gridCell.js b/dist/lib/entities/gridCell.js
new file mode 100644
index 0000000000000000000000000000000000000000..7d3e229ee214c791caea864fffefefd3771f9238
--- /dev/null
+++ b/dist/lib/entities/gridCell.js
@@ -0,0 +1,26 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var utils_1 = require('../utils');
+var gridRow_1 = require("./gridRow");
+var GridCell = (function () {
+    function GridCell(rowIndex, floating, column) {
+        this.rowIndex = rowIndex;
+        this.column = column;
+        this.floating = utils_1.Utils.makeNull(floating);
+    }
+    GridCell.prototype.getGridRow = function () {
+        return new gridRow_1.GridRow(this.rowIndex, this.floating);
+    };
+    GridCell.prototype.toString = function () {
+        return "rowIndex = " + this.rowIndex + ", floating = " + this.floating + ", column = " + (this.column ? this.column.getId() : null);
+    };
+    GridCell.prototype.createId = function () {
+        return this.rowIndex + "." + this.floating + "." + this.column.getId();
+    };
+    return GridCell;
+})();
+exports.GridCell = GridCell;
diff --git a/dist/lib/entities/gridRow.d.ts b/dist/lib/entities/gridRow.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..26cb2a425df4550c83411230c17de7a5563ab2a1
--- /dev/null
+++ b/dist/lib/entities/gridRow.d.ts
@@ -0,0 +1,18 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { GridCell } from "./gridCell";
+import { Column } from "./column";
+export declare class GridRow {
+    floating: string;
+    rowIndex: number;
+    constructor(rowIndex: number, floating: string);
+    isFloatingTop(): boolean;
+    isFloatingBottom(): boolean;
+    isNotFloating(): boolean;
+    equals(otherSelection: GridRow): boolean;
+    toString(): string;
+    getGridCell(column: Column): GridCell;
+    before(otherSelection: GridRow): boolean;
+}
diff --git a/dist/lib/entities/gridRow.js b/dist/lib/entities/gridRow.js
new file mode 100644
index 0000000000000000000000000000000000000000..4c9419ce733517eb279e0596844dad2ae03b9925
--- /dev/null
+++ b/dist/lib/entities/gridRow.js
@@ -0,0 +1,68 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var constants_1 = require("../constants");
+var utils_1 = require('../utils');
+var gridCell_1 = require("./gridCell");
+var GridRow = (function () {
+    function GridRow(rowIndex, floating) {
+        this.rowIndex = rowIndex;
+        this.floating = utils_1.Utils.makeNull(floating);
+    }
+    GridRow.prototype.isFloatingTop = function () {
+        return this.floating === constants_1.Constants.FLOATING_TOP;
+    };
+    GridRow.prototype.isFloatingBottom = function () {
+        return this.floating === constants_1.Constants.FLOATING_BOTTOM;
+    };
+    GridRow.prototype.isNotFloating = function () {
+        return !this.isFloatingBottom() && !this.isFloatingTop();
+    };
+    GridRow.prototype.equals = function (otherSelection) {
+        return this.rowIndex === otherSelection.rowIndex
+            && this.floating === otherSelection.floating;
+    };
+    GridRow.prototype.toString = function () {
+        return "rowIndex = " + this.rowIndex + ", floating = " + this.floating;
+    };
+    GridRow.prototype.getGridCell = function (column) {
+        return new gridCell_1.GridCell(this.rowIndex, this.floating, column);
+    };
+    // tests if this row selection is before the other row selection
+    GridRow.prototype.before = function (otherSelection) {
+        var otherFloating = otherSelection.floating;
+        switch (this.floating) {
+            case constants_1.Constants.FLOATING_TOP:
+                // we we are floating top, and other isn't, then we are always before
+                if (otherFloating !== constants_1.Constants.FLOATING_TOP) {
+                    return true;
+                }
+                break;
+            case constants_1.Constants.FLOATING_BOTTOM:
+                // if we are floating bottom, and the other isn't, then we are never before
+                if (otherFloating !== constants_1.Constants.FLOATING_BOTTOM) {
+                    return false;
+                }
+                break;
+            default:
+                // if we are not floating, but the other one is floating...
+                if (utils_1.Utils.exists(otherFloating)) {
+                    if (otherFloating === constants_1.Constants.FLOATING_TOP) {
+                        // we are not floating, other is floating top, we are first
+                        return false;
+                    }
+                    else {
+                        // we are not floating, other is floating bottom, we are always first
+                        return true;
+                    }
+                }
+                break;
+        }
+        return this.rowIndex <= otherSelection.rowIndex;
+    };
+    return GridRow;
+})();
+exports.GridRow = GridRow;
diff --git a/dist/lib/focusedCellController.d.ts b/dist/lib/focusedCellController.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..94f4d838d9bd900124d5fd068ca1dda34c47953b
--- /dev/null
+++ b/dist/lib/focusedCellController.d.ts
@@ -0,0 +1,20 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { Column } from "./entities/column";
+import { ColDef } from "./entities/colDef";
+import { GridCell } from "./entities/gridCell";
+export declare class FocusedCellController {
+    private eventService;
+    private gridOptionsWrapper;
+    private columnController;
+    private focusedCell;
+    private init();
+    clearFocusedCell(): void;
+    getFocusedCell(): GridCell;
+    setFocusedCell(rowIndex: number, colKey: Column | ColDef | string, floating: string, forceBrowserFocus?: boolean): void;
+    isCellFocused(rowIndex: number, column: Column, floating: string): boolean;
+    isRowFocused(rowIndex: number, floating: string): boolean;
+    private onCellFocused(forceBrowserFocus);
+}
diff --git a/dist/lib/focusedCellController.js b/dist/lib/focusedCellController.js
new file mode 100644
index 0000000000000000000000000000000000000000..290f4baaa2a9354a3eebe20c5409561080a0dbd6
--- /dev/null
+++ b/dist/lib/focusedCellController.js
@@ -0,0 +1,104 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var context_1 = require("./context/context");
+var context_2 = require("./context/context");
+var eventService_1 = require("./eventService");
+var context_3 = require("./context/context");
+var events_1 = require("./events");
+var gridOptionsWrapper_1 = require("./gridOptionsWrapper");
+var columnController_1 = require("./columnController/columnController");
+var utils_1 = require('./utils');
+var gridCell_1 = require("./entities/gridCell");
+var FocusedCellController = (function () {
+    function FocusedCellController() {
+    }
+    FocusedCellController.prototype.init = function () {
+        this.eventService.addEventListener(events_1.Events.EVENT_COLUMN_EVERYTHING_CHANGED, this.clearFocusedCell.bind(this));
+        this.eventService.addEventListener(events_1.Events.EVENT_COLUMN_GROUP_OPENED, this.clearFocusedCell.bind(this));
+        this.eventService.addEventListener(events_1.Events.EVENT_COLUMN_MOVED, this.clearFocusedCell.bind(this));
+        this.eventService.addEventListener(events_1.Events.EVENT_COLUMN_PINNED, this.clearFocusedCell.bind(this));
+        this.eventService.addEventListener(events_1.Events.EVENT_COLUMN_ROW_GROUP_CHANGE, this.clearFocusedCell.bind(this));
+        this.eventService.addEventListener(events_1.Events.EVENT_COLUMN_VISIBLE, this.clearFocusedCell.bind(this));
+        //this.eventService.addEventListener(Events.EVENT_COLUMN_VISIBLE, this.clearFocusedCell.bind(this));
+    };
+    FocusedCellController.prototype.clearFocusedCell = function () {
+        this.focusedCell = null;
+        this.onCellFocused(false);
+    };
+    FocusedCellController.prototype.getFocusedCell = function () {
+        return this.focusedCell;
+    };
+    FocusedCellController.prototype.setFocusedCell = function (rowIndex, colKey, floating, forceBrowserFocus) {
+        if (forceBrowserFocus === void 0) { forceBrowserFocus = false; }
+        if (this.gridOptionsWrapper.isSuppressCellSelection()) {
+            return;
+        }
+        var column = utils_1.Utils.makeNull(this.columnController.getColumn(colKey));
+        this.focusedCell = new gridCell_1.GridCell(rowIndex, utils_1.Utils.makeNull(floating), column);
+        this.onCellFocused(forceBrowserFocus);
+    };
+    FocusedCellController.prototype.isCellFocused = function (rowIndex, column, floating) {
+        if (utils_1.Utils.missing(this.focusedCell)) {
+            return false;
+        }
+        return this.focusedCell.column === column && this.isRowFocused(rowIndex, floating);
+    };
+    FocusedCellController.prototype.isRowFocused = function (rowIndex, floating) {
+        if (utils_1.Utils.missing(this.focusedCell)) {
+            return false;
+        }
+        var floatingOrNull = utils_1.Utils.makeNull(floating);
+        return this.focusedCell.rowIndex === rowIndex && this.focusedCell.floating === floatingOrNull;
+    };
+    FocusedCellController.prototype.onCellFocused = function (forceBrowserFocus) {
+        var event = {
+            rowIndex: null,
+            column: null,
+            floating: null,
+            forceBrowserFocus: forceBrowserFocus
+        };
+        if (this.focusedCell) {
+            event.rowIndex = this.focusedCell.rowIndex;
+            event.column = this.focusedCell.column;
+            event.floating = this.focusedCell.floating;
+        }
+        this.eventService.dispatchEvent(events_1.Events.EVENT_CELL_FOCUSED, event);
+    };
+    __decorate([
+        context_2.Autowired('eventService'), 
+        __metadata('design:type', eventService_1.EventService)
+    ], FocusedCellController.prototype, "eventService", void 0);
+    __decorate([
+        context_2.Autowired('gridOptionsWrapper'), 
+        __metadata('design:type', gridOptionsWrapper_1.GridOptionsWrapper)
+    ], FocusedCellController.prototype, "gridOptionsWrapper", void 0);
+    __decorate([
+        context_2.Autowired('columnController'), 
+        __metadata('design:type', columnController_1.ColumnController)
+    ], FocusedCellController.prototype, "columnController", void 0);
+    __decorate([
+        context_3.PostConstruct, 
+        __metadata('design:type', Function), 
+        __metadata('design:paramtypes', []), 
+        __metadata('design:returntype', void 0)
+    ], FocusedCellController.prototype, "init", null);
+    FocusedCellController = __decorate([
+        context_1.Bean('focusedCellController'), 
+        __metadata('design:paramtypes', [])
+    ], FocusedCellController);
+    return FocusedCellController;
+})();
+exports.FocusedCellController = FocusedCellController;
diff --git a/dist/lib/gridCore.d.ts b/dist/lib/gridCore.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..03cdc55bdad08ee6269cddbce257bc2fbf0d4df0
--- /dev/null
+++ b/dist/lib/gridCore.d.ts
@@ -0,0 +1,42 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { LoggerFactory } from "./logger";
+export declare class GridCore {
+    private gridOptions;
+    private gridOptionsWrapper;
+    private paginationController;
+    private rowModel;
+    private columnController;
+    private rowRenderer;
+    private filterManager;
+    private eventService;
+    private gridPanel;
+    private eGridDiv;
+    private $scope;
+    private quickFilterOnScope;
+    private popupService;
+    private focusedCellController;
+    private rowGroupPanel;
+    private toolPanel;
+    private statusBar;
+    private finished;
+    private doingVirtualPaging;
+    private eRootPanel;
+    private toolPanelShowing;
+    private windowResizeListener;
+    private logger;
+    constructor(loggerFactory: LoggerFactory);
+    init(): void;
+    private createSouthPanel();
+    private onRowGroupChanged();
+    agApplicationBoot(): void;
+    private addWindowResizeListener();
+    private periodicallyDoLayout();
+    showToolPanel(show: any): void;
+    isToolPanelShowing(): boolean;
+    agDestroy(): void;
+    ensureNodeVisible(comparator: any): void;
+    doLayout(): void;
+}
diff --git a/dist/lib/gridCore.js b/dist/lib/gridCore.js
new file mode 100644
index 0000000000000000000000000000000000000000..06341e958acf0ec6a4f43476b2e4e2aa3694f3d5
--- /dev/null
+++ b/dist/lib/gridCore.js
@@ -0,0 +1,288 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var __param = (this && this.__param) || function (paramIndex, decorator) {
+    return function (target, key) { decorator(target, key, paramIndex); }
+};
+var gridOptionsWrapper_1 = require("./gridOptionsWrapper");
+var paginationController_1 = require("./rowControllers/paginationController");
+var columnController_1 = require("./columnController/columnController");
+var rowRenderer_1 = require("./rendering/rowRenderer");
+var filterManager_1 = require("./filter/filterManager");
+var eventService_1 = require("./eventService");
+var gridPanel_1 = require("./gridPanel/gridPanel");
+var constants_1 = require("./constants");
+var popupService_1 = require("./widgets/popupService");
+var logger_1 = require("./logger");
+var events_1 = require("./events");
+var borderLayout_1 = require("./layout/borderLayout");
+var context_1 = require("./context/context");
+var context_2 = require("./context/context");
+var context_3 = require("./context/context");
+var context_4 = require("./context/context");
+var focusedCellController_1 = require("./focusedCellController");
+var context_5 = require("./context/context");
+var component_1 = require("./widgets/component");
+var GridCore = (function () {
+    function GridCore(loggerFactory) {
+        this.logger = loggerFactory.create('GridCore');
+    }
+    GridCore.prototype.init = function () {
+        var _this = this;
+        // and the last bean, done in it's own section, as it's optional
+        var toolPanelGui;
+        var eSouthPanel = this.createSouthPanel();
+        if (this.toolPanel && !this.gridOptionsWrapper.isForPrint()) {
+            toolPanelGui = this.toolPanel.getGui();
+        }
+        var rowGroupGui;
+        if (this.rowGroupPanel) {
+            rowGroupGui = this.rowGroupPanel.getGui();
+        }
+        this.eRootPanel = new borderLayout_1.BorderLayout({
+            center: this.gridPanel.getLayout(),
+            east: toolPanelGui,
+            north: rowGroupGui,
+            south: eSouthPanel,
+            dontFill: this.gridOptionsWrapper.isForPrint(),
+            name: 'eRootPanel'
+        });
+        // see what the grid options are for default of toolbar
+        this.showToolPanel(this.gridOptionsWrapper.isShowToolPanel());
+        this.eGridDiv.appendChild(this.eRootPanel.getGui());
+        // if using angular, watch for quickFilter changes
+        if (this.$scope) {
+            this.$scope.$watch(this.quickFilterOnScope, function (newFilter) { return _this.filterManager.setQuickFilter(newFilter); });
+        }
+        if (!this.gridOptionsWrapper.isForPrint()) {
+            this.addWindowResizeListener();
+        }
+        this.doLayout();
+        this.finished = false;
+        this.periodicallyDoLayout();
+        this.popupService.setPopupParent(this.eRootPanel.getGui());
+        this.eventService.addEventListener(events_1.Events.EVENT_COLUMN_ROW_GROUP_CHANGE, this.onRowGroupChanged.bind(this));
+        this.eventService.addEventListener(events_1.Events.EVENT_COLUMN_EVERYTHING_CHANGED, this.onRowGroupChanged.bind(this));
+        this.onRowGroupChanged();
+        this.logger.log('ready');
+    };
+    GridCore.prototype.createSouthPanel = function () {
+        if (!this.statusBar && this.gridOptionsWrapper.isEnableStatusBar()) {
+            console.warn('ag-Grid: status bar is only available in ag-Grid-Enterprise');
+        }
+        var statusBarEnabled = this.statusBar && this.gridOptionsWrapper.isEnableStatusBar();
+        var paginationPanelEnabled = this.gridOptionsWrapper.isRowModelPagination() && !this.gridOptionsWrapper.isForPrint();
+        if (!statusBarEnabled && !paginationPanelEnabled) {
+            return null;
+        }
+        var eSouthPanel = document.createElement('div');
+        if (statusBarEnabled) {
+            eSouthPanel.appendChild(this.statusBar.getGui());
+        }
+        if (paginationPanelEnabled) {
+            eSouthPanel.appendChild(this.paginationController.getGui());
+        }
+        return eSouthPanel;
+    };
+    GridCore.prototype.onRowGroupChanged = function () {
+        if (!this.rowGroupPanel) {
+            return;
+        }
+        var rowGroupPanelShow = this.gridOptionsWrapper.getRowGroupPanelShow();
+        if (rowGroupPanelShow === constants_1.Constants.ALWAYS) {
+            this.eRootPanel.setNorthVisible(true);
+        }
+        else if (rowGroupPanelShow === constants_1.Constants.ONLY_WHEN_GROUPING) {
+            var grouping = !this.columnController.isRowGroupEmpty();
+            this.eRootPanel.setNorthVisible(grouping);
+        }
+        else {
+            this.eRootPanel.setNorthVisible(false);
+        }
+    };
+    GridCore.prototype.agApplicationBoot = function () {
+        var readyEvent = {
+            api: this.gridOptions.api,
+            columnApi: this.gridOptions.columnApi
+        };
+        this.eventService.dispatchEvent(events_1.Events.EVENT_GRID_READY, readyEvent);
+    };
+    GridCore.prototype.addWindowResizeListener = function () {
+        var that = this;
+        // putting this into a function, so when we remove the function,
+        // we are sure we are removing the exact same function (i'm not
+        // sure what 'bind' does to the function reference, if it's safe
+        // the result from 'bind').
+        this.windowResizeListener = function resizeListener() {
+            that.doLayout();
+        };
+        window.addEventListener('resize', this.windowResizeListener);
+    };
+    GridCore.prototype.periodicallyDoLayout = function () {
+        if (!this.finished) {
+            var that = this;
+            setTimeout(function () {
+                that.doLayout();
+                that.gridPanel.periodicallyCheck();
+                that.periodicallyDoLayout();
+            }, 500);
+        }
+    };
+    GridCore.prototype.showToolPanel = function (show) {
+        if (show && !this.toolPanel) {
+            console.warn('ag-Grid: toolPanel is only available in ag-Grid Enterprise');
+            this.toolPanelShowing = false;
+            return;
+        }
+        this.toolPanelShowing = show;
+        this.eRootPanel.setEastVisible(show);
+    };
+    GridCore.prototype.isToolPanelShowing = function () {
+        return this.toolPanelShowing;
+    };
+    GridCore.prototype.agDestroy = function () {
+        if (this.windowResizeListener) {
+            window.removeEventListener('resize', this.windowResizeListener);
+            this.logger.log('Removing windowResizeListener');
+        }
+        this.finished = true;
+        this.eGridDiv.removeChild(this.eRootPanel.getGui());
+        this.logger.log('Grid DOM removed');
+    };
+    GridCore.prototype.ensureNodeVisible = function (comparator) {
+        if (this.doingVirtualPaging) {
+            throw 'Cannot use ensureNodeVisible when doing virtual paging, as we cannot check rows that are not in memory';
+        }
+        // look for the node index we want to display
+        var rowCount = this.rowModel.getRowCount();
+        var comparatorIsAFunction = typeof comparator === 'function';
+        var indexToSelect = -1;
+        // go through all the nodes, find the one we want to show
+        for (var i = 0; i < rowCount; i++) {
+            var node = this.rowModel.getRow(i);
+            if (comparatorIsAFunction) {
+                if (comparator(node)) {
+                    indexToSelect = i;
+                    break;
+                }
+            }
+            else {
+                // check object equality against node and data
+                if (comparator === node || comparator === node.data) {
+                    indexToSelect = i;
+                    break;
+                }
+            }
+        }
+        if (indexToSelect >= 0) {
+            this.gridPanel.ensureIndexVisible(indexToSelect);
+        }
+    };
+    GridCore.prototype.doLayout = function () {
+        // need to do layout first, as drawVirtualRows and setPinnedColHeight
+        // need to know the result of the resizing of the panels.
+        var sizeChanged = this.eRootPanel.doLayout();
+        // both of the two below should be done in gridPanel, the gridPanel should register 'resize' to the panel
+        if (sizeChanged) {
+            this.rowRenderer.drawVirtualRows();
+            var event = {
+                clientWidth: this.eRootPanel.getGui().clientWidth,
+                clientHeight: this.eRootPanel.getGui().clientHeight
+            };
+            this.eventService.dispatchEvent(events_1.Events.EVENT_GRID_SIZE_CHANGED, event);
+        }
+    };
+    __decorate([
+        context_3.Autowired('gridOptions'), 
+        __metadata('design:type', Object)
+    ], GridCore.prototype, "gridOptions", void 0);
+    __decorate([
+        context_3.Autowired('gridOptionsWrapper'), 
+        __metadata('design:type', gridOptionsWrapper_1.GridOptionsWrapper)
+    ], GridCore.prototype, "gridOptionsWrapper", void 0);
+    __decorate([
+        context_3.Autowired('paginationController'), 
+        __metadata('design:type', paginationController_1.PaginationController)
+    ], GridCore.prototype, "paginationController", void 0);
+    __decorate([
+        context_3.Autowired('rowModel'), 
+        __metadata('design:type', Object)
+    ], GridCore.prototype, "rowModel", void 0);
+    __decorate([
+        context_3.Autowired('columnController'), 
+        __metadata('design:type', columnController_1.ColumnController)
+    ], GridCore.prototype, "columnController", void 0);
+    __decorate([
+        context_3.Autowired('rowRenderer'), 
+        __metadata('design:type', rowRenderer_1.RowRenderer)
+    ], GridCore.prototype, "rowRenderer", void 0);
+    __decorate([
+        context_3.Autowired('filterManager'), 
+        __metadata('design:type', filterManager_1.FilterManager)
+    ], GridCore.prototype, "filterManager", void 0);
+    __decorate([
+        context_3.Autowired('eventService'), 
+        __metadata('design:type', eventService_1.EventService)
+    ], GridCore.prototype, "eventService", void 0);
+    __decorate([
+        context_3.Autowired('gridPanel'), 
+        __metadata('design:type', gridPanel_1.GridPanel)
+    ], GridCore.prototype, "gridPanel", void 0);
+    __decorate([
+        context_3.Autowired('eGridDiv'), 
+        __metadata('design:type', HTMLElement)
+    ], GridCore.prototype, "eGridDiv", void 0);
+    __decorate([
+        context_3.Autowired('$scope'), 
+        __metadata('design:type', Object)
+    ], GridCore.prototype, "$scope", void 0);
+    __decorate([
+        context_3.Autowired('quickFilterOnScope'), 
+        __metadata('design:type', String)
+    ], GridCore.prototype, "quickFilterOnScope", void 0);
+    __decorate([
+        context_3.Autowired('popupService'), 
+        __metadata('design:type', popupService_1.PopupService)
+    ], GridCore.prototype, "popupService", void 0);
+    __decorate([
+        context_3.Autowired('focusedCellController'), 
+        __metadata('design:type', focusedCellController_1.FocusedCellController)
+    ], GridCore.prototype, "focusedCellController", void 0);
+    __decorate([
+        context_5.Optional('rowGroupPanel'), 
+        __metadata('design:type', component_1.Component)
+    ], GridCore.prototype, "rowGroupPanel", void 0);
+    __decorate([
+        context_5.Optional('toolPanel'), 
+        __metadata('design:type', component_1.Component)
+    ], GridCore.prototype, "toolPanel", void 0);
+    __decorate([
+        context_5.Optional('statusBar'), 
+        __metadata('design:type', component_1.Component)
+    ], GridCore.prototype, "statusBar", void 0);
+    __decorate([
+        context_4.PostConstruct, 
+        __metadata('design:type', Function), 
+        __metadata('design:paramtypes', []), 
+        __metadata('design:returntype', void 0)
+    ], GridCore.prototype, "init", null);
+    GridCore = __decorate([
+        context_1.Bean('gridCore'),
+        __param(0, context_2.Qualifier('loggerFactory')), 
+        __metadata('design:paramtypes', [logger_1.LoggerFactory])
+    ], GridCore);
+    return GridCore;
+})();
+exports.GridCore = GridCore;
diff --git a/dist/lib/gridPanel/mouseEventService.d.ts b/dist/lib/gridPanel/mouseEventService.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..70974c6632283e3d314e51b45aea8e4e6dde8592
--- /dev/null
+++ b/dist/lib/gridPanel/mouseEventService.d.ts
@@ -0,0 +1,21 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { GridCell } from "../entities/gridCell";
+export declare class MouseEventService {
+    private gridPanel;
+    private columnController;
+    private rowModel;
+    private floatingRowModel;
+    private gridOptionsWrapper;
+    getCellForMouseEvent(mouseEvent: MouseEvent): GridCell;
+    private getFloating(mouseEvent);
+    private getFloatingRowIndex(mouseEvent, floating);
+    private getRowIndex(mouseEvent, floating);
+    private getBodyRowIndex(mouseEvent);
+    private getContainer(mouseEvent);
+    private getColumn(mouseEvent);
+    private getColumnsForContainer(container);
+    private getXForContainer(container, mouseEvent);
+}
diff --git a/dist/lib/gridPanel/mouseEventService.js b/dist/lib/gridPanel/mouseEventService.js
new file mode 100644
index 0000000000000000000000000000000000000000..ffc4f268b4dea1e392e556b8fe809b83a53d4202
--- /dev/null
+++ b/dist/lib/gridPanel/mouseEventService.js
@@ -0,0 +1,170 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var context_1 = require("../context/context");
+var context_2 = require("../context/context");
+var gridPanel_1 = require("./gridPanel");
+var columnController_1 = require("../columnController/columnController");
+var column_1 = require("../entities/column");
+var constants_1 = require("../constants");
+var floatingRowModel_1 = require("../rowControllers/floatingRowModel");
+var utils_1 = require('../utils');
+var gridCell_1 = require("../entities/gridCell");
+var gridOptionsWrapper_1 = require("../gridOptionsWrapper");
+var MouseEventService = (function () {
+    function MouseEventService() {
+    }
+    MouseEventService.prototype.getCellForMouseEvent = function (mouseEvent) {
+        var floating = this.getFloating(mouseEvent);
+        var rowIndex = this.getRowIndex(mouseEvent, floating);
+        var column = this.getColumn(mouseEvent);
+        if (rowIndex >= 0 && utils_1.Utils.exists(column)) {
+            return new gridCell_1.GridCell(rowIndex, floating, column);
+        }
+        else {
+            return null;
+        }
+    };
+    MouseEventService.prototype.getFloating = function (mouseEvent) {
+        var floatingTopRect = this.gridPanel.getFloatingTopClientRect();
+        var floatingBottomRect = this.gridPanel.getFloatingBottomClientRect();
+        var floatingTopRowsExist = !this.floatingRowModel.isEmpty(constants_1.Constants.FLOATING_TOP);
+        var floatingBottomRowsExist = !this.floatingRowModel.isEmpty(constants_1.Constants.FLOATING_BOTTOM);
+        if (floatingTopRowsExist && floatingTopRect.bottom >= mouseEvent.clientY) {
+            return constants_1.Constants.FLOATING_TOP;
+        }
+        else if (floatingBottomRowsExist && floatingBottomRect.top <= mouseEvent.clientY) {
+            return constants_1.Constants.FLOATING_BOTTOM;
+        }
+        else {
+            return null;
+        }
+    };
+    MouseEventService.prototype.getFloatingRowIndex = function (mouseEvent, floating) {
+        var clientRect;
+        switch (floating) {
+            case constants_1.Constants.FLOATING_TOP:
+                clientRect = this.gridPanel.getFloatingTopClientRect();
+                break;
+            case constants_1.Constants.FLOATING_BOTTOM:
+                clientRect = this.gridPanel.getFloatingBottomClientRect();
+                break;
+        }
+        var bodyY = mouseEvent.clientY - clientRect.top;
+        var rowIndex = this.floatingRowModel.getRowAtPixel(bodyY, floating);
+        return rowIndex;
+    };
+    MouseEventService.prototype.getRowIndex = function (mouseEvent, floating) {
+        switch (floating) {
+            case constants_1.Constants.FLOATING_TOP:
+            case constants_1.Constants.FLOATING_BOTTOM:
+                return this.getFloatingRowIndex(mouseEvent, floating);
+            default: return this.getBodyRowIndex(mouseEvent);
+        }
+    };
+    MouseEventService.prototype.getBodyRowIndex = function (mouseEvent) {
+        var clientRect = this.gridPanel.getBodyViewportClientRect();
+        var scrollY = this.gridPanel.getVerticalScrollPosition();
+        var bodyY = mouseEvent.clientY - clientRect.top + scrollY;
+        var rowIndex = this.rowModel.getRowAtPixel(bodyY);
+        return rowIndex;
+    };
+    MouseEventService.prototype.getContainer = function (mouseEvent) {
+        var centerRect = this.gridPanel.getBodyViewportClientRect();
+        var mouseX = mouseEvent.clientX;
+        if (mouseX < centerRect.left && this.columnController.isPinningLeft()) {
+            return column_1.Column.PINNED_LEFT;
+        }
+        else if (mouseX > centerRect.right && this.columnController.isPinningRight()) {
+            return column_1.Column.PINNED_RIGHT;
+        }
+        else {
+            return null;
+        }
+    };
+    MouseEventService.prototype.getColumn = function (mouseEvent) {
+        if (this.columnController.isEmpty()) {
+            return null;
+        }
+        var container = this.getContainer(mouseEvent);
+        var columns = this.getColumnsForContainer(container);
+        var containerX = this.getXForContainer(container, mouseEvent);
+        var hoveringColumn;
+        if (containerX < 0) {
+            hoveringColumn = columns[0];
+        }
+        columns.forEach(function (column) {
+            var afterLeft = containerX >= column.getLeft();
+            var beforeRight = containerX <= column.getRight();
+            if (afterLeft && beforeRight) {
+                hoveringColumn = column;
+            }
+        });
+        if (!hoveringColumn) {
+            hoveringColumn = columns[columns.length - 1];
+        }
+        return hoveringColumn;
+    };
+    MouseEventService.prototype.getColumnsForContainer = function (container) {
+        switch (container) {
+            case column_1.Column.PINNED_LEFT: return this.columnController.getDisplayedLeftColumns();
+            case column_1.Column.PINNED_RIGHT: return this.columnController.getDisplayedRightColumns();
+            default: return this.columnController.getDisplayedCenterColumns();
+        }
+    };
+    MouseEventService.prototype.getXForContainer = function (container, mouseEvent) {
+        var containerX;
+        switch (container) {
+            case column_1.Column.PINNED_LEFT:
+                containerX = this.gridPanel.getPinnedLeftColsViewportClientRect().left;
+                break;
+            case column_1.Column.PINNED_RIGHT:
+                containerX = this.gridPanel.getPinnedRightColsViewportClientRect().left;
+                break;
+            default:
+                var centerRect = this.gridPanel.getBodyViewportClientRect();
+                var centerScroll = this.gridPanel.getHorizontalScrollPosition();
+                containerX = centerRect.left - centerScroll;
+        }
+        var result = mouseEvent.clientX - containerX;
+        return result;
+    };
+    __decorate([
+        context_2.Autowired('gridPanel'), 
+        __metadata('design:type', gridPanel_1.GridPanel)
+    ], MouseEventService.prototype, "gridPanel", void 0);
+    __decorate([
+        context_2.Autowired('columnController'), 
+        __metadata('design:type', columnController_1.ColumnController)
+    ], MouseEventService.prototype, "columnController", void 0);
+    __decorate([
+        context_2.Autowired('rowModel'), 
+        __metadata('design:type', Object)
+    ], MouseEventService.prototype, "rowModel", void 0);
+    __decorate([
+        context_2.Autowired('floatingRowModel'), 
+        __metadata('design:type', floatingRowModel_1.FloatingRowModel)
+    ], MouseEventService.prototype, "floatingRowModel", void 0);
+    __decorate([
+        context_2.Autowired('gridOptionsWrapper'), 
+        __metadata('design:type', gridOptionsWrapper_1.GridOptionsWrapper)
+    ], MouseEventService.prototype, "gridOptionsWrapper", void 0);
+    MouseEventService = __decorate([
+        context_1.Bean('mouseEventService'), 
+        __metadata('design:paramtypes', [])
+    ], MouseEventService);
+    return MouseEventService;
+})();
+exports.MouseEventService = MouseEventService;
diff --git a/dist/lib/headerRendering/cssClassApplier.d.ts b/dist/lib/headerRendering/cssClassApplier.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..296dc26093db4688f13ee51853cf963fc8baac11
--- /dev/null
+++ b/dist/lib/headerRendering/cssClassApplier.d.ts
@@ -0,0 +1,9 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { AbstractColDef } from "../entities/colDef";
+import { GridOptionsWrapper } from "../gridOptionsWrapper";
+export declare class CssClassApplier {
+    static addHeaderClassesFromCollDef(abstractColDef: AbstractColDef, eHeaderCell: HTMLElement, gridOptionsWrapper: GridOptionsWrapper): void;
+}
diff --git a/dist/lib/headerRendering/cssClassApplier.js b/dist/lib/headerRendering/cssClassApplier.js
new file mode 100644
index 0000000000000000000000000000000000000000..7cd946e1ddf9cd005b1920d160574e4f3f598053
--- /dev/null
+++ b/dist/lib/headerRendering/cssClassApplier.js
@@ -0,0 +1,41 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var utils_1 = require('../utils');
+var CssClassApplier = (function () {
+    function CssClassApplier() {
+    }
+    CssClassApplier.addHeaderClassesFromCollDef = function (abstractColDef, eHeaderCell, gridOptionsWrapper) {
+        if (abstractColDef && abstractColDef.headerClass) {
+            var classToUse;
+            if (typeof abstractColDef.headerClass === 'function') {
+                var params = {
+                    // bad naming, as colDef here can be a group or a column,
+                    // however most people won't appreciate the difference,
+                    // so keeping it as colDef to avoid confusion.
+                    colDef: abstractColDef,
+                    context: gridOptionsWrapper.getContext(),
+                    api: gridOptionsWrapper.getApi()
+                };
+                var headerClassFunc = abstractColDef.headerClass;
+                classToUse = headerClassFunc(params);
+            }
+            else {
+                classToUse = abstractColDef.headerClass;
+            }
+            if (typeof classToUse === 'string') {
+                utils_1.Utils.addCssClass(eHeaderCell, classToUse);
+            }
+            else if (Array.isArray(classToUse)) {
+                classToUse.forEach(function (cssClassItem) {
+                    utils_1.Utils.addCssClass(eHeaderCell, cssClassItem);
+                });
+            }
+        }
+    };
+    return CssClassApplier;
+})();
+exports.CssClassApplier = CssClassApplier;
diff --git a/dist/lib/headerRendering/headerContainer.d.ts b/dist/lib/headerRendering/headerContainer.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c37161e7a0b61bb70bcaebac479c13c1d38be3de
--- /dev/null
+++ b/dist/lib/headerRendering/headerContainer.d.ts
@@ -0,0 +1,26 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { Column } from "../entities/column";
+export declare class HeaderContainer {
+    private gridOptionsWrapper;
+    private context;
+    private $scope;
+    private dragAndDropService;
+    private columnController;
+    private gridPanel;
+    private eContainer;
+    private eViewport;
+    private eRoot;
+    private pinned;
+    private headerElements;
+    private dropTarget;
+    constructor(eContainer: HTMLElement, eViewport: HTMLElement, eRoot: HTMLElement, pinned: string);
+    init(): void;
+    removeAllChildren(): void;
+    insertHeaderRowsIntoContainer(): void;
+    private addTreeNodesAtDept(cellTree, dept, result);
+    private createHeaderElement(columnGroupChild);
+    onIndividualColumnResized(column: Column): void;
+}
diff --git a/dist/lib/headerRendering/headerContainer.js b/dist/lib/headerRendering/headerContainer.js
new file mode 100644
index 0000000000000000000000000000000000000000..79023b3689a230f40f9f8e36ec5b926809e47d55
--- /dev/null
+++ b/dist/lib/headerRendering/headerContainer.js
@@ -0,0 +1,165 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var utils_1 = require('../utils');
+var columnGroup_1 = require("../entities/columnGroup");
+var gridOptionsWrapper_1 = require("../gridOptionsWrapper");
+var context_1 = require("../context/context");
+var column_1 = require("../entities/column");
+var context_2 = require("../context/context");
+var renderedHeaderGroupCell_1 = require("./renderedHeaderGroupCell");
+var renderedHeaderCell_1 = require("./renderedHeaderCell");
+var dragAndDropService_1 = require("../dragAndDrop/dragAndDropService");
+var moveColumnController_1 = require("./moveColumnController");
+var columnController_1 = require("../columnController/columnController");
+var gridPanel_1 = require("../gridPanel/gridPanel");
+var context_3 = require("../context/context");
+var HeaderContainer = (function () {
+    function HeaderContainer(eContainer, eViewport, eRoot, pinned) {
+        this.headerElements = [];
+        this.eContainer = eContainer;
+        this.eRoot = eRoot;
+        this.pinned = pinned;
+        this.eViewport = eViewport;
+    }
+    HeaderContainer.prototype.init = function () {
+        var moveColumnController = new moveColumnController_1.MoveColumnController(this.pinned);
+        this.context.wireBean(moveColumnController);
+        var secondaryContainers;
+        switch (this.pinned) {
+            case column_1.Column.PINNED_LEFT:
+                secondaryContainers = this.gridPanel.getDropTargetLeftContainers();
+                break;
+            case column_1.Column.PINNED_RIGHT:
+                secondaryContainers = this.gridPanel.getDropTargetPinnedRightContainers();
+                break;
+            default:
+                secondaryContainers = this.gridPanel.getDropTargetBodyContainers();
+                break;
+        }
+        var icon = this.pinned ? dragAndDropService_1.DragAndDropService.ICON_PINNED : dragAndDropService_1.DragAndDropService.ICON_MOVE;
+        this.dropTarget = {
+            eContainer: this.eViewport ? this.eViewport : this.eContainer,
+            iconName: icon,
+            eSecondaryContainers: secondaryContainers,
+            onDragging: moveColumnController.onDragging.bind(moveColumnController),
+            onDragEnter: moveColumnController.onDragEnter.bind(moveColumnController),
+            onDragLeave: moveColumnController.onDragLeave.bind(moveColumnController),
+            onDragStop: moveColumnController.onDragStop.bind(moveColumnController)
+        };
+        this.dragAndDropService.addDropTarget(this.dropTarget);
+    };
+    HeaderContainer.prototype.removeAllChildren = function () {
+        this.headerElements.forEach(function (headerElement) {
+            headerElement.destroy();
+        });
+        this.headerElements.length = 0;
+        utils_1.Utils.removeAllChildren(this.eContainer);
+    };
+    HeaderContainer.prototype.insertHeaderRowsIntoContainer = function () {
+        var _this = this;
+        var cellTree = this.columnController.getDisplayedColumnGroups(this.pinned);
+        // if we are displaying header groups, then we have many rows here.
+        // go through each row of the header, one by one.
+        var rowHeight = this.gridOptionsWrapper.getHeaderHeight();
+        for (var dept = 0;; dept++) {
+            var nodesAtDept = [];
+            this.addTreeNodesAtDept(cellTree, dept, nodesAtDept);
+            // we want to break the for loop when we get to an empty set of cells,
+            // that's how we know we have finished rendering the last row.
+            if (nodesAtDept.length === 0) {
+                break;
+            }
+            var eRow = document.createElement('div');
+            eRow.className = 'ag-header-row';
+            eRow.style.top = (dept * rowHeight) + 'px';
+            eRow.style.height = rowHeight + 'px';
+            nodesAtDept.forEach(function (child) {
+                // skip groups that have no displayed children. this can happen when the group is broken,
+                // and this section happens to have nothing to display for the open / closed state
+                if (child instanceof columnGroup_1.ColumnGroup && child.getDisplayedChildren().length == 0) {
+                    return;
+                }
+                var renderedHeaderElement = _this.createHeaderElement(child);
+                _this.headerElements.push(renderedHeaderElement);
+                var eGui = renderedHeaderElement.getGui();
+                eRow.appendChild(eGui);
+            });
+            this.eContainer.appendChild(eRow);
+        }
+    };
+    HeaderContainer.prototype.addTreeNodesAtDept = function (cellTree, dept, result) {
+        var _this = this;
+        cellTree.forEach(function (abstractColumn) {
+            if (dept === 0) {
+                result.push(abstractColumn);
+            }
+            else if (abstractColumn instanceof columnGroup_1.ColumnGroup) {
+                var columnGroup = abstractColumn;
+                _this.addTreeNodesAtDept(columnGroup.getDisplayedChildren(), dept - 1, result);
+            }
+            else {
+            }
+        });
+    };
+    HeaderContainer.prototype.createHeaderElement = function (columnGroupChild) {
+        var result;
+        if (columnGroupChild instanceof columnGroup_1.ColumnGroup) {
+            result = new renderedHeaderGroupCell_1.RenderedHeaderGroupCell(columnGroupChild, this.eRoot, this.$scope);
+        }
+        else {
+            result = new renderedHeaderCell_1.RenderedHeaderCell(columnGroupChild, this.$scope, this.eRoot, this.dropTarget);
+        }
+        this.context.wireBean(result);
+        return result;
+    };
+    HeaderContainer.prototype.onIndividualColumnResized = function (column) {
+        this.headerElements.forEach(function (headerElement) {
+            headerElement.onIndividualColumnResized(column);
+        });
+    };
+    __decorate([
+        context_1.Autowired('gridOptionsWrapper'), 
+        __metadata('design:type', gridOptionsWrapper_1.GridOptionsWrapper)
+    ], HeaderContainer.prototype, "gridOptionsWrapper", void 0);
+    __decorate([
+        context_1.Autowired('context'), 
+        __metadata('design:type', context_2.Context)
+    ], HeaderContainer.prototype, "context", void 0);
+    __decorate([
+        context_1.Autowired('$scope'), 
+        __metadata('design:type', Object)
+    ], HeaderContainer.prototype, "$scope", void 0);
+    __decorate([
+        context_1.Autowired('dragAndDropService'), 
+        __metadata('design:type', dragAndDropService_1.DragAndDropService)
+    ], HeaderContainer.prototype, "dragAndDropService", void 0);
+    __decorate([
+        context_1.Autowired('columnController'), 
+        __metadata('design:type', columnController_1.ColumnController)
+    ], HeaderContainer.prototype, "columnController", void 0);
+    __decorate([
+        context_1.Autowired('gridPanel'), 
+        __metadata('design:type', gridPanel_1.GridPanel)
+    ], HeaderContainer.prototype, "gridPanel", void 0);
+    __decorate([
+        context_3.PostConstruct, 
+        __metadata('design:type', Function), 
+        __metadata('design:paramtypes', []), 
+        __metadata('design:returntype', void 0)
+    ], HeaderContainer.prototype, "init", null);
+    return HeaderContainer;
+})();
+exports.HeaderContainer = HeaderContainer;
diff --git a/dist/lib/headerRendering/horizontalDragService.d.ts b/dist/lib/headerRendering/horizontalDragService.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cd163dc939449a9a5d4c63cab36205fba3418455
--- /dev/null
+++ b/dist/lib/headerRendering/horizontalDragService.d.ts
@@ -0,0 +1,16 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+/** need to get this class to use the dragService, so no duplication */
+export interface DragServiceParams {
+    eDraggableElement: Element;
+    eBody: HTMLElement;
+    cursor: string;
+    startAfterPixels: number;
+    onDragStart: (event?: MouseEvent) => void;
+    onDragging: (delta: number, finished: boolean) => void;
+}
+export declare class HorizontalDragService {
+    addDragHandling(params: DragServiceParams): void;
+}
diff --git a/dist/lib/headerRendering/horizontalDragService.js b/dist/lib/headerRendering/horizontalDragService.js
new file mode 100644
index 0000000000000000000000000000000000000000..de16f0b3bc61afe261550f383c4072cdce175d24
--- /dev/null
+++ b/dist/lib/headerRendering/horizontalDragService.js
@@ -0,0 +1,100 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var context_1 = require("../context/context");
+var HorizontalDragService = (function () {
+    function HorizontalDragService() {
+    }
+    HorizontalDragService.prototype.addDragHandling = function (params) {
+        params.eDraggableElement.addEventListener('mousedown', function (startEvent) {
+            new DragInstance(params, startEvent);
+        });
+    };
+    HorizontalDragService = __decorate([
+        context_1.Bean('horizontalDragService'), 
+        __metadata('design:paramtypes', [])
+    ], HorizontalDragService);
+    return HorizontalDragService;
+})();
+exports.HorizontalDragService = HorizontalDragService;
+var DragInstance = (function () {
+    function DragInstance(params, startEvent) {
+        this.mouseMove = this.onMouseMove.bind(this);
+        this.mouseUp = this.onMouseUp.bind(this);
+        this.mouseLeave = this.onMouseLeave.bind(this);
+        this.lastDelta = 0;
+        this.params = params;
+        this.eDragParent = document.querySelector('body');
+        this.dragStartX = startEvent.clientX;
+        this.startEvent = startEvent;
+        this.eDragParent.addEventListener('mousemove', this.mouseMove);
+        this.eDragParent.addEventListener('mouseup', this.mouseUp);
+        this.eDragParent.addEventListener('mouseleave', this.mouseLeave);
+        this.draggingStarted = false;
+        var startAfterPixelsExist = typeof params.startAfterPixels === 'number' && params.startAfterPixels > 0;
+        if (!startAfterPixelsExist) {
+            this.startDragging();
+        }
+    }
+    DragInstance.prototype.startDragging = function () {
+        this.draggingStarted = true;
+        this.oldBodyCursor = this.params.eBody.style.cursor;
+        this.oldParentCursor = this.eDragParent.style.cursor;
+        this.oldMsUserSelect = this.eDragParent.style.msUserSelect;
+        this.oldWebkitUserSelect = this.eDragParent.style.webkitUserSelect;
+        // change the body cursor, so when drag moves out of the drag bar, the cursor is still 'resize' (or 'move'
+        this.params.eBody.style.cursor = this.params.cursor;
+        // same for outside the grid, we want to keep the resize (or move) cursor
+        this.eDragParent.style.cursor = this.params.cursor;
+        // we don't want text selection outside the grid (otherwise it looks weird as text highlights when we move)
+        this.eDragParent.style.msUserSelect = 'none';
+        this.eDragParent.style.webkitUserSelect = 'none';
+        this.params.onDragStart(this.startEvent);
+    };
+    DragInstance.prototype.onMouseMove = function (moveEvent) {
+        var newX = moveEvent.clientX;
+        this.lastDelta = newX - this.dragStartX;
+        if (!this.draggingStarted) {
+            var dragExceededStartAfterPixels = Math.abs(this.lastDelta) >= this.params.startAfterPixels;
+            if (dragExceededStartAfterPixels) {
+                this.startDragging();
+            }
+        }
+        if (this.draggingStarted) {
+            this.params.onDragging(this.lastDelta, false);
+        }
+    };
+    DragInstance.prototype.onMouseUp = function () {
+        this.stopDragging();
+    };
+    DragInstance.prototype.onMouseLeave = function () {
+        this.stopDragging();
+    };
+    DragInstance.prototype.stopDragging = function () {
+        // reset cursor back to original cursor, if they were changed in the first place
+        if (this.draggingStarted) {
+            this.params.eBody.style.cursor = this.oldBodyCursor;
+            this.eDragParent.style.cursor = this.oldParentCursor;
+            this.eDragParent.style.msUserSelect = this.oldMsUserSelect;
+            this.eDragParent.style.webkitUserSelect = this.oldWebkitUserSelect;
+            this.params.onDragging(this.lastDelta, true);
+        }
+        // always remove the listeners, as these are always added
+        this.eDragParent.removeEventListener('mousemove', this.mouseMove);
+        this.eDragParent.removeEventListener('mouseup', this.mouseUp);
+        this.eDragParent.removeEventListener('mouseleave', this.mouseLeave);
+    };
+    return DragInstance;
+})();
diff --git a/dist/lib/headerRendering/iRenderedHeaderElement.d.ts b/dist/lib/headerRendering/iRenderedHeaderElement.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d38b9c49b070cfdd9efe69f0936ffd3a53d99298
--- /dev/null
+++ b/dist/lib/headerRendering/iRenderedHeaderElement.d.ts
@@ -0,0 +1,10 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { Column } from "../entities/column";
+export interface IRenderedHeaderElement {
+    destroy(): void;
+    onIndividualColumnResized(column: Column): void;
+    getGui(): HTMLElement;
+}
diff --git a/dist/lib/headerRendering/iRenderedHeaderElement.js b/dist/lib/headerRendering/iRenderedHeaderElement.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b3773483b8b47d6e09d3aa0c5cd9256ffbfd1ed
--- /dev/null
+++ b/dist/lib/headerRendering/iRenderedHeaderElement.js
@@ -0,0 +1,7 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+
diff --git a/dist/lib/headerRendering/standardMenu.d.ts b/dist/lib/headerRendering/standardMenu.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fbebf2ef8f229a813ba9c73ed71019b49039a95e
--- /dev/null
+++ b/dist/lib/headerRendering/standardMenu.d.ts
@@ -0,0 +1,13 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { IMenuFactory } from "../interfaces/iMenuFactory";
+import { Column } from "../entities/column";
+export declare class StandardMenuFactory implements IMenuFactory {
+    private filterManager;
+    private popupService;
+    private gridOptionsWrapper;
+    showMenu(column: Column, eventSource: HTMLElement): void;
+    isMenuEnabled(column: Column): boolean;
+}
diff --git a/dist/lib/headerRendering/standardMenu.js b/dist/lib/headerRendering/standardMenu.js
new file mode 100644
index 0000000000000000000000000000000000000000..9d2f014c34013a8f89487e0492b7165f3865fd14
--- /dev/null
+++ b/dist/lib/headerRendering/standardMenu.js
@@ -0,0 +1,64 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var context_1 = require("../context/context");
+var filterManager_1 = require("../filter/filterManager");
+var utils_1 = require('../utils');
+var context_2 = require("../context/context");
+var popupService_1 = require("../widgets/popupService");
+var gridOptionsWrapper_1 = require("../gridOptionsWrapper");
+var StandardMenuFactory = (function () {
+    function StandardMenuFactory() {
+    }
+    StandardMenuFactory.prototype.showMenu = function (column, eventSource) {
+        var filterWrapper = this.filterManager.getOrCreateFilterWrapper(column);
+        var eMenu = document.createElement('div');
+        utils_1.Utils.addCssClass(eMenu, 'ag-menu');
+        eMenu.appendChild(filterWrapper.gui);
+        // need to show filter before positioning, as only after filter
+        // is visible can we find out what the width of it is
+        var hidePopup = this.popupService.addAsModalPopup(eMenu, true);
+        this.popupService.positionPopupUnderComponent({ eventSource: eventSource, ePopup: eMenu, keepWithinBounds: true });
+        if (filterWrapper.filter.afterGuiAttached) {
+            var params = {
+                hidePopup: hidePopup,
+                eventSource: eventSource
+            };
+            filterWrapper.filter.afterGuiAttached(params);
+        }
+    };
+    StandardMenuFactory.prototype.isMenuEnabled = function (column) {
+        // for standard, we show menu if filter is enabled, and he menu is not suppressed
+        return this.gridOptionsWrapper.isEnableFilter();
+    };
+    __decorate([
+        context_2.Autowired('filterManager'), 
+        __metadata('design:type', filterManager_1.FilterManager)
+    ], StandardMenuFactory.prototype, "filterManager", void 0);
+    __decorate([
+        context_2.Autowired('popupService'), 
+        __metadata('design:type', popupService_1.PopupService)
+    ], StandardMenuFactory.prototype, "popupService", void 0);
+    __decorate([
+        context_2.Autowired('gridOptionsWrapper'), 
+        __metadata('design:type', gridOptionsWrapper_1.GridOptionsWrapper)
+    ], StandardMenuFactory.prototype, "gridOptionsWrapper", void 0);
+    StandardMenuFactory = __decorate([
+        context_1.Bean('menuFactory'), 
+        __metadata('design:paramtypes', [])
+    ], StandardMenuFactory);
+    return StandardMenuFactory;
+})();
+exports.StandardMenuFactory = StandardMenuFactory;
diff --git a/dist/lib/interfaces/IMenuFactory.d.ts b/dist/lib/interfaces/IMenuFactory.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..49058f2bd0774205a1f4bbb295ecd7289ee5d83c
--- /dev/null
+++ b/dist/lib/interfaces/IMenuFactory.d.ts
@@ -0,0 +1,9 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { Column } from "../entities/column";
+export interface IMenuFactory {
+    showMenu(column: Column, eventSource: HTMLElement): void;
+    isMenuEnabled(column: Column): boolean;
+}
diff --git a/dist/lib/interfaces/IMenuFactory.js b/dist/lib/interfaces/IMenuFactory.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b3773483b8b47d6e09d3aa0c5cd9256ffbfd1ed
--- /dev/null
+++ b/dist/lib/interfaces/IMenuFactory.js
@@ -0,0 +1,7 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+
diff --git a/dist/lib/interfaces/iClipboardService.d.ts b/dist/lib/interfaces/iClipboardService.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ba5da1084405b8ece1a08da2dd07cdd8c5b5a0b7
--- /dev/null
+++ b/dist/lib/interfaces/iClipboardService.d.ts
@@ -0,0 +1,8 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+export interface IClipboardService {
+    pasteFromClipboard(): void;
+    copyToClipboard(): void;
+}
diff --git a/dist/lib/interfaces/iClipboardService.js b/dist/lib/interfaces/iClipboardService.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b3773483b8b47d6e09d3aa0c5cd9256ffbfd1ed
--- /dev/null
+++ b/dist/lib/interfaces/iClipboardService.js
@@ -0,0 +1,7 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+
diff --git a/dist/lib/interfaces/iContextMenuFactory.d.ts b/dist/lib/interfaces/iContextMenuFactory.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4e112fb8f4aef93798e2685b27519c6ddb51ee1f
--- /dev/null
+++ b/dist/lib/interfaces/iContextMenuFactory.d.ts
@@ -0,0 +1,9 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { Column } from "../entities/column";
+import { RowNode } from "../entities/rowNode";
+export interface IContextMenuFactory {
+    showMenu(node: RowNode, column: Column, value: any, mouseEvent: MouseEvent): void;
+}
diff --git a/dist/lib/interfaces/iContextMenuFactory.js b/dist/lib/interfaces/iContextMenuFactory.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b3773483b8b47d6e09d3aa0c5cd9256ffbfd1ed
--- /dev/null
+++ b/dist/lib/interfaces/iContextMenuFactory.js
@@ -0,0 +1,7 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+
diff --git a/dist/lib/interfaces/iMenu.d.ts b/dist/lib/interfaces/iMenu.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..840a1bd3f57b4625408a6575e5443066d398d0c0
--- /dev/null
+++ b/dist/lib/interfaces/iMenu.d.ts
@@ -0,0 +1,6 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+export interface IMenu {
+}
diff --git a/dist/lib/interfaces/iMenu.js b/dist/lib/interfaces/iMenu.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b3773483b8b47d6e09d3aa0c5cd9256ffbfd1ed
--- /dev/null
+++ b/dist/lib/interfaces/iMenu.js
@@ -0,0 +1,7 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+
diff --git a/dist/lib/interfaces/iRangeController.d.ts b/dist/lib/interfaces/iRangeController.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4050bd63bf6c455df10a7aa2cc73f281a8c1b6b4
--- /dev/null
+++ b/dist/lib/interfaces/iRangeController.d.ts
@@ -0,0 +1,32 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { Column } from "../entities/column";
+import { ColDef } from "../entities/colDef";
+import { GridCell } from "../entities/gridCell";
+export interface IRangeController {
+    clearSelection(): void;
+    getCellRangeCount(cell: GridCell): number;
+    isCellInAnyRange(cell: GridCell): boolean;
+    onDragStart(mouseEvent: MouseEvent): void;
+    onDragStop(): void;
+    onDragging(mouseEvent: MouseEvent): void;
+    getCellRanges(): RangeSelection[];
+    setRangeToCell(cell: GridCell): void;
+    setRange(rangeSelection: AddRangeSelectionParams): void;
+    addRange(rangeSelection: AddRangeSelectionParams): void;
+}
+export interface RangeSelection {
+    start: GridCell;
+    end: GridCell;
+    columns: Column[];
+}
+export interface AddRangeSelectionParams {
+    rowStart: number;
+    floatingStart: string;
+    rowEnd: number;
+    floatingEnd: string;
+    columnStart: Column | ColDef | string;
+    columnEnd: Column | ColDef | string;
+}
diff --git a/dist/lib/interfaces/iRangeController.js b/dist/lib/interfaces/iRangeController.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b3773483b8b47d6e09d3aa0c5cd9256ffbfd1ed
--- /dev/null
+++ b/dist/lib/interfaces/iRangeController.js
@@ -0,0 +1,7 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+
diff --git a/dist/lib/interfaces/iRowModel.d.ts b/dist/lib/interfaces/iRowModel.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c764cbd844a7c8b621ccddbc6f3e9f159f310f0b
--- /dev/null
+++ b/dist/lib/interfaces/iRowModel.d.ts
@@ -0,0 +1,21 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { RowNode } from "../entities/rowNode";
+export interface IRowModel {
+    getTopLevelNodes(): RowNode[];
+    getRow(index: number): RowNode;
+    getRowCount(): number;
+    getRowAtPixel(pixel: number): number;
+    getRowCombinedHeight(): number;
+    isEmpty(): boolean;
+    isRowsToRender(): boolean;
+    refreshModel(step: number, fromIndex?: number): void;
+    forEachNode(callback: (rowNode: RowNode) => void): void;
+    forEachNodeAfterFilter(callback: (rowNode: RowNode) => void): void;
+    forEachNodeAfterFilterAndSort(callback: (rowNode: RowNode) => void): void;
+    expandOrCollapseAll(expand: boolean): void;
+    setRowData(rows: any[], refresh: boolean, firstId?: number): void;
+    setDatasource(datasource: any): void;
+}
diff --git a/dist/lib/interfaces/iRowModel.js b/dist/lib/interfaces/iRowModel.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b3773483b8b47d6e09d3aa0c5cd9256ffbfd1ed
--- /dev/null
+++ b/dist/lib/interfaces/iRowModel.js
@@ -0,0 +1,7 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+
diff --git a/dist/lib/interfaces/iRowNodeStage.d.ts b/dist/lib/interfaces/iRowNodeStage.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4c05b8cef8c7679f321c9dcf09974aaa50981f3b
--- /dev/null
+++ b/dist/lib/interfaces/iRowNodeStage.d.ts
@@ -0,0 +1,8 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { RowNode } from "../entities/rowNode";
+export interface IRowNodeStage {
+    execute(rowNodes: RowNode[]): RowNode[];
+}
diff --git a/dist/lib/interfaces/iRowNodeStage.js b/dist/lib/interfaces/iRowNodeStage.js
new file mode 100644
index 0000000000000000000000000000000000000000..3b3773483b8b47d6e09d3aa0c5cd9256ffbfd1ed
--- /dev/null
+++ b/dist/lib/interfaces/iRowNodeStage.js
@@ -0,0 +1,7 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+
diff --git a/dist/lib/layout/tabbedLayout.d.ts b/dist/lib/layout/tabbedLayout.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ee23328eac5b238c9cc550062a26a926ef5cee7
--- /dev/null
+++ b/dist/lib/layout/tabbedLayout.d.ts
@@ -0,0 +1,31 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+export declare class TabbedLayout {
+    private eGui;
+    private eHeader;
+    private eBody;
+    private params;
+    private static TEMPLATE;
+    private items;
+    private activeItem;
+    constructor(params: TabbedLayoutParams);
+    getMinWidth(): number;
+    showFirstItem(): void;
+    private addItem(item);
+    showItem(tabbedItem: TabbedItem): void;
+    private showItemWrapper(wrapper);
+    getGui(): HTMLElement;
+}
+export interface TabbedLayoutParams {
+    items: TabbedItem[];
+    cssClass?: string;
+    onItemClicked?: Function;
+    onActiveItemClicked?: Function;
+}
+export interface TabbedItem {
+    title: Element;
+    body: HTMLElement;
+    afterAttachedCallback?: Function;
+}
diff --git a/dist/lib/layout/tabbedLayout.js b/dist/lib/layout/tabbedLayout.js
new file mode 100644
index 0000000000000000000000000000000000000000..7d17038640fbeb1253e12aa58547184b7e7489b5
--- /dev/null
+++ b/dist/lib/layout/tabbedLayout.js
@@ -0,0 +1,94 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var utils_1 = require('../utils');
+var TabbedLayout = (function () {
+    function TabbedLayout(params) {
+        var _this = this;
+        this.items = [];
+        this.params = params;
+        this.eGui = document.createElement('div');
+        this.eGui.innerHTML = TabbedLayout.TEMPLATE;
+        this.eHeader = this.eGui.querySelector('#tabHeader');
+        this.eBody = this.eGui.querySelector('#tabBody');
+        utils_1.Utils.addCssClass(this.eGui, params.cssClass);
+        if (params.items) {
+            params.items.forEach(function (item) { return _this.addItem(item); });
+        }
+    }
+    TabbedLayout.prototype.getMinWidth = function () {
+        var eDummyContainer = document.createElement('span');
+        // position fixed, so it isn't restricted to the boundaries of the parent
+        eDummyContainer.style.position = 'fixed';
+        // we put the dummy into the body container, so it will inherit all the
+        // css styles that the real cells are inheriting
+        this.eGui.appendChild(eDummyContainer);
+        var minWidth = 0;
+        this.items.forEach(function (itemWrapper) {
+            utils_1.Utils.removeAllChildren(eDummyContainer);
+            var eClone = itemWrapper.tabbedItem.body.cloneNode(true);
+            eDummyContainer.appendChild(eClone);
+            if (minWidth < eDummyContainer.offsetWidth) {
+                minWidth = eDummyContainer.offsetWidth;
+            }
+        });
+        this.eGui.removeChild(eDummyContainer);
+        return minWidth;
+    };
+    TabbedLayout.prototype.showFirstItem = function () {
+        if (this.items.length > 0) {
+            this.showItemWrapper(this.items[0]);
+        }
+    };
+    TabbedLayout.prototype.addItem = function (item) {
+        var eHeaderButton = document.createElement('span');
+        eHeaderButton.appendChild(item.title);
+        utils_1.Utils.addCssClass(eHeaderButton, 'ag-tab');
+        this.eHeader.appendChild(eHeaderButton);
+        var wrapper = {
+            tabbedItem: item,
+            eHeaderButton: eHeaderButton
+        };
+        this.items.push(wrapper);
+        eHeaderButton.addEventListener('click', this.showItemWrapper.bind(this, wrapper));
+    };
+    TabbedLayout.prototype.showItem = function (tabbedItem) {
+        var itemWrapper = utils_1.Utils.find(this.items, function (itemWrapper) {
+            return itemWrapper.tabbedItem === tabbedItem;
+        });
+        if (itemWrapper) {
+            this.showItemWrapper(itemWrapper);
+        }
+    };
+    TabbedLayout.prototype.showItemWrapper = function (wrapper) {
+        if (this.params.onItemClicked) {
+            this.params.onItemClicked({ item: wrapper.tabbedItem });
+        }
+        if (this.activeItem === wrapper) {
+            utils_1.Utils.callIfPresent(this.params.onActiveItemClicked);
+            return;
+        }
+        utils_1.Utils.removeAllChildren(this.eBody);
+        this.eBody.appendChild(wrapper.tabbedItem.body);
+        if (this.activeItem) {
+            utils_1.Utils.removeCssClass(this.activeItem.eHeaderButton, 'ag-tab-selected');
+        }
+        utils_1.Utils.addCssClass(wrapper.eHeaderButton, 'ag-tab-selected');
+        this.activeItem = wrapper;
+        if (wrapper.tabbedItem.afterAttachedCallback) {
+            wrapper.tabbedItem.afterAttachedCallback();
+        }
+    };
+    TabbedLayout.prototype.getGui = function () {
+        return this.eGui;
+    };
+    TabbedLayout.TEMPLATE = '<div>' +
+        '<div id="tabHeader" class="ag-tab-header"></div>' +
+        '<div id="tabBody" class="ag-tab-body"></div>' +
+        '</div>';
+    return TabbedLayout;
+})();
+exports.TabbedLayout = TabbedLayout;
diff --git a/dist/lib/rowControllers/inMemory/fillterStage.d.ts b/dist/lib/rowControllers/inMemory/fillterStage.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..330de89dcc4c3e7e21577331eff8e43fa8296ecf
--- /dev/null
+++ b/dist/lib/rowControllers/inMemory/fillterStage.d.ts
@@ -0,0 +1,14 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { RowNode } from "../../entities/rowNode";
+import { IRowNodeStage } from "../../interfaces/iRowNodeStage";
+export declare class FilterStage implements IRowNodeStage {
+    private gridOptionsWrapper;
+    private filterManager;
+    execute(rowsToFilter: RowNode[]): RowNode[];
+    private filterItems(rowNodes);
+    private recursivelyResetFilter(nodes);
+    private getTotalChildCount(rowNodes);
+}
diff --git a/dist/lib/rowControllers/inMemory/fillterStage.js b/dist/lib/rowControllers/inMemory/fillterStage.js
new file mode 100644
index 0000000000000000000000000000000000000000..65aaafa5cc25f73f86017cd9ddb577942579879f
--- /dev/null
+++ b/dist/lib/rowControllers/inMemory/fillterStage.js
@@ -0,0 +1,102 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var context_1 = require("../../context/context");
+var context_2 = require("../../context/context");
+var gridOptionsWrapper_1 = require("../../gridOptionsWrapper");
+var filterManager_1 = require("../../filter/filterManager");
+var FilterStage = (function () {
+    function FilterStage() {
+    }
+    FilterStage.prototype.execute = function (rowsToFilter) {
+        var filterActive;
+        if (this.gridOptionsWrapper.isEnableServerSideFilter()) {
+            filterActive = false;
+        }
+        else {
+            filterActive = this.filterManager.isAnyFilterPresent();
+        }
+        var result;
+        if (filterActive) {
+            result = this.filterItems(rowsToFilter);
+        }
+        else {
+            // do it here
+            result = rowsToFilter;
+            this.recursivelyResetFilter(rowsToFilter);
+        }
+        return result;
+    };
+    FilterStage.prototype.filterItems = function (rowNodes) {
+        var result = [];
+        for (var i = 0, l = rowNodes.length; i < l; i++) {
+            var node = rowNodes[i];
+            if (node.group) {
+                // deal with group
+                node.childrenAfterFilter = this.filterItems(node.children);
+                if (node.childrenAfterFilter.length > 0) {
+                    node.allChildrenCount = this.getTotalChildCount(node.childrenAfterFilter);
+                    result.push(node);
+                }
+            }
+            else {
+                if (this.filterManager.doesRowPassFilter(node)) {
+                    result.push(node);
+                }
+            }
+        }
+        return result;
+    };
+    FilterStage.prototype.recursivelyResetFilter = function (nodes) {
+        if (!nodes) {
+            return;
+        }
+        for (var i = 0, l = nodes.length; i < l; i++) {
+            var node = nodes[i];
+            if (node.group && node.children) {
+                node.childrenAfterFilter = node.children;
+                this.recursivelyResetFilter(node.children);
+                node.allChildrenCount = this.getTotalChildCount(node.childrenAfterFilter);
+            }
+        }
+    };
+    FilterStage.prototype.getTotalChildCount = function (rowNodes) {
+        var count = 0;
+        for (var i = 0, l = rowNodes.length; i < l; i++) {
+            var item = rowNodes[i];
+            if (item.group) {
+                count += item.allChildrenCount;
+            }
+            else {
+                count++;
+            }
+        }
+        return count;
+    };
+    __decorate([
+        context_2.Autowired('gridOptionsWrapper'), 
+        __metadata('design:type', gridOptionsWrapper_1.GridOptionsWrapper)
+    ], FilterStage.prototype, "gridOptionsWrapper", void 0);
+    __decorate([
+        context_2.Autowired('filterManager'), 
+        __metadata('design:type', filterManager_1.FilterManager)
+    ], FilterStage.prototype, "filterManager", void 0);
+    FilterStage = __decorate([
+        context_1.Bean('filterStage'), 
+        __metadata('design:paramtypes', [])
+    ], FilterStage);
+    return FilterStage;
+})();
+exports.FilterStage = FilterStage;
diff --git a/dist/lib/rowControllers/inMemory/flattenStage.d.ts b/dist/lib/rowControllers/inMemory/flattenStage.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..182e5f045d8616e137fb1fd8e09f81a9ba0e5c27
--- /dev/null
+++ b/dist/lib/rowControllers/inMemory/flattenStage.d.ts
@@ -0,0 +1,15 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { RowNode } from "../../entities/rowNode";
+import { IRowNodeStage } from "../../interfaces/iRowNodeStage";
+export declare class FlattenStage implements IRowNodeStage {
+    private gridOptionsWrapper;
+    private selectionController;
+    private eventService;
+    execute(rowsToFlatten: RowNode[]): RowNode[];
+    private recursivelyAddToRowsToDisplay(rowsToFlatten, result, nextRowTop);
+    private addRowNodeToRowsToDisplay(rowNode, result, nextRowTop);
+    private createFooterNode(groupNode);
+}
diff --git a/dist/lib/rowControllers/inMemory/flattenStage.js b/dist/lib/rowControllers/inMemory/flattenStage.js
new file mode 100644
index 0000000000000000000000000000000000000000..02d6df5ded80b8a65779453789c82052f3832204
--- /dev/null
+++ b/dist/lib/rowControllers/inMemory/flattenStage.js
@@ -0,0 +1,94 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var context_1 = require("../../context/context");
+var rowNode_1 = require("../../entities/rowNode");
+var utils_1 = require('../../utils');
+var gridOptionsWrapper_1 = require("../../gridOptionsWrapper");
+var context_2 = require("../../context/context");
+var selectionController_1 = require("../../selectionController");
+var eventService_1 = require("../../eventService");
+var FlattenStage = (function () {
+    function FlattenStage() {
+    }
+    FlattenStage.prototype.execute = function (rowsToFlatten) {
+        // even if not doing grouping, we do the mapping, as the client might
+        // of passed in data that already has a grouping in it somewhere
+        var result = [];
+        // putting value into a wrapper so it's passed by reference
+        var nextRowTop = { value: 0 };
+        this.recursivelyAddToRowsToDisplay(rowsToFlatten, result, nextRowTop);
+        return result;
+    };
+    FlattenStage.prototype.recursivelyAddToRowsToDisplay = function (rowsToFlatten, result, nextRowTop) {
+        if (utils_1.Utils.missingOrEmpty(rowsToFlatten)) {
+            return;
+        }
+        var groupSuppressRow = this.gridOptionsWrapper.isGroupSuppressRow();
+        for (var i = 0; i < rowsToFlatten.length; i++) {
+            var rowNode = rowsToFlatten[i];
+            var skipGroupNode = groupSuppressRow && rowNode.group;
+            if (!skipGroupNode) {
+                this.addRowNodeToRowsToDisplay(rowNode, result, nextRowTop);
+            }
+            if (rowNode.group && rowNode.expanded) {
+                this.recursivelyAddToRowsToDisplay(rowNode.childrenAfterSort, result, nextRowTop);
+                // put a footer in if user is looking for it
+                if (this.gridOptionsWrapper.isGroupIncludeFooter()) {
+                    var footerNode = this.createFooterNode(rowNode);
+                    this.addRowNodeToRowsToDisplay(footerNode, result, nextRowTop);
+                }
+            }
+        }
+    };
+    // duplicated method, it's also in floatingRowModel
+    FlattenStage.prototype.addRowNodeToRowsToDisplay = function (rowNode, result, nextRowTop) {
+        result.push(rowNode);
+        rowNode.rowHeight = this.gridOptionsWrapper.getRowHeightForNode(rowNode);
+        rowNode.rowTop = nextRowTop.value;
+        nextRowTop.value += rowNode.rowHeight;
+    };
+    FlattenStage.prototype.createFooterNode = function (groupNode) {
+        var footerNode = new rowNode_1.RowNode(this.eventService, this.gridOptionsWrapper, this.selectionController);
+        Object.keys(groupNode).forEach(function (key) {
+            footerNode[key] = groupNode[key];
+        });
+        footerNode.footer = true;
+        // get both header and footer to reference each other as siblings. this is never undone,
+        // only overwritten. so if a group is expanded, then contracted, it will have a ghost
+        // sibling - but that's fine, as we can ignore this if the header is contracted.
+        footerNode.sibling = groupNode;
+        groupNode.sibling = footerNode;
+        return footerNode;
+    };
+    __decorate([
+        context_2.Autowired('gridOptionsWrapper'), 
+        __metadata('design:type', gridOptionsWrapper_1.GridOptionsWrapper)
+    ], FlattenStage.prototype, "gridOptionsWrapper", void 0);
+    __decorate([
+        context_2.Autowired('selectionController'), 
+        __metadata('design:type', selectionController_1.SelectionController)
+    ], FlattenStage.prototype, "selectionController", void 0);
+    __decorate([
+        context_2.Autowired('eventService'), 
+        __metadata('design:type', eventService_1.EventService)
+    ], FlattenStage.prototype, "eventService", void 0);
+    FlattenStage = __decorate([
+        context_1.Bean('flattenStage'), 
+        __metadata('design:paramtypes', [])
+    ], FlattenStage);
+    return FlattenStage;
+})();
+exports.FlattenStage = FlattenStage;
diff --git a/dist/lib/rowControllers/inMemory/inMemoryRowController.d.ts b/dist/lib/rowControllers/inMemory/inMemoryRowController.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..40afb98eb56972d38db24b9e23421097ae1e4b45
--- /dev/null
+++ b/dist/lib/rowControllers/inMemory/inMemoryRowController.d.ts
@@ -0,0 +1,50 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { RowNode } from "../../entities/rowNode";
+import { IRowModel } from "./../../interfaces/iRowModel";
+export declare class InMemoryRowController implements IRowModel {
+    private gridOptionsWrapper;
+    private columnController;
+    private filterManager;
+    private $scope;
+    private selectionController;
+    private eventService;
+    private filterStage;
+    private sortStage;
+    private flattenStage;
+    private groupStage;
+    private aggregationStage;
+    private allRows;
+    private rowsAfterGroup;
+    private rowsAfterFilter;
+    private rowsAfterSort;
+    private rowsToDisplay;
+    init(): void;
+    refreshModel(step: number, fromIndex?: any, groupState?: any): void;
+    isEmpty(): boolean;
+    isRowsToRender(): boolean;
+    setDatasource(datasource: any): void;
+    getTopLevelNodes(): RowNode[];
+    getRow(index: number): RowNode;
+    getVirtualRowCount(): number;
+    getRowCount(): number;
+    getRowAtPixel(pixelToMatch: number): number;
+    private isRowInPixel(rowNode, pixelToMatch);
+    getRowCombinedHeight(): number;
+    forEachNode(callback: Function): void;
+    forEachNodeAfterFilter(callback: Function): void;
+    forEachNodeAfterFilterAndSort(callback: Function): void;
+    private recursivelyWalkNodesAndCallback(nodes, callback, recursionType, index);
+    doAggregate(): void;
+    expandOrCollapseAll(expand: boolean): void;
+    private doSort();
+    private doRowGrouping(groupState);
+    private restoreGroupState(groupState);
+    private doFilter();
+    setRowData(rowData: any[], refresh: boolean, firstId?: number): void;
+    private getGroupState();
+    private createRowNodesFromData(rowData, firstId?);
+    private doRowsToDisplay();
+}
diff --git a/dist/lib/rowControllers/inMemory/inMemoryRowController.js b/dist/lib/rowControllers/inMemory/inMemoryRowController.js
new file mode 100644
index 0000000000000000000000000000000000000000..d8817abc1789f458f43e28c842e8fe624a5fa961
--- /dev/null
+++ b/dist/lib/rowControllers/inMemory/inMemoryRowController.js
@@ -0,0 +1,370 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var utils_1 = require('../../utils');
+var constants_1 = require('../../constants');
+var gridOptionsWrapper_1 = require("../../gridOptionsWrapper");
+var columnController_1 = require("../../columnController/columnController");
+var filterManager_1 = require("../../filter/filterManager");
+var rowNode_1 = require("../../entities/rowNode");
+var eventService_1 = require("../../eventService");
+var events_1 = require("../../events");
+var context_1 = require("../../context/context");
+var selectionController_1 = require("../../selectionController");
+var context_2 = require("../../context/context");
+var constants_2 = require("../../constants");
+var context_3 = require("../../context/context");
+var context_4 = require("../../context/context");
+var RecursionType;
+(function (RecursionType) {
+    RecursionType[RecursionType["Normal"] = 0] = "Normal";
+    RecursionType[RecursionType["AfterFilter"] = 1] = "AfterFilter";
+    RecursionType[RecursionType["AfterFilterAndSort"] = 2] = "AfterFilterAndSort";
+})(RecursionType || (RecursionType = {}));
+;
+var InMemoryRowController = (function () {
+    function InMemoryRowController() {
+        // the rows go through a pipeline of steps, each array below is the result
+        // after a certain step.
+        this.allRows = []; // the rows, in a list, as provided by the user, but wrapped in RowNode objects
+    }
+    InMemoryRowController.prototype.init = function () {
+        this.eventService.addModalPriorityEventListener(events_1.Events.EVENT_COLUMN_EVERYTHING_CHANGED, this.refreshModel.bind(this, constants_2.Constants.STEP_EVERYTHING));
+        this.eventService.addModalPriorityEventListener(events_1.Events.EVENT_COLUMN_ROW_GROUP_CHANGE, this.refreshModel.bind(this, constants_2.Constants.STEP_EVERYTHING));
+        this.eventService.addModalPriorityEventListener(events_1.Events.EVENT_COLUMN_VALUE_CHANGE, this.refreshModel.bind(this, constants_2.Constants.STEP_AGGREGATE));
+        this.eventService.addModalPriorityEventListener(events_1.Events.EVENT_FILTER_CHANGED, this.refreshModel.bind(this, constants_1.Constants.STEP_FILTER));
+        this.eventService.addModalPriorityEventListener(events_1.Events.EVENT_SORT_CHANGED, this.refreshModel.bind(this, constants_1.Constants.STEP_SORT));
+        if (this.gridOptionsWrapper.isRowModelDefault()) {
+            this.setRowData(this.gridOptionsWrapper.getRowData(), this.columnController.isReady());
+        }
+    };
+    InMemoryRowController.prototype.refreshModel = function (step, fromIndex, groupState) {
+        // this goes through the pipeline of stages. what's in my head is similar
+        // to the diagram on this page:
+        // http://commons.apache.org/sandbox/commons-pipeline/pipeline_basics.html
+        // however we want to keep the results of each stage, hence we manually call
+        // each step rather than have them chain each other.
+        var _this = this;
+        // fallthrough in below switch is on purpose,
+        // eg if STEP_FILTER, then all steps below this
+        // step get done
+        switch (step) {
+            case constants_1.Constants.STEP_EVERYTHING:
+                this.doRowGrouping(groupState);
+            case constants_1.Constants.STEP_FILTER:
+                this.doFilter();
+            case constants_1.Constants.STEP_AGGREGATE:
+                this.doAggregate();
+            case constants_1.Constants.STEP_SORT:
+                this.doSort();
+            case constants_1.Constants.STEP_MAP:
+                this.doRowsToDisplay();
+        }
+        this.eventService.dispatchEvent(events_1.Events.EVENT_MODEL_UPDATED, { fromIndex: fromIndex });
+        if (this.$scope) {
+            setTimeout(function () {
+                _this.$scope.$apply();
+            }, 0);
+        }
+    };
+    InMemoryRowController.prototype.isEmpty = function () {
+        return this.allRows === null || this.allRows.length === 0 || !this.columnController.isReady();
+    };
+    InMemoryRowController.prototype.isRowsToRender = function () {
+        return utils_1.Utils.exists(this.rowsToDisplay) && this.rowsToDisplay.length > 0;
+    };
+    InMemoryRowController.prototype.setDatasource = function (datasource) {
+        console.error('ag-Grid: should never call setDatasource on inMemoryRowController');
+    };
+    InMemoryRowController.prototype.getTopLevelNodes = function () {
+        return this.rowsAfterGroup;
+    };
+    InMemoryRowController.prototype.getRow = function (index) {
+        return this.rowsToDisplay[index];
+    };
+    InMemoryRowController.prototype.getVirtualRowCount = function () {
+        console.warn('ag-Grid: rowModel.getVirtualRowCount() is not longer a function, use rowModel.getRowCount() instead');
+        return this.getRowCount();
+    };
+    InMemoryRowController.prototype.getRowCount = function () {
+        if (this.rowsToDisplay) {
+            return this.rowsToDisplay.length;
+        }
+        else {
+            return 0;
+        }
+    };
+    InMemoryRowController.prototype.getRowAtPixel = function (pixelToMatch) {
+        if (this.isEmpty()) {
+            return -1;
+        }
+        // do binary search of tree
+        // http://oli.me.uk/2013/06/08/searching-javascript-arrays-with-a-binary-search/
+        var bottomPointer = 0;
+        var topPointer = this.rowsToDisplay.length - 1;
+        // quick check, if the pixel is out of bounds, then return last row
+        if (pixelToMatch <= 0) {
+            // if pixel is less than or equal zero, it's always the first row
+            return 0;
+        }
+        var lastNode = this.rowsToDisplay[this.rowsToDisplay.length - 1];
+        if (lastNode.rowTop <= pixelToMatch) {
+            return this.rowsToDisplay.length - 1;
+        }
+        while (true) {
+            var midPointer = Math.floor((bottomPointer + topPointer) / 2);
+            var currentRowNode = this.rowsToDisplay[midPointer];
+            if (this.isRowInPixel(currentRowNode, pixelToMatch)) {
+                return midPointer;
+            }
+            else if (currentRowNode.rowTop < pixelToMatch) {
+                bottomPointer = midPointer + 1;
+            }
+            else if (currentRowNode.rowTop > pixelToMatch) {
+                topPointer = midPointer - 1;
+            }
+        }
+    };
+    InMemoryRowController.prototype.isRowInPixel = function (rowNode, pixelToMatch) {
+        var topPixel = rowNode.rowTop;
+        var bottomPixel = rowNode.rowTop + rowNode.rowHeight;
+        var pixelInRow = topPixel <= pixelToMatch && bottomPixel > pixelToMatch;
+        return pixelInRow;
+    };
+    InMemoryRowController.prototype.getRowCombinedHeight = function () {
+        if (this.rowsToDisplay && this.rowsToDisplay.length > 0) {
+            var lastRow = this.rowsToDisplay[this.rowsToDisplay.length - 1];
+            var lastPixel = lastRow.rowTop + lastRow.rowHeight;
+            return lastPixel;
+        }
+        else {
+            return 0;
+        }
+    };
+    InMemoryRowController.prototype.forEachNode = function (callback) {
+        this.recursivelyWalkNodesAndCallback(this.rowsAfterGroup, callback, RecursionType.Normal, 0);
+    };
+    InMemoryRowController.prototype.forEachNodeAfterFilter = function (callback) {
+        this.recursivelyWalkNodesAndCallback(this.rowsAfterFilter, callback, RecursionType.AfterFilter, 0);
+    };
+    InMemoryRowController.prototype.forEachNodeAfterFilterAndSort = function (callback) {
+        this.recursivelyWalkNodesAndCallback(this.rowsAfterSort, callback, RecursionType.AfterFilterAndSort, 0);
+    };
+    // iterates through each item in memory, and calls the callback function
+    // nodes - the rowNodes to traverse
+    // callback - the user provided callback
+    // recursion type - need this to know what child nodes to recurse, eg if looking at all nodes, or filtered notes etc
+    // index - works similar to the index in forEach in javascripts array function
+    InMemoryRowController.prototype.recursivelyWalkNodesAndCallback = function (nodes, callback, recursionType, index) {
+        if (nodes) {
+            for (var i = 0; i < nodes.length; i++) {
+                var node = nodes[i];
+                callback(node, index++);
+                // go to the next level if it is a group
+                if (node.group) {
+                    // depending on the recursion type, we pick a difference set of children
+                    var nodeChildren;
+                    switch (recursionType) {
+                        case RecursionType.Normal:
+                            nodeChildren = node.children;
+                            break;
+                        case RecursionType.AfterFilter:
+                            nodeChildren = node.childrenAfterFilter;
+                            break;
+                        case RecursionType.AfterFilterAndSort:
+                            nodeChildren = node.childrenAfterSort;
+                            break;
+                    }
+                    if (nodeChildren) {
+                        index = this.recursivelyWalkNodesAndCallback(nodeChildren, callback, recursionType, index);
+                    }
+                }
+            }
+        }
+        return index;
+    };
+    // it's possible to recompute the aggregate without doing the other parts
+    // + gridApi.recomputeAggregates()
+    InMemoryRowController.prototype.doAggregate = function () {
+        if (this.aggregationStage) {
+            this.aggregationStage.execute(this.rowsAfterFilter);
+        }
+    };
+    // + gridApi.expandAll()
+    // + gridApi.collapseAll()
+    InMemoryRowController.prototype.expandOrCollapseAll = function (expand) {
+        recursiveExpandOrCollapse(this.rowsAfterGroup);
+        function recursiveExpandOrCollapse(rowNodes) {
+            if (!rowNodes) {
+                return;
+            }
+            rowNodes.forEach(function (rowNode) {
+                if (rowNode.group) {
+                    rowNode.expanded = expand;
+                    recursiveExpandOrCollapse(rowNode.children);
+                }
+            });
+        }
+        this.refreshModel(constants_2.Constants.STEP_MAP);
+    };
+    InMemoryRowController.prototype.doSort = function () {
+        this.rowsAfterSort = this.sortStage.execute(this.rowsAfterFilter);
+    };
+    InMemoryRowController.prototype.doRowGrouping = function (groupState) {
+        // grouping is enterprise only, so if service missing, skip the step
+        var rowsAlreadyGrouped = utils_1.Utils.exists(this.gridOptionsWrapper.getNodeChildDetailsFunc());
+        if (this.groupStage && !rowsAlreadyGrouped) {
+            // remove old groups from the selection model, as we are about to replace them
+            // with new groups
+            this.selectionController.removeGroupsFromSelection();
+            this.rowsAfterGroup = this.groupStage.execute(this.allRows);
+            this.restoreGroupState(groupState);
+            if (this.gridOptionsWrapper.isGroupSelectsChildren()) {
+                this.selectionController.updateGroupsFromChildrenSelections();
+            }
+        }
+        else {
+            this.rowsAfterGroup = this.allRows;
+        }
+    };
+    InMemoryRowController.prototype.restoreGroupState = function (groupState) {
+        if (!groupState) {
+            return;
+        }
+        utils_1.Utils.traverseNodesWithKey(this.rowsAfterGroup, function (node, key) {
+            node.expanded = groupState[key] === true;
+        });
+    };
+    InMemoryRowController.prototype.doFilter = function () {
+        this.rowsAfterFilter = this.filterStage.execute(this.rowsAfterGroup);
+    };
+    // rows: the rows to put into the model
+    // firstId: the first id to use, used for paging, where we are not on the first page
+    InMemoryRowController.prototype.setRowData = function (rowData, refresh, firstId) {
+        // remember group state, so we can expand groups that should be expanded
+        var groupState = this.getGroupState();
+        // place each row into a wrapper
+        this.allRows = this.createRowNodesFromData(rowData, firstId);
+        this.eventService.dispatchEvent(events_1.Events.EVENT_ROW_DATA_CHANGED);
+        if (refresh) {
+            this.refreshModel(constants_2.Constants.STEP_EVERYTHING, null, groupState);
+        }
+    };
+    InMemoryRowController.prototype.getGroupState = function () {
+        if (!this.rowsAfterGroup || !this.gridOptionsWrapper.isRememberGroupStateWhenNewData()) {
+            return null;
+        }
+        var result = {};
+        utils_1.Utils.traverseNodesWithKey(this.rowsAfterGroup, function (node, key) { return result[key] = node.expanded; });
+        return result;
+    };
+    InMemoryRowController.prototype.createRowNodesFromData = function (rowData, firstId) {
+        if (!rowData) {
+            return [];
+        }
+        var rowNodeId = utils_1.Utils.exists(firstId) ? firstId : 0;
+        // func below doesn't have 'this' pointer, so need to pull out these bits
+        var nodeChildDetailsFunc = this.gridOptionsWrapper.getNodeChildDetailsFunc();
+        var suppressParentsInRowNodes = this.gridOptionsWrapper.isSuppressParentsInRowNodes();
+        var eventService = this.eventService;
+        var gridOptionsWrapper = this.gridOptionsWrapper;
+        var selectionController = this.selectionController;
+        // kick off recursion
+        var result = recursiveFunction(rowData, null, 0);
+        return result;
+        function recursiveFunction(rowData, parent, level) {
+            var rowNodes = [];
+            rowData.forEach(function (dataItem) {
+                var node = new rowNode_1.RowNode(eventService, gridOptionsWrapper, selectionController);
+                var nodeChildDetails = nodeChildDetailsFunc ? nodeChildDetailsFunc(dataItem) : null;
+                if (nodeChildDetails && nodeChildDetails.group) {
+                    node.group = true;
+                    node.children = recursiveFunction(nodeChildDetails.children, node, level + 1);
+                    node.expanded = nodeChildDetails.expanded === true;
+                    node.field = nodeChildDetails.field;
+                    node.key = nodeChildDetails.key;
+                }
+                if (parent && !suppressParentsInRowNodes) {
+                    node.parent = parent;
+                }
+                node.level = level;
+                node.id = rowNodeId++;
+                node.data = dataItem;
+                rowNodes.push(node);
+            });
+            return rowNodes;
+        }
+    };
+    InMemoryRowController.prototype.doRowsToDisplay = function () {
+        this.rowsToDisplay = this.flattenStage.execute(this.rowsAfterSort);
+    };
+    __decorate([
+        context_2.Autowired('gridOptionsWrapper'), 
+        __metadata('design:type', gridOptionsWrapper_1.GridOptionsWrapper)
+    ], InMemoryRowController.prototype, "gridOptionsWrapper", void 0);
+    __decorate([
+        context_2.Autowired('columnController'), 
+        __metadata('design:type', columnController_1.ColumnController)
+    ], InMemoryRowController.prototype, "columnController", void 0);
+    __decorate([
+        context_2.Autowired('filterManager'), 
+        __metadata('design:type', filterManager_1.FilterManager)
+    ], InMemoryRowController.prototype, "filterManager", void 0);
+    __decorate([
+        context_2.Autowired('$scope'), 
+        __metadata('design:type', Object)
+    ], InMemoryRowController.prototype, "$scope", void 0);
+    __decorate([
+        context_2.Autowired('selectionController'), 
+        __metadata('design:type', selectionController_1.SelectionController)
+    ], InMemoryRowController.prototype, "selectionController", void 0);
+    __decorate([
+        context_2.Autowired('eventService'), 
+        __metadata('design:type', eventService_1.EventService)
+    ], InMemoryRowController.prototype, "eventService", void 0);
+    __decorate([
+        context_2.Autowired('filterStage'), 
+        __metadata('design:type', Object)
+    ], InMemoryRowController.prototype, "filterStage", void 0);
+    __decorate([
+        context_2.Autowired('sortStage'), 
+        __metadata('design:type', Object)
+    ], InMemoryRowController.prototype, "sortStage", void 0);
+    __decorate([
+        context_2.Autowired('flattenStage'), 
+        __metadata('design:type', Object)
+    ], InMemoryRowController.prototype, "flattenStage", void 0);
+    __decorate([
+        context_4.Optional('groupStage'), 
+        __metadata('design:type', Object)
+    ], InMemoryRowController.prototype, "groupStage", void 0);
+    __decorate([
+        context_4.Optional('aggregationStage'), 
+        __metadata('design:type', Object)
+    ], InMemoryRowController.prototype, "aggregationStage", void 0);
+    __decorate([
+        // the rows mapped to rows to display
+        context_3.PostConstruct, 
+        __metadata('design:type', Function), 
+        __metadata('design:paramtypes', []), 
+        __metadata('design:returntype', void 0)
+    ], InMemoryRowController.prototype, "init", null);
+    InMemoryRowController = __decorate([
+        context_1.Bean('rowModel'), 
+        __metadata('design:paramtypes', [])
+    ], InMemoryRowController);
+    return InMemoryRowController;
+})();
+exports.InMemoryRowController = InMemoryRowController;
diff --git a/dist/lib/rowControllers/inMemory/sortStage.d.ts b/dist/lib/rowControllers/inMemory/sortStage.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c25c29c7f44f95cb836327ce44934eeba52350a6
--- /dev/null
+++ b/dist/lib/rowControllers/inMemory/sortStage.d.ts
@@ -0,0 +1,14 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { RowNode } from "../../entities/rowNode";
+export declare class SortStage {
+    private gridOptionsWrapper;
+    private sortController;
+    private valueService;
+    execute(rowsToSort: RowNode[]): RowNode[];
+    private sortList(nodes, sortOptions);
+    private recursivelyResetSort(rowNodes);
+    private updateChildIndexes(nodes);
+}
diff --git a/dist/lib/rowControllers/inMemory/sortStage.js b/dist/lib/rowControllers/inMemory/sortStage.js
new file mode 100644
index 0000000000000000000000000000000000000000..19696d259c3fd07929e5949cec97a8fbdd9faced
--- /dev/null
+++ b/dist/lib/rowControllers/inMemory/sortStage.js
@@ -0,0 +1,124 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var context_1 = require("../../context/context");
+var context_2 = require("../../context/context");
+var gridOptionsWrapper_1 = require("../../gridOptionsWrapper");
+var sortController_1 = require("../../sortController");
+var valueService_1 = require("../../valueService");
+var utils_1 = require('../../utils');
+var SortStage = (function () {
+    function SortStage() {
+    }
+    SortStage.prototype.execute = function (rowsToSort) {
+        var sorting;
+        // if the sorting is already done by the server, then we should not do it here
+        if (this.gridOptionsWrapper.isEnableServerSideSorting()) {
+            sorting = false;
+        }
+        else {
+            //see if there is a col we are sorting by
+            var sortingOptions = this.sortController.getSortForRowController();
+            sorting = sortingOptions.length > 0;
+        }
+        var result = rowsToSort.slice(0);
+        if (sorting) {
+            this.sortList(result, sortingOptions);
+        }
+        else {
+            // if no sorting, set all group children after sort to the original list.
+            // note: it is important to do this, even if doing server side sorting,
+            // to allow the rows to pass to the next stage (ie set the node value
+            // childrenAfterSort)
+            this.recursivelyResetSort(result);
+        }
+        return result;
+    };
+    SortStage.prototype.sortList = function (nodes, sortOptions) {
+        // sort any groups recursively
+        for (var i = 0, l = nodes.length; i < l; i++) {
+            var node = nodes[i];
+            if (node.group && node.children) {
+                node.childrenAfterSort = node.childrenAfterFilter.slice(0);
+                this.sortList(node.childrenAfterSort, sortOptions);
+            }
+        }
+        var that = this;
+        function compare(nodeA, nodeB, column, isInverted) {
+            var valueA = that.valueService.getValue(column, nodeA);
+            var valueB = that.valueService.getValue(column, nodeB);
+            if (column.getColDef().comparator) {
+                //if comparator provided, use it
+                return column.getColDef().comparator(valueA, valueB, nodeA, nodeB, isInverted);
+            }
+            else {
+                //otherwise do our own comparison
+                return utils_1.Utils.defaultComparator(valueA, valueB);
+            }
+        }
+        nodes.sort(function (nodeA, nodeB) {
+            // Iterate columns, return the first that doesn't match
+            for (var i = 0, len = sortOptions.length; i < len; i++) {
+                var sortOption = sortOptions[i];
+                var compared = compare(nodeA, nodeB, sortOption.column, sortOption.inverter === -1);
+                if (compared !== 0) {
+                    return compared * sortOption.inverter;
+                }
+            }
+            // All matched, these are identical as far as the sort is concerned:
+            return 0;
+        });
+        this.updateChildIndexes(nodes);
+    };
+    SortStage.prototype.recursivelyResetSort = function (rowNodes) {
+        if (!rowNodes) {
+            return;
+        }
+        for (var i = 0, l = rowNodes.length; i < l; i++) {
+            var item = rowNodes[i];
+            if (item.group && item.children) {
+                item.childrenAfterSort = item.childrenAfterFilter;
+                this.recursivelyResetSort(item.children);
+            }
+        }
+        this.updateChildIndexes(rowNodes);
+    };
+    SortStage.prototype.updateChildIndexes = function (nodes) {
+        for (var j = 0; j < nodes.length; j++) {
+            var node = nodes[j];
+            node.firstChild = j === 0;
+            node.lastChild = j === nodes.length - 1;
+            node.childIndex = j;
+        }
+    };
+    __decorate([
+        context_2.Autowired('gridOptionsWrapper'), 
+        __metadata('design:type', gridOptionsWrapper_1.GridOptionsWrapper)
+    ], SortStage.prototype, "gridOptionsWrapper", void 0);
+    __decorate([
+        context_2.Autowired('sortController'), 
+        __metadata('design:type', sortController_1.SortController)
+    ], SortStage.prototype, "sortController", void 0);
+    __decorate([
+        context_2.Autowired('valueService'), 
+        __metadata('design:type', valueService_1.ValueService)
+    ], SortStage.prototype, "valueService", void 0);
+    SortStage = __decorate([
+        context_1.Bean('sortStage'), 
+        __metadata('design:paramtypes', [])
+    ], SortStage);
+    return SortStage;
+})();
+exports.SortStage = SortStage;
diff --git a/dist/lib/sortController.d.ts b/dist/lib/sortController.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d0815027abb11e80b9e05e8607b568f76ec0b196
--- /dev/null
+++ b/dist/lib/sortController.d.ts
@@ -0,0 +1,22 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { Column } from "./entities/column";
+export declare class SortController {
+    private static DEFAULT_SORTING_ORDER;
+    private gridOptionsWrapper;
+    private columnController;
+    private eventService;
+    progressSort(column: Column, multiSort: boolean): void;
+    private dispatchSortChangedEvents();
+    private clearSortBarThisColumn(columnToSkip);
+    private getNextSortDirection(column);
+    getSortModel(): {
+        colId: string;
+        sort: string;
+    }[];
+    setSortModel(sortModel: any): void;
+    getColumnsWithSortingOrdered(): Column[];
+    getSortForRowController(): any[];
+}
diff --git a/dist/lib/sortController.js b/dist/lib/sortController.js
new file mode 100644
index 0000000000000000000000000000000000000000..f5fea7a33f08771b8a7c4b0670ba7ebe8a21f3e2
--- /dev/null
+++ b/dist/lib/sortController.js
@@ -0,0 +1,170 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var column_1 = require("./entities/column");
+var context_1 = require("./context/context");
+var gridOptionsWrapper_1 = require("./gridOptionsWrapper");
+var columnController_1 = require("./columnController/columnController");
+var eventService_1 = require("./eventService");
+var events_1 = require("./events");
+var context_2 = require("./context/context");
+var utils_1 = require('./utils');
+var SortController = (function () {
+    function SortController() {
+    }
+    SortController.prototype.progressSort = function (column, multiSort) {
+        // update sort on current col
+        column.setSort(this.getNextSortDirection(column));
+        // sortedAt used for knowing order of cols when multi-col sort
+        if (column.getSort()) {
+            column.setSortedAt(new Date().valueOf());
+        }
+        else {
+            column.setSortedAt(null);
+        }
+        var doingMultiSort = multiSort && !this.gridOptionsWrapper.isSuppressMultiSort();
+        // clear sort on all columns except this one, and update the icons
+        if (!doingMultiSort) {
+            this.clearSortBarThisColumn(column);
+        }
+        this.dispatchSortChangedEvents();
+    };
+    SortController.prototype.dispatchSortChangedEvents = function () {
+        this.eventService.dispatchEvent(events_1.Events.EVENT_BEFORE_SORT_CHANGED);
+        this.eventService.dispatchEvent(events_1.Events.EVENT_SORT_CHANGED);
+        this.eventService.dispatchEvent(events_1.Events.EVENT_AFTER_SORT_CHANGED);
+    };
+    SortController.prototype.clearSortBarThisColumn = function (columnToSkip) {
+        this.columnController.getAllColumnsIncludingAuto().forEach(function (columnToClear) {
+            // Do not clear if either holding shift, or if column in question was clicked
+            if (!(columnToClear === columnToSkip)) {
+                columnToClear.setSort(null);
+            }
+        });
+    };
+    SortController.prototype.getNextSortDirection = function (column) {
+        var sortingOrder;
+        if (column.getColDef().sortingOrder) {
+            sortingOrder = column.getColDef().sortingOrder;
+        }
+        else if (this.gridOptionsWrapper.getSortingOrder()) {
+            sortingOrder = this.gridOptionsWrapper.getSortingOrder();
+        }
+        else {
+            sortingOrder = SortController.DEFAULT_SORTING_ORDER;
+        }
+        if (!Array.isArray(sortingOrder) || sortingOrder.length <= 0) {
+            console.warn('ag-grid: sortingOrder must be an array with at least one element, currently it\'s ' + sortingOrder);
+            return;
+        }
+        var currentIndex = sortingOrder.indexOf(column.getSort());
+        var notInArray = currentIndex < 0;
+        var lastItemInArray = currentIndex == sortingOrder.length - 1;
+        var result;
+        if (notInArray || lastItemInArray) {
+            result = sortingOrder[0];
+        }
+        else {
+            result = sortingOrder[currentIndex + 1];
+        }
+        // verify the sort type exists, as the user could provide the sortOrder, need to make sure it's valid
+        if (SortController.DEFAULT_SORTING_ORDER.indexOf(result) < 0) {
+            console.warn('ag-grid: invalid sort type ' + result);
+            return null;
+        }
+        return result;
+    };
+    // used by the public api, for saving the sort model
+    SortController.prototype.getSortModel = function () {
+        var columnsWithSorting = this.getColumnsWithSortingOrdered();
+        return utils_1.Utils.map(columnsWithSorting, function (column) {
+            return {
+                colId: column.getColId(),
+                sort: column.getSort()
+            };
+        });
+    };
+    SortController.prototype.setSortModel = function (sortModel) {
+        if (!this.gridOptionsWrapper.isEnableSorting()) {
+            console.warn('ag-grid: You are setting the sort model on a grid that does not have sorting enabled');
+            return;
+        }
+        // first up, clear any previous sort
+        var sortModelProvided = sortModel && sortModel.length > 0;
+        var allColumnsIncludingAuto = this.columnController.getAllColumnsIncludingAuto();
+        allColumnsIncludingAuto.forEach(function (column) {
+            var sortForCol = null;
+            var sortedAt = -1;
+            if (sortModelProvided && !column.getColDef().suppressSorting) {
+                for (var j = 0; j < sortModel.length; j++) {
+                    var sortModelEntry = sortModel[j];
+                    if (typeof sortModelEntry.colId === 'string'
+                        && typeof column.getColId() === 'string'
+                        && sortModelEntry.colId === column.getColId()) {
+                        sortForCol = sortModelEntry.sort;
+                        sortedAt = j;
+                    }
+                }
+            }
+            if (sortForCol) {
+                column.setSort(sortForCol);
+                column.setSortedAt(sortedAt);
+            }
+            else {
+                column.setSort(null);
+                column.setSortedAt(null);
+            }
+        });
+        this.dispatchSortChangedEvents();
+    };
+    SortController.prototype.getColumnsWithSortingOrdered = function () {
+        // pull out all the columns that have sorting set
+        var allColumnsIncludingAuto = this.columnController.getAllColumnsIncludingAuto();
+        var columnsWithSorting = utils_1.Utils.filter(allColumnsIncludingAuto, function (column) { return !!column.getSort(); });
+        // put the columns in order of which one got sorted first
+        columnsWithSorting.sort(function (a, b) { return a.sortedAt - b.sortedAt; });
+        return columnsWithSorting;
+    };
+    // used by row controller, when doing the sorting
+    SortController.prototype.getSortForRowController = function () {
+        var columnsWithSorting = this.getColumnsWithSortingOrdered();
+        return utils_1.Utils.map(columnsWithSorting, function (column) {
+            var ascending = column.getSort() === column_1.Column.SORT_ASC;
+            return {
+                inverter: ascending ? 1 : -1,
+                column: column
+            };
+        });
+    };
+    SortController.DEFAULT_SORTING_ORDER = [column_1.Column.SORT_ASC, column_1.Column.SORT_DESC, null];
+    __decorate([
+        context_1.Autowired('gridOptionsWrapper'), 
+        __metadata('design:type', gridOptionsWrapper_1.GridOptionsWrapper)
+    ], SortController.prototype, "gridOptionsWrapper", void 0);
+    __decorate([
+        context_1.Autowired('columnController'), 
+        __metadata('design:type', columnController_1.ColumnController)
+    ], SortController.prototype, "columnController", void 0);
+    __decorate([
+        context_1.Autowired('eventService'), 
+        __metadata('design:type', eventService_1.EventService)
+    ], SortController.prototype, "eventService", void 0);
+    SortController = __decorate([
+        context_2.Bean('sortController'), 
+        __metadata('design:paramtypes', [])
+    ], SortController);
+    return SortController;
+})();
+exports.SortController = SortController;
diff --git a/dist/lib/widgets/PopupService.d.ts b/dist/lib/widgets/PopupService.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a3a034f81d64cc58fcbb977ff08b88e5e3844556
--- /dev/null
+++ b/dist/lib/widgets/PopupService.d.ts
@@ -0,0 +1,26 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+export declare class PopupService {
+    private ePopupParent;
+    setPopupParent(ePopupParent: any): void;
+    positionPopupForMenu(params: {
+        eventSource: any;
+        ePopup: HTMLElement;
+    }): void;
+    positionPopupUnderMouseEvent(params: {
+        mouseEvent: MouseEvent;
+        ePopup: HTMLElement;
+    }): void;
+    positionPopupUnderComponent(params: {
+        eventSource: HTMLElement;
+        ePopup: HTMLElement;
+        minWidth?: number;
+        nudgeX?: number;
+        nudgeY?: number;
+        keepWithinBounds?: boolean;
+    }): void;
+    private positionPopup(params);
+    addAsModalPopup(eChild: any, closeOnEsc: boolean, closedCallback?: () => void): (event: any) => void;
+}
diff --git a/dist/lib/widgets/PopupService.js b/dist/lib/widgets/PopupService.js
new file mode 100644
index 0000000000000000000000000000000000000000..dba3e19d4faef7672c8d88edf492e14455b1bb3f
--- /dev/null
+++ b/dist/lib/widgets/PopupService.js
@@ -0,0 +1,165 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var utils_1 = require('../utils');
+var constants_1 = require("../constants");
+var context_1 = require("../context/context");
+var PopupService = (function () {
+    function PopupService() {
+    }
+    PopupService.prototype.setPopupParent = function (ePopupParent) {
+        this.ePopupParent = ePopupParent;
+    };
+    PopupService.prototype.positionPopupForMenu = function (params) {
+        var sourceRect = params.eventSource.getBoundingClientRect();
+        var parentRect = this.ePopupParent.getBoundingClientRect();
+        var x = sourceRect.right - parentRect.left - 2;
+        var y = sourceRect.top - parentRect.top;
+        var minWidth;
+        if (params.ePopup.clientWidth > 0) {
+            minWidth = params.ePopup.clientWidth;
+        }
+        else {
+            minWidth = 200;
+        }
+        var widthOfParent = parentRect.right - parentRect.left;
+        var maxX = widthOfParent - minWidth;
+        if (x > maxX) {
+            // try putting menu to the left
+            x = sourceRect.left - minWidth;
+        }
+        if (x < 0) {
+            x = 0;
+        }
+        params.ePopup.style.left = x + "px";
+        params.ePopup.style.top = y + "px";
+    };
+    PopupService.prototype.positionPopupUnderMouseEvent = function (params) {
+        var parentRect = this.ePopupParent.getBoundingClientRect();
+        this.positionPopup({
+            ePopup: params.ePopup,
+            x: params.mouseEvent.clientX - parentRect.left,
+            y: params.mouseEvent.clientY - parentRect.top,
+            keepWithinBounds: true
+        });
+    };
+    PopupService.prototype.positionPopupUnderComponent = function (params) {
+        var sourceRect = params.eventSource.getBoundingClientRect();
+        var parentRect = this.ePopupParent.getBoundingClientRect();
+        this.positionPopup({
+            ePopup: params.ePopup,
+            minWidth: params.minWidth,
+            nudgeX: params.nudgeX,
+            nudgeY: params.nudgeY,
+            x: sourceRect.left - parentRect.left,
+            y: sourceRect.top - parentRect.top + sourceRect.height,
+            keepWithinBounds: params.keepWithinBounds
+        });
+    };
+    PopupService.prototype.positionPopup = function (params) {
+        var parentRect = this.ePopupParent.getBoundingClientRect();
+        var x = params.x;
+        var y = params.y;
+        if (params.nudgeX) {
+            x += params.nudgeX;
+        }
+        if (params.nudgeY) {
+            y += params.nudgeY;
+        }
+        // if popup is overflowing to the right, move it left
+        if (params.keepWithinBounds) {
+            var minWidth;
+            if (params.minWidth > 0) {
+                minWidth = params.minWidth;
+            }
+            else if (params.ePopup.clientWidth > 0) {
+                minWidth = params.ePopup.clientWidth;
+            }
+            else {
+                minWidth = 200;
+            }
+            var widthOfParent = parentRect.right - parentRect.left;
+            var maxX = widthOfParent - minWidth;
+            if (x > maxX) {
+                x = maxX;
+            }
+            if (x < 0) {
+                x = 0;
+            }
+        }
+        params.ePopup.style.left = x + "px";
+        params.ePopup.style.top = y + "px";
+    };
+    //adds an element to a div, but also listens to background checking for clicks,
+    //so that when the background is clicked, the child is removed again, giving
+    //a model look to popups.
+    PopupService.prototype.addAsModalPopup = function (eChild, closeOnEsc, closedCallback) {
+        var eBody = document.body;
+        if (!eBody) {
+            console.warn('ag-grid: could not find the body of the document, document.body is empty');
+            return;
+        }
+        var popupAlreadyShown = utils_1.Utils.isVisible(eChild);
+        if (popupAlreadyShown) {
+            return;
+        }
+        this.ePopupParent.appendChild(eChild);
+        var that = this;
+        // if we add these listeners now, then the current mouse
+        // click will be included, which we don't want
+        setTimeout(function () {
+            if (closeOnEsc) {
+                eBody.addEventListener('keydown', hidePopupOnEsc);
+            }
+            eBody.addEventListener('click', hidePopup);
+            eBody.addEventListener('contextmenu', hidePopup);
+            //eBody.addEventListener('mousedown', hidePopup);
+            eChild.addEventListener('click', consumeClick);
+            //eChild.addEventListener('mousedown', consumeClick);
+        }, 0);
+        var eventFromChild = null;
+        function hidePopupOnEsc(event) {
+            var key = event.which || event.keyCode;
+            if (key === constants_1.Constants.KEY_ESCAPE) {
+                hidePopup(null);
+            }
+        }
+        function hidePopup(event) {
+            if (event && event === eventFromChild) {
+                return;
+            }
+            that.ePopupParent.removeChild(eChild);
+            eBody.removeEventListener('keydown', hidePopupOnEsc);
+            //eBody.removeEventListener('mousedown', hidePopupOnEsc);
+            eBody.removeEventListener('click', hidePopup);
+            eBody.removeEventListener('contextmenu', hidePopup);
+            eChild.removeEventListener('click', consumeClick);
+            //eChild.removeEventListener('mousedown', consumeClick);
+            if (closedCallback) {
+                closedCallback();
+            }
+        }
+        function consumeClick(event) {
+            eventFromChild = event;
+        }
+        return hidePopup;
+    };
+    PopupService = __decorate([
+        context_1.Bean('popupService'), 
+        __metadata('design:paramtypes', [])
+    ], PopupService);
+    return PopupService;
+})();
+exports.PopupService = PopupService;
diff --git a/dist/lib/widgets/cMenuItem.d.ts b/dist/lib/widgets/cMenuItem.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0a9a178116a5ced349ddf027a2eb5d91827e6ce9
--- /dev/null
+++ b/dist/lib/widgets/cMenuItem.d.ts
@@ -0,0 +1,23 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { Component } from "./component";
+import { MenuList } from "./menuList";
+export declare class CMenuItem extends Component {
+    private popupService;
+    private static TEMPLATE;
+    static EVENT_ITEM_SELECTED: string;
+    private params;
+    constructor(params: MenuItem);
+    private onOptionSelected();
+}
+export interface MenuItem {
+    name: string;
+    disabled?: boolean;
+    shortcut?: string;
+    action?: () => void;
+    checked?: boolean;
+    icon?: HTMLElement | string;
+    childMenu?: MenuList;
+}
diff --git a/dist/lib/widgets/cMenuItem.js b/dist/lib/widgets/cMenuItem.js
new file mode 100644
index 0000000000000000000000000000000000000000..556c9e897e6b2b67a5f0dc0c43959a7f816152ed
--- /dev/null
+++ b/dist/lib/widgets/cMenuItem.js
@@ -0,0 +1,86 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var component_1 = require("./component");
+var context_1 = require("../context/context");
+var popupService_1 = require("./popupService");
+var utils_1 = require('../utils');
+var svgFactory_1 = require("../svgFactory");
+var svgFactory = svgFactory_1.SvgFactory.getInstance();
+var CMenuItem = (function (_super) {
+    __extends(CMenuItem, _super);
+    function CMenuItem(params) {
+        _super.call(this, CMenuItem.TEMPLATE);
+        this.params = params;
+        if (params.checked) {
+            this.queryForHtmlElement('#eIcon').innerHTML = '&#10004;';
+        }
+        else if (params.icon) {
+            if (utils_1.Utils.isNodeOrElement(params.icon)) {
+                this.queryForHtmlElement('#eIcon').appendChild(params.icon);
+            }
+            else if (typeof params.icon === 'string') {
+                this.queryForHtmlElement('#eIcon').innerHTML = params.icon;
+            }
+            else {
+                console.log('ag-Grid: menu item icon must be DOM node or string');
+            }
+        }
+        else {
+            // if i didn't put space here, the alignment was messed up, probably
+            // fixable with CSS but i was spending to much time trying to figure
+            // it out.
+            this.queryForHtmlElement('#eIcon').innerHTML = '&nbsp;';
+        }
+        if (params.shortcut) {
+            this.queryForHtmlElement('#eShortcut').innerHTML = params.shortcut;
+        }
+        if (params.childMenu) {
+            this.queryForHtmlElement('#ePopupPointer').appendChild(svgFactory.createSmallArrowRightSvg());
+        }
+        else {
+            this.queryForHtmlElement('#ePopupPointer').innerHTML = '&nbsp;';
+        }
+        this.queryForHtmlElement('#eName').innerHTML = params.name;
+        if (params.disabled) {
+            utils_1.Utils.addCssClass(this.getGui(), 'ag-menu-option-disabled');
+        }
+        this.addGuiEventListener('click', this.onOptionSelected.bind(this));
+    }
+    CMenuItem.prototype.onOptionSelected = function () {
+        this.dispatchEvent(CMenuItem.EVENT_ITEM_SELECTED, this.params);
+        if (this.params.action) {
+            this.params.action();
+        }
+    };
+    CMenuItem.TEMPLATE = '<div class="ag-menu-option">' +
+        '  <span id="eIcon" class="ag-menu-option-icon"></span>' +
+        '  <span id="eName" class="ag-menu-option-text"></span>' +
+        '  <span id="eShortcut" class="ag-menu-option-shortcut"></span>' +
+        '  <span id="ePopupPointer" class="ag-menu-option-popup-pointer"></span>' +
+        '</div>';
+    CMenuItem.EVENT_ITEM_SELECTED = 'itemSelected';
+    __decorate([
+        context_1.Autowired('popupService'), 
+        __metadata('design:type', popupService_1.PopupService)
+    ], CMenuItem.prototype, "popupService", void 0);
+    return CMenuItem;
+})(component_1.Component);
+exports.CMenuItem = CMenuItem;
diff --git a/dist/lib/widgets/component.d.ts b/dist/lib/widgets/component.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7f21dcaae1602de2bbf5b0d98c5c693446b43f35
--- /dev/null
+++ b/dist/lib/widgets/component.d.ts
@@ -0,0 +1,22 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { EventService } from "../eventService";
+export declare class Component {
+    private eGui;
+    private destroyFunctions;
+    private localEventService;
+    constructor(template: string);
+    addEventListener(eventType: string, listener: Function): void;
+    dispatchEvent(eventType: string, event?: any): void;
+    getGui(): HTMLElement;
+    protected queryForHtmlElement(cssSelector: string): HTMLElement;
+    protected queryForHtmlInputElement(cssSelector: string): HTMLInputElement;
+    appendChild(newChild: Node | Component): void;
+    setVisible(visible: boolean): void;
+    destroy(): void;
+    addGuiEventListener(event: string, listener: () => void): void;
+    addDestroyableEventListener(eElement: HTMLElement | EventService, event: string, listener: () => void): void;
+    addDestroyFunc(func: () => void): void;
+}
diff --git a/dist/lib/widgets/component.js b/dist/lib/widgets/component.js
new file mode 100644
index 0000000000000000000000000000000000000000..fda73776bfb982c43e67c40af717ae12f176cfcf
--- /dev/null
+++ b/dist/lib/widgets/component.js
@@ -0,0 +1,74 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var utils_1 = require('../utils');
+var eventService_1 = require("../eventService");
+var Component = (function () {
+    function Component(template) {
+        this.destroyFunctions = [];
+        this.eGui = utils_1.Utils.loadTemplate(template);
+    }
+    Component.prototype.addEventListener = function (eventType, listener) {
+        if (!this.localEventService) {
+            this.localEventService = new eventService_1.EventService();
+        }
+        this.localEventService.addEventListener(eventType, listener);
+    };
+    Component.prototype.dispatchEvent = function (eventType, event) {
+        if (this.localEventService) {
+            this.localEventService.dispatchEvent(eventType, event);
+        }
+    };
+    Component.prototype.getGui = function () {
+        return this.eGui;
+    };
+    Component.prototype.queryForHtmlElement = function (cssSelector) {
+        return this.eGui.querySelector(cssSelector);
+    };
+    Component.prototype.queryForHtmlInputElement = function (cssSelector) {
+        return this.eGui.querySelector(cssSelector);
+    };
+    Component.prototype.appendChild = function (newChild) {
+        if (utils_1.Utils.isNodeOrElement(newChild)) {
+            this.eGui.appendChild(newChild);
+        }
+        else {
+            this.eGui.appendChild(newChild.getGui());
+        }
+    };
+    Component.prototype.setVisible = function (visible) {
+        utils_1.Utils.addOrRemoveCssClass(this.eGui, 'ag-hidden', !visible);
+    };
+    Component.prototype.destroy = function () {
+        this.destroyFunctions.forEach(function (func) { return func(); });
+    };
+    Component.prototype.addGuiEventListener = function (event, listener) {
+        var _this = this;
+        this.getGui().addEventListener(event, listener);
+        this.destroyFunctions.push(function () { return _this.getGui().removeEventListener(event, listener); });
+    };
+    Component.prototype.addDestroyableEventListener = function (eElement, event, listener) {
+        if (eElement instanceof eventService_1.EventService) {
+            eElement.addEventListener(event, listener);
+        }
+        else {
+            eElement.addEventListener(event, listener);
+        }
+        this.destroyFunctions.push(function () {
+            if (eElement instanceof eventService_1.EventService) {
+                eElement.removeEventListener(event, listener);
+            }
+            else {
+                eElement.removeEventListener(event, listener);
+            }
+        });
+    };
+    Component.prototype.addDestroyFunc = function (func) {
+        this.destroyFunctions.push(func);
+    };
+    return Component;
+})();
+exports.Component = Component;
diff --git a/dist/lib/widgets/menuList.d.ts b/dist/lib/widgets/menuList.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..edbd33713589e1a3deb61c7b3b5f38eb2e162964
--- /dev/null
+++ b/dist/lib/widgets/menuList.d.ts
@@ -0,0 +1,30 @@
+// Type definitions for ag-grid v4.0.0
+// Project: http://www.ag-grid.com/
+// Definitions by: Niall Crosby <https://github.com/ceolter/>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+import { Component } from "./component";
+import { MenuItem } from "./cMenuItem";
+export declare class MenuList extends Component {
+    private context;
+    private popupService;
+    private static TEMPLATE;
+    private static SEPARATOR_TEMPLATE;
+    private activeMenuItemParams;
+    private activeMenuItem;
+    private timerCount;
+    private showingChildMenu;
+    private childPopupRemoveFunc;
+    constructor();
+    clearActiveItem(): void;
+    addMenuItems(menuItems: [string | MenuItem], defaultMenuItems: {
+        [key: string]: MenuItem;
+    }): void;
+    addItem(params: MenuItem): void;
+    private mouseEnterItem(menuItemParams, menuItem);
+    private removeActiveItem();
+    private addHoverForChildPopup(menuItemParams, menuItem);
+    private showChildMenu(menuItemParams, menuItem);
+    addSeparator(): void;
+    private removeOldChildPopup();
+    destroy(): void;
+}
diff --git a/dist/lib/widgets/menuList.js b/dist/lib/widgets/menuList.js
new file mode 100644
index 0000000000000000000000000000000000000000..ee786720bd420fa5a74a0129869a0fbd432938ed
--- /dev/null
+++ b/dist/lib/widgets/menuList.js
@@ -0,0 +1,153 @@
+/**
+ * ag-grid - Advanced Data Grid / Data Table supporting Javascript / React / AngularJS / Web Components
+ * @version v4.0.0
+ * @link http://www.ag-grid.com/
+ * @license MIT
+ */
+var __extends = (this && this.__extends) || function (d, b) {
+    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
+    function __() { this.constructor = d; }
+    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+};
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
+    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
+    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
+    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
+    return c > 3 && r && Object.defineProperty(target, key, r), r;
+};
+var __metadata = (this && this.__metadata) || function (k, v) {
+    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
+};
+var component_1 = require("./component");
+var utils_1 = require('../utils');
+var context_1 = require("../context/context");
+var context_2 = require("../context/context");
+var popupService_1 = require("./popupService");
+var cMenuItem_1 = require("./cMenuItem");
+var MenuList = (function (_super) {
+    __extends(MenuList, _super);
+    function MenuList() {
+        _super.call(this, MenuList.TEMPLATE);
+        this.timerCount = 0;
+    }
+    MenuList.prototype.clearActiveItem = function () {
+        this.removeActiveItem();
+        this.removeOldChildPopup();
+    };
+    MenuList.prototype.addMenuItems = function (menuItems, defaultMenuItems) {
+        var _this = this;
+        if (utils_1.Utils.missing(menuItems)) {
+            return;
+        }
+        menuItems.forEach(function (listItem) {
+            if (listItem === 'separator') {
+                _this.addSeparator();
+            }
+            else {
+                var menuItem;
+                if (typeof listItem === 'string') {
+                    menuItem = defaultMenuItems[listItem];
+                }
+                else {
+                    menuItem = listItem;
+                }
+                _this.addItem(menuItem);
+            }
+        });
+    };
+    MenuList.prototype.addItem = function (params) {
+        var _this = this;
+        var cMenuItem = new cMenuItem_1.CMenuItem(params);
+        this.context.wireBean(cMenuItem);
+        this.getGui().appendChild(cMenuItem.getGui());
+        cMenuItem.addEventListener(cMenuItem_1.CMenuItem.EVENT_ITEM_SELECTED, function (event) {
+            if (params.childMenu) {
+                _this.showChildMenu(params, cMenuItem);
+            }
+            else {
+                _this.dispatchEvent(cMenuItem_1.CMenuItem.EVENT_ITEM_SELECTED, event);
+            }
+        });
+        cMenuItem.addGuiEventListener('mouseenter', this.mouseEnterItem.bind(this, params, cMenuItem));
+        cMenuItem.addGuiEventListener('mouseleave', function () { return _this.timerCount++; });
+        if (params.childMenu) {
+            this.addDestroyFunc(function () { return params.childMenu.destroy(); });
+        }
+    };
+    MenuList.prototype.mouseEnterItem = function (menuItemParams, menuItem) {
+        if (menuItemParams.disabled) {
+            return;
+        }
+        if (this.activeMenuItemParams !== menuItemParams) {
+            this.removeOldChildPopup();
+        }
+        this.removeActiveItem();
+        this.activeMenuItemParams = menuItemParams;
+        this.activeMenuItem = menuItem;
+        utils_1.Utils.addCssClass(this.activeMenuItem.getGui(), 'ag-menu-option-active');
+        if (menuItemParams.childMenu) {
+            this.addHoverForChildPopup(menuItemParams, menuItem);
+        }
+    };
+    MenuList.prototype.removeActiveItem = function () {
+        if (this.activeMenuItem) {
+            utils_1.Utils.removeCssClass(this.activeMenuItem.getGui(), 'ag-menu-option-active');
+            this.activeMenuItem = null;
+            this.activeMenuItemParams = null;
+        }
+    };
+    MenuList.prototype.addHoverForChildPopup = function (menuItemParams, menuItem) {
+        var _this = this;
+        var timerCountCopy = this.timerCount;
+        setTimeout(function () {
+            var shouldShow = timerCountCopy === _this.timerCount;
+            var showingThisMenu = _this.showingChildMenu === menuItemParams.childMenu;
+            if (shouldShow && !showingThisMenu) {
+                _this.showChildMenu(menuItemParams, menuItem);
+            }
+        }, 500);
+    };
+    MenuList.prototype.showChildMenu = function (menuItemParams, menuItem) {
+        this.removeOldChildPopup();
+        var ePopup = utils_1.Utils.loadTemplate('<div class="ag-menu"></div>');
+        ePopup.appendChild(menuItemParams.childMenu.getGui());
+        this.childPopupRemoveFunc = this.popupService.addAsModalPopup(ePopup, true);
+        this.popupService.positionPopupForMenu({
+            eventSource: menuItem.getGui(),
+            ePopup: ePopup
+        });
+        this.showingChildMenu = menuItemParams.childMenu;
+    };
+    MenuList.prototype.addSeparator = function () {
+        this.getGui().appendChild(utils_1.Utils.loadTemplate(MenuList.SEPARATOR_TEMPLATE));
+    };
+    MenuList.prototype.removeOldChildPopup = function () {
+        if (this.childPopupRemoveFunc) {
+            this.showingChildMenu.clearActiveItem();
+            this.childPopupRemoveFunc();
+            this.childPopupRemoveFunc = null;
+            this.showingChildMenu = null;
+        }
+    };
+    MenuList.prototype.destroy = function () {
+        this.removeOldChildPopup();
+        _super.prototype.destroy.call(this);
+    };
+    MenuList.TEMPLATE = '<div class="ag-menu-list"></div>';
+    MenuList.SEPARATOR_TEMPLATE = '<div class="ag-menu-separator">' +
+        '  <span class="ag-menu-separator-cell"></span>' +
+        '  <span class="ag-menu-separator-cell"></span>' +
+        '  <span class="ag-menu-separator-cell"></span>' +
+        '  <span class="ag-menu-separator-cell"></span>' +
+        '</div>';
+    __decorate([
+        context_1.Autowired('context'), 
+        __metadata('design:type', context_2.Context)
+    ], MenuList.prototype, "context", void 0);
+    __decorate([
+        context_1.Autowired('popupService'), 
+        __metadata('design:type', popupService_1.PopupService)
+    ], MenuList.prototype, "popupService", void 0);
+    return MenuList;
+})(component_1.Component);
+exports.MenuList = MenuList;