<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <link rel="stylesheet" href="style.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.14.3/ui-bootstrap-tpls.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-smart-table/2.1.6/smart-table.min.js"></script>
    <script src="smart-table-improved.js"></script>
    <script src="script.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.css" />
  </head>

  <body>
    <section ng-controller="demoCtrl">
      <table class="table" st-table="displayedCollection" st-safe-src="rowCollection" sti-table default-sort="age" default-sort-reverse="true">
        <thead>
          <tr>
            <th st-sort="id" st-skip-natural="true">ID</th>
            <th st-sort="name">Name</th>
            <th st-soft="firstName">First Name</th>
            <th st-sort="age">Age</th>
          </tr>
        </thead>
        <tbody>
          <tr ng-repeat="row in displayedCollection">
            <td>{{ row.id }}</td>
            <td>{{ row.name }}</td>
            <td>{{ row.firstName }}</td>
            <td>{{ row.age }}</td>
          </tr>
        </tbody>
        <tfoot>
          <tr>
            <td colspan="5" class="text-center">
              <div st-pagination="" st-items-by-page="15" st-displayed-pages="10"></div>
            </td>
          </tr>
        </tfoot>
      </table>
    </section>
  </body>

</html>
var appModule = angular.module('myApp', ['smart-table-improved', 'ui.bootstrap']);

appModule.controller('demoCtrl', ['$scope', function ($scope) {
  $scope.rowCollection = [
    {id: 1, name: 'Renard', firstName: 'Laurent', age: 66},
    {id: 2, name: 'Francoise', firstName: 'Frere', age: 99},
    {id: 3, name: 'Renard', firstName: 'Olivier', age: 33},
    {id: 4, name: 'Leponge', firstName: 'Bob', age: 22},
    {id: 5, name: 'Faivre', firstName: 'Blandine', age: 44},
    {id: 6, name: 'Seth', firstName: 'Blick', age: 54},
    {id: 7, name: 'Rebecca', firstName: 'Welch', age: 29},
    {id: 8, name: 'Felton', firstName: 'Auer', age: 58},
    {id: 9, name: 'Ramiro', firstName: 'Crona', age: 60},
    {id: 10, name: "Skiles", firstName: "Ashley", age: 46},
    {id: 11, name: "Jenkins", firstName: "Nicola", age: 10},
    {id: 12, name: "Cruickshank", firstName: "Jeanie", age: 87},
    {id: 13, name: "Gutkowski", firstName: "Elmo", age: 92},
    {id: 14, name: "Reichert", firstName: "Monique", age: 17},
    {id: 15, name: "Champlin", firstName: "Cristal", age: 82},
    {id: 16, name: "Ward", firstName: "Leilani", age: 79},
    {id: 17, name: "Kunde", firstName: "Yazmin", age: 87},
    {id: 18, name: "Langworth", firstName: "Ed", age: 71},
    {id: 19, name: "Hodkiewicz", firstName: "Talia", age: 77},
    {id: 20, name: "O'Kon", firstName: "Dortha", age: 50},
    {id: 21, name: "Jakubowski", firstName: "Vinnie", age: 32},
    {id: 22, name: "Conroy", firstName: "Otis", age: 9},
    {id: 23, name: "Sauer", firstName: "Phyllis", age: 85},
    {id: 24, name: "Senger", firstName: "Ike", age: 47},
    {id: 25, name: "Durgan", firstName: "Delfina", age: 67},
    {id: 26, name: "Herman", firstName: "Gennaro", age: 52},
    {id: 27, name: "Fay", firstName: "Antonina", age: 45},
    {id: 28, name: "Swaniawski", firstName: "Hobart", age: 11},
    {id: 29, name: "Lockman", firstName: "Nathaniel", age: 5},
    {id: 30, name: "Dach", firstName: "Hassan", age: 36},
    {id: 31, name: "Christiansen", firstName: "Reilly", age: 42},
    {id: 32, name: "Ruecker", firstName: "Abe", age: 75},
    {id: 33, name: "Barton", firstName: "Arlie", age: 18},
    {id: 34, name: "Frami", firstName: "Ada", age: 88},
    {id: 35, name: "Morissette", firstName: "Kathlyn", age: 93},
    {id: 36, name: "Gusikowski", firstName: "Jeanette", age: 98},
    {id: 37, name: "Rempel", firstName: "Nichole", age: 50},
    {id: 38, name: "Swaniawski", firstName: "Selena", age: 76},
    {id: 39, name: "Koch", firstName: "Madaline", age: 66},
    {id: 40, name: "Wisoky", firstName: "Jeffrey", age: 49},
    {id: 41, name: "Crooks", firstName: "Marcelle", age: 15},
    {id: 42, name: "Ebert", firstName: "Elbert", age: 5},
    {id: 43, name: "Stark", firstName: "Sarina", age: 80},
    {id: 44, name: "Reynolds", firstName: "Weldon", age: 97},
    {id: 45, name: "Schmitt", firstName: "Ettie", age: 11},
    {id: 46, name: "Wyman", firstName: "Timmy", age: 70},
    {id: 47, name: "Hessel", firstName: "Irving", age: 71},
    {id: 48, name: "Corkery", firstName: "Callie", age: 59},
    {id: 49, name: "Wyman", firstName: "Abdul", age: 45},
    {id: 50, name: "Treutel", firstName: "Ova", age: 79},
    {id: 51, name: "Lind", firstName: "Mikel", age: 59},
    {id: 52, name: "Schuppe", firstName: "Rogers", age: 29},
    {id: 53, name: "Heaney", firstName: "Kiana", age: 78},
    {id: 54, name: "Jakubowski", firstName: "Allen", age: 94},
    {id: 55, name: "Kilback", firstName: "Gianni", age: 5},
    {id: 56, name: "Kunde", firstName: "Telly", age: 39},
    {id: 57, name: "Jenkins", firstName: "Johnathon", age: 24},
    {id: 58, name: "Zieme", firstName: "Jacinthe", age: 92},
    {id: 59, name: "Stamm", firstName: "Mitchell", age: 87},
    {id: 60, name: "Homenick", firstName: "Seamus", age: 43},
    {id: 61, name: "Morissette", firstName: "Nathanial", age: 15},
    {id: 62, name: "Heaney", firstName: "Itzel", age: 31},
    {id: 63, name: "Champlin", firstName: "Wilburn", age: 84},
    {id: 64, name: "Hackett", firstName: "Kaya", age: 32},
    {id: 65, name: "Bechtelar", firstName: "Abbie", age: 30},
    {id: 66, name: "Armstrong", firstName: "Stephan", age: 17},
    {id: 67, name: "Weber", firstName: "Jayme", age: 82},
    {id: 68, name: "Streich", firstName: "Ora", age: 92},
    {id: 69, name: "Hilll", firstName: "Ruthie", age: 90},
    {id: 70, name: "Brekke", firstName: "Lewis", age: 5},
    {id: 71, name: "Feeney", firstName: "Alba", age: 45},
    {id: 72, name: "McCullough", firstName: "Sienna", age: 24},
    {id: 73, name: "Bogan", firstName: "Heidi", age: 39},
    {id: 74, name: "Johnson", firstName: "Sherwood", age: 2},
    {id: 75, name: "Bergnaum", firstName: "Jaeden", age: 8},
    {id: 76, name: "Hills", firstName: "Amari", age: 27},
    {id: 77, name: "Maggio", firstName: "Haylie", age: 56},
    {id: 78, name: "Barton", firstName: "Monserrat", age: 72},
    {id: 79, name: "Marvin", firstName: "Monserrat", age: 93},
    {id: 80, name: "Morissette", firstName: "Ezra", age: 40},
    {id: 81, name: "Schuster", firstName: "Kari", age: 37},
    {id: 82, name: "Dooley", firstName: "Sister", age: 22},
    {id: 83, name: "Williamson", firstName: "Monroe", age: 32},
    {id: 84, name: "Wisoky", firstName: "Delbert", age: 30},
    {id: 85, name: "Hoppe", firstName: "Ludwig", age: 45},
    {id: 86, name: "Kuphal", firstName: "Nina", age: 66},
    {id: 87, name: "Casper", firstName: "Gretchen", age: 26},
    {id: 88, name: "Prosacco", firstName: "Lera", age: 82},
    {id: 89, name: "Little", firstName: "Velda", age: 55},
    {id: 90, name: "Jakubowski", firstName: "Stanford", age: 30},
    {id: 91, name: "Turner", firstName: "Cornell", age: 60},
    {id: 92, name: "Ankunding", firstName: "Oswaldo", age: 49},
    {id: 93, name: "Cummerata", firstName: "Hortense", age: 89},
    {id: 94, name: "Denesik", firstName: "Christy", age: 23},
    {id: 95, name: "Moen", firstName: "Berenice", age: 92},
    {id: 96, name: "Bernier", firstName: "Carolyn", age: 26},
    {id: 97, name: "Padberg", firstName: "Augustus", age: 94},
    {id: 98, name: "Farrell", firstName: "Libby", age: 41},
    {id: 99, name: "Lemke", firstName: "Skyla", age: 74},
    {id: 100, name: "O'Conner", firstName: "Jordy", age: 33},
    {id: 101, name: "Moore", firstName: "Sarai", age: 78},
    {id: 102, name: "Orn", firstName: "Adelle", age: 78},
    {id: 103, name: "Feeney", firstName: "Lucienne", age: 84},
    {id: 104, name: "Runolfsdottir", firstName: "Mariane", age: 49},
    {id: 105, name: "Jast", firstName: "Myrna", age: 19},
    {id: 106, name: "Blick", firstName: "Toni", age: 98},
    {id: 107, name: "Kassulke", firstName: "Mohamed", age: 51},
    {id: 108, name: "Hermiston", firstName: "Okey", age: 51},
    {id: 109, name: "Hamill", firstName: "Eli", age: 92},
    {id: 110, name: "Macejkovic", firstName: "Elise", age: 86},
    {id: 111, name: "Wyman", firstName: "Dewayne", age: 75},
    {id: 112, name: "Sporer", firstName: "Barbara", age: 92},
    {id: 113, name: "Schultz", firstName: "Danial", age: 44},
    {id: 114, name: "Ritchie", firstName: "Sallie", age: 9},
    {id: 115, name: "Hartmann", firstName: "Nyasia", age: 80},
    {id: 116, name: "Harvey", firstName: "Helen", age: 86},
    {id: 117, name: "Connelly", firstName: "Jeramy", age: 23},
    {id: 118, name: "Grant", firstName: "Mattie", age: 14},
    {id: 119, name: "Lemke", firstName: "Madisyn", age: 4},
    {id: 120, name: "Nolan", firstName: "Cristobal", age: 75},
    {id: 121, name: "Sawayn", firstName: "Teagan", age: 34},
    {id: 122, name: "Murray", firstName: "Robbie", age: 33},
    {id: 123, name: "Wilkinson", firstName: "King", age: 29},
    {id: 124, name: "Yost", firstName: "Jamison", age: 51},
    {id: 125, name: "Armstrong", firstName: "Natalia", age: 6},
    {id: 126, name: "Crist", firstName: "Dino", age: 5},
    {id: 127, name: "Emmerich", firstName: "Rosalia", age: 65},
    {id: 128, name: "Balistreri", firstName: "Prudence", age: 38},
    {id: 129, name: "Wehner", firstName: "Lucinda", age: 40},
    {id: 130, name: "Kunde", firstName: "Angelina", age: 84},
    {id: 131, name: "Abshire", firstName: "Walter", age: 72},
    {id: 132, name: "Dibbert", firstName: "Jabari", age: 96},
    {id: 133, name: "Dicki", firstName: "Jean", age: 94},
    {id: 134, name: "Simonis", firstName: "Webster", age: 62},
    {id: 135, name: "Eichmann", firstName: "Caleb", age: 70},
    {id: 136, name: "Thompson", firstName: "Oran", age: 92},
    {id: 137, name: "Bauch", firstName: "Jackeline", age: 64},
    {id: 138, name: "Ryan", firstName: "Nasir", age: 5},
    {id: 139, name: "Bruen", firstName: "Melba", age: 45},
    {id: 140, name: "Larson", firstName: "Ross", age: 97},
    {id: 141, name: "Kreiger", firstName: "Shawn", age: 56},
    {id: 142, name: "Jerde", firstName: "Idell", age: 92},
    {id: 143, name: "Metz", firstName: "Audie", age: 50},
    {id: 144, name: "Jenkins", firstName: "Dahlia", age: 57},
    {id: 145, name: "Russel", firstName: "Domenic", age: 23},
    {id: 146, name: "Wisozk", firstName: "Dillon", age: 5},
    {id: 147, name: "Konopelski", firstName: "Lacy", age: 35},
    {id: 148, name: "Ryan", firstName: "Arnoldo", age: 30},
    {id: 149, name: "Kulas", firstName: "Edison", age: 15},
    {id: 150, name: "Hilpert", firstName: "Wade", age: 46},
    {id: 151, name: "Kshlerin", firstName: "Adriana", age: 89},
    {id: 152, name: "Mayert", firstName: "Joan", age: 87},
    {id: 153, name: "MacGyver", firstName: "Reynold", age: 73},
    {id: 154, name: "Lesch", firstName: "Freddy", age: 26},
    {id: 155, name: "Powlowski", firstName: "Gladys", age: 40},
    {id: 156, name: "Roob", firstName: "Jeff", age: 10},
    {id: 157, name: "Kessler", firstName: "Elisa", age: 75},
    {id: 158, name: "Quitzon", firstName: "Carlie", age: 52},
    {id: 159, name: "Langosh", firstName: "Daisha", age: 63},
    {id: 160, name: "Volkman", firstName: "Shaina", age: 11},
    {id: 161, name: "Zieme", firstName: "Jasen", age: 98},
    {id: 162, name: "Huels", firstName: "Thea", age: 47},
    {id: 163, name: "Gutmann", firstName: "Cristina", age: 60},
    {id: 164, name: "Goodwin", firstName: "Madaline", age: 37},
    {id: 165, name: "Mann", firstName: "Isaac", age: 36},
    {id: 166, name: "Haag", firstName: "Julianne", age: 4},
    {id: 167, name: "Bergnaum", firstName: "Enrique", age: 80},
    {id: 168, name: "Kilback", firstName: "Moses", age: 92},
    {id: 169, name: "Kuhn", firstName: "Devante", age: 10},
    {id: 170, name: "Marks", firstName: "Kelsie", age: 95},
    {id: 171, name: "Spinka", firstName: "Walton", age: 47},
    {id: 172, name: "Nader", firstName: "Cordell", age: 30},
    {id: 173, name: "Paucek", firstName: "Carmelo", age: 33},
    {id: 174, name: "Gerhold", firstName: "Thomas", age: 33},
    {id: 175, name: "Casper", firstName: "Albertha", age: 3},
    {id: 176, name: "Medhurst", firstName: "Lucinda", age: 17},
    {id: 177, name: "Brown", firstName: "Garrett", age: 53},
    {id: 178, name: "Sporer", firstName: "Eldon", age: 29},
    {id: 179, name: "Ernser", firstName: "Katharina", age: 80},
    {id: 180, name: "Schultz", firstName: "Edd", age: 61},
    {id: 181, name: "Bruen", firstName: "Georgiana", age: 6},
    {id: 182, name: "Hane", firstName: "Estefania", age: 66},
    {id: 183, name: "Denesik", firstName: "Armani", age: 42},
    {id: 184, name: "Larson", firstName: "Euna", age: 19},
    {id: 185, name: "Waelchi", firstName: "Jammie", age: 92},
    {id: 186, name: "Durgan", firstName: "Bernhard", age: 93},
    {id: 187, name: "Altenwerth", firstName: "Zoe", age: 70},
    {id: 188, name: "Grady", firstName: "Reymundo", age: 58},
    {id: 189, name: "Cruickshank", firstName: "Trent", age: 78},
    {id: 190, name: "Jakubowski", firstName: "Raheem", age: 92},
    {id: 191, name: "Lueilwitz", firstName: "Alaina", age: 8},
    {id: 192, name: "Cormier", firstName: "Aurelia", age: 35},
    {id: 193, name: "Runolfsson", firstName: "Terrill", age: 39},
    {id: 194, name: "Miller", firstName: "Jeramie", age: 99},
    {id: 195, name: "Schowalter", firstName: "Porter", age: 59},
    {id: 196, name: "Parisian", firstName: "Baby", age: 63},
    {id: 197, name: "Kautzer", firstName: "Kyler", age: 4},
    {id: 198, name: "Kreiger", firstName: "Godfrey", age: 97},
    {id: 199, name: "Heathcote", firstName: "Sophia", age: 81}
  ];
}]);
.st-sort-ascent:before{
  content: '\25B2';
}

.st-sort-descent:before{
  content: '\25BC';
}
/*
 * angular-smart-table-improved v0.4.1
 * https://github.com/timonwong/smart-table-improved
 *
 * (c) 2016 Timon Wong
 * License: MIT
 */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('angular')) :
  typeof define === 'function' && define.amd ? define(['angular'], factory) :
  factory(global.angular);
}(this, function (angular) { 'use strict';

  angular.module('smart-table-improved.templates', []);

  angular.module('smart-table-improved', ['smart-table', 'smart-table-improved.templates']);

  var prefix = 'stiTable';

  var EventNames = {
    rowSelected: prefix + ':rowSelected'
  };

  angular.module('smart-table-improved').controller('StiTableController', StiTableController).directive('stiTable', stiTable);

  /**
   * @ngdoc controller
   * @name smart-table-improved.controller:StiTableController
   * @description
   * Controller used by `stiTable`
   */
  StiTableController.$inject = ['$scope'];
  function StiTableController($scope) {
    var ctrl = this;

    $scope.$stiSelected = {};
    $scope.$stiNumSelected = 0;

    ctrl.isSelected = isSelected;
    ctrl.select = select;
    ctrl.updateSelectedStatus = updateSelectedStatus;

    /**
     * Return true if the row is selected.
     * @param {Object} row
     * @returns {Boolean}
     */
    function isSelected(row) {
      var rowIdField = $scope.$stiRowIdField;
      var rowState = $scope.$stiSelected[row[rowIdField]];
      return rowState && rowState.checked;
    }

    /**
     * Set row checked state
     * @param {Object} row
     * @param {Boolean} checkedState
     * @param {Boolean} broadcast (default false)
     */
    function select(row, checkedState) {
      var _ref = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];

      var _ref$broadcast = _ref.broadcast;
      var broadcast = _ref$broadcast === undefined ? false : _ref$broadcast;

      var rowIdField = $scope.$stiRowIdField;
      $scope.$stiSelected[row[rowIdField]] = {
        checked: checkedState,
        row: row
      };

      if (checkedState) {
        $scope.$stiNumSelected++;
      } else {
        $scope.$stiNumSelected--;
      }

      if (broadcast) {
        var rowObj = {
          row: row,
          checked: checkedState
        };
        $scope.$broadcast(EventNames.rowSelected, rowObj);
      }
    }

    /**
     * Update current selection status from latest collection
     */
    function updateSelectedStatus(collection) {
      var rowIdField = $scope.$stiRowIdField;
      var lastSelected = $scope.$stiSelected;
      var nextSelected = {};
      var nextNumSelected = 0;

      angular.forEach(collection, function (item) {
        var id = item[rowIdField];
        var selectedItem = lastSelected[id];

        if (!selectedItem) {
          return;
        }

        nextSelected[id] = selectedItem;
        if (selectedItem.checked) {
          nextNumSelected++;
        }
      });

      $scope.$stiNumSelected = nextNumSelected;
      $scope.$stiSelected = nextSelected;
    }
  }

  /**
   * @ngdoc directive
   * @name smart-table-improved.directive:stiTable
   * @restrict A
   * @scope true
   *
   * @description
   * The stiTable directive is a helper to stTable directive.
   *
   * @element table st-table='rowCollection'
   * @param {string} defaultSort
   * @param {Boolean} defaultSortReverse (default false)
   * @param {Expression} onPagination Expression to evaluate upon pagination state
   * changes. (Pagination object is available as $pagination, with `currentPage`,
   * `numberOfPages` and `totalItemCount` inside)
   * @param {string} rowIdField (default '$$hashkey')
   * @param {string|Boolean} trackSelectedMode (default false)
   *
   */
  stiTable.$inject = ['$parse'];
  function stiTable($parse) {
    return {
      restrict: 'A',
      require: ['stTable', 'stiTable'],
      scope: true,
      controller: 'StiTableController',
      controllerAs: 'stiTableCtrl',
      link: link
    };

    function link(scope, element, attrs, ctrls) {
      var stTableCtrl = ctrls[0];
      var stiTableCtrl = ctrls[1];

      scope.$stiRowIdField = angular.isDefined(attrs.rowIdField) ? attrs.rowIdField : '$$hashKey';
      if (attrs.trackSelectedMode === 'all') {
        // Track all collection (from st-safe-src attribute)
        scope.$watchCollection(attrs.stSafeSrc, stiTableCtrl.updateSelectedStatus.bind(stiTableCtrl));
      } else if (attrs.trackSelectedMode === 'displayed') {
        // Track displayed collection only
        scope.$watchCollection(attrs.stTable, stiTableCtrl.updateSelectedStatus.bind(stiTableCtrl));
      }

      if (attrs.defaultSort) {
        var reverse = angular.isDefined(attrs.defaultSortReverse) ? scope.$parent.$eval(attrs.defaultSortReverse) : false;
        stTableCtrl.sortBy(attrs.defaultSort, reverse);
      }

      var onPaginationHandler = undefined;
      if (attrs.onPagination) {
        onPaginationHandler = $parse(attrs.onPagination);

        scope.$watch(function () {
          return stTableCtrl.tableState().pagination;
        }, handlePaginationChange, true);
      }

      function handlePaginationChange() {
        var paginationState = stTableCtrl.tableState().pagination;
        var pagination = {
          currentPage: Math.floor(paginationState.start / paginationState.number) + 1,
          numberOfPages: paginationState.numberOfPages,
          totalItemCount: paginationState.totalItemCount
        };

        onPaginationHandler(scope, { $pagination: pagination });
      }
    }
  }

  angular.module('smart-table-improved').constant('stiPaginationConfig', {
    itemsPerPage: 10,
    maxSize: 10,
    templateUrl: 'sti/template/sti-pagination.html'
  }).directive('stiPagination', stiPagination);

  stiPagination.$inject = ['stiPaginationConfig'];
  function stiPagination(stiPaginationConfig) {
    return {
      restrict: 'EA',
      require: '^stTable',
      scope: {
        itemsPerPage: '=?',
        maxSize: '=?',
        onPageChange: '&',
        autoHide: '=?'
      },
      templateUrl: function templateUrl(element, attrs) {
        return attrs.templateUrl || stiPaginationConfig.templateUrl;
      },
      link: link
    };

    function link(scope, element, attrs, ctrl) {
      scope.itemsPerPage = scope.itemsPerPage ? scope.itemsPerPage : stiPaginationConfig.itemsPerPage;
      scope.maxSize = scope.maxSize ? scope.maxSize : stiPaginationConfig.maxSize;

      scope.autoHide = angular.isDefined(scope.autoHide) ? scope.autoHide : false;
      scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : true;
      scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : false;
      scope.paginationClass = attrs.paginationClass || '';

      var paginationRange = Math.max(scope.maxSize, 5);
      scope.pages = [];
      scope.pagination = {
        last: 1,
        current: 1
      };
      scope.range = {
        lower: 1,
        upper: 1
      };

      // table state --> view
      scope.$watch(function () {
        return ctrl.tableState().pagination;
      }, redraw, true);

      // scope --> table state  (--> view)
      scope.$watch('itemsPerPage', function (newValue, oldValue) {
        if (newValue !== oldValue) {
          scope.selectPage(1);
        }
      });

      scope.$watch('maxSize', redraw);

      // view -> table state
      scope.selectPage = function (page) {
        if (page > 0 && page <= scope.numPages) {
          ctrl.slice((page - 1) * scope.itemsPerPage, scope.itemsPerPage);
        }
      };

      if (!ctrl.tableState().pagination.number) {
        ctrl.slice(0, scope.itemsPerPage);
      }

      /**
       * Custom "track by" function which allows for duplicate "..." entries on long lists,
       * yet fixes the problem of wrongly-highlighted links which happens when using
       * "track by $index" - see https://github.com/michaelbromley/angularUtils/issues/153
       * @param id
       * @param index
       * @returns {string}
       */
      scope.tracker = function (id, index) {
        return id + '_' + index;
      };

      function redraw() {
        var paginationState = ctrl.tableState().pagination;
        var prevPage = scope.currentPage;

        scope.currentPage = Math.floor(paginationState.start / paginationState.number) + 1;
        scope.totalItemCount = paginationState.totalItemCount;
        scope.numPages = paginationState.numberOfPages;
        scope.pages = generatePagesArray(scope.currentPage, paginationState.totalItemCount, scope.itemsPerPage, paginationRange);

        scope.range.lower = paginationState.start + 1;
        scope.range.upper = paginationState.start + paginationState.number + 1;

        if (prevPage !== scope.currentPage) {
          scope.onPageChange({ newPage: scope.currentPage });
        }
      }
    }

    /**
     * Generate an array of page numbers (or the '...' string) which is used in an ng-repeat to generate the
     * links used in pagination
     *
     * @param currentPage
     * @param rowsPerPage
     * @param paginationRange
     * @param collectionLength
     * @returns {Array}
     */
    function generatePagesArray(currentPage, collectionLength, rowsPerPage, paginationRange) {
      var pages = [];
      var totalPages = Math.ceil(collectionLength / rowsPerPage);
      var halfWay = Math.ceil(paginationRange / 2);
      var position = undefined;

      if (currentPage <= halfWay) {
        position = 'start';
      } else if (totalPages - halfWay < currentPage) {
        position = 'end';
      } else {
        position = 'middle';
      }

      var ellipsesNeeded = paginationRange < totalPages;
      var i = 1;
      while (i <= totalPages && i <= paginationRange) {
        var pageNumber = calculatePageNumber(i, currentPage, paginationRange, totalPages);

        var openingEllipsesNeeded = i === 2 && (position === 'middle' || position === 'end');
        var closingEllipsesNeeded = i === paginationRange - 1 && (position === 'middle' || position === 'start');
        if (ellipsesNeeded && (openingEllipsesNeeded || closingEllipsesNeeded)) {
          pages.push('...');
        } else {
          pages.push(pageNumber);
        }
        i++;
      }
      return pages;
    }

    /**
     * Given the position in the sequence of pagination links [i], figure out what page number corresponds to that position.
     *
     * @param i
     * @param currentPage
     * @param paginationRange
     * @param totalPages
     * @returns {*}
     */
    function calculatePageNumber(i, currentPage, paginationRange, totalPages) {
      var halfWay = Math.ceil(paginationRange / 2);
      if (i === paginationRange) {
        return totalPages;
      } else if (i === 1) {
        return i;
      } else if (paginationRange < totalPages) {
        if (totalPages - halfWay < currentPage) {
          return totalPages - paginationRange + i;
        } else if (halfWay < currentPage) {
          return currentPage - halfWay + i;
        } else {
          return i;
        }
      } else {
        return i;
      }
    }
  }

  angular.module('smart-table-improved').directive('stiSelect', stiSelect);

  stiSelect.$inject = [];
  function stiSelect() {
    return {
      restrict: 'A',
      require: '^stiTable',
      scope: {
        row: '=stiSelect'
      },
      link: link
    };

    function link(scope, element, attrs, ctrl) {
      var stiTableCtrl = ctrl;

      element.on('click', clickHandler);

      // Select or unselect row
      function clickHandler() {
        scope.$apply(function () {
          var checkedState = element.prop('checked');
          stiTableCtrl.select(scope.row, checkedState, { broadcast: true });
        });
      }
    }
  }

  angular.module('smart-table-improved').directive('stiSelectAll', stiSelectAll);

  stiSelectAll.$inject = [];
  function stiSelectAll() {
    return {
      restrict: 'A',
      require: ['^stiTable', '^stTable'],
      scope: {
        rows: '=stiSelectAll'
      },
      link: link
    };

    function link(scope, element, attrs, ctrls) {
      var stiTableCtrl = ctrls[0];
      var stTableCtrl = ctrls[1];

      element.on('click', clickHandler);

      // Watch the table state for changes (sort, filter, pagination, etc)
      scope.$watch(function () {
        return stTableCtrl.tableState();
      }, updateRowCheckState, true);
      // Watch the row length for added/removed rows
      scope.$watch('rows.length', updateRowCheckState);
      // Watch for row selection
      scope.$on(EventNames.rowSelected, updateRowCheckState);

      // Toggle checked state for "select all" checkbox
      function clickHandler() {
        scope.$apply(function () {
          var checked = element.prop('checked');
          angular.forEach(scope.rows, function (row) {
            var selected = stiTableCtrl.isSelected(row);
            if (selected !== checked) {
              stiTableCtrl.select(row, checked);
            }
          });
        });
      }

      // Update "select all" checkbox when table state changes
      function updateRowCheckState() {
        var visibleRows = scope.rows;
        var numVisibleRows = visibleRows.length;
        var checkedCount = visibleRows.filter(stiTableCtrl.isSelected).length;
        element.prop('checked', numVisibleRows > 0 && numVisibleRows === checkedCount);
      }
    }
  }

  angular.module("smart-table-improved.templates").run(["$templateCache", function ($templateCache) {
    $templateCache.put("sti/template/sti-pagination.html", "<ul class=\"pagination {{ paginationClass }}\" ng-if=\"pages.length > 1 || !autoHide\">\n  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: currentPage === 1}\">\n    <a href=\"\" ng-click=\"selectPage(1)\">&laquo;</a>\n  </li>\n  <li ng-if=\"::directionLinks\" ng-class=\"{disabled: currentPage === 1}\">\n    <a href=\"\" ng-click=\"selectPage(currentPage - 1)\">&lsaquo;</a>\n  </li>\n  <li ng-repeat=\"pageNumber in pages track by tracker(pageNumber, $index)\" ng-class=\"{active: currentPage === pageNumber, disabled: pageNumber === \'...\'}\" class=\"pagenumbers\">\n    <a href=\"\" ng-click=\"selectPage(pageNumber)\">{{ pageNumber }}</a>\n  </li>\n  <li ng-if=\"::directionLinks\" ng-class=\"{disabled: currentPage === numPages}\">\n    <a href=\"\" ng-click=\"selectPage(currentPage + 1)\">&rsaquo;</a>\n  </li>\n  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: currentPage === numPages}\">\n    <a href=\"\" ng-click=\"selectPage(numPages)\">&raquo;</a>\n  </li>\n</ul>\n");
  }]);

}));