text: /* Column module */
  
  (function (global, angular) {
      "use strict";
      var smartTableColumnModule = angular.module('smartTable.column', ['smartTable.templateUrlList']).constant('DefaultColumnConfiguration', {
          isSortable: true,
          isEditable: false,
          type: 'text',
  
  
          //it is useless to have that empty strings, but it reminds what is available
          headerTemplateUrl: '',
          map: '',
          label: '',
          sortPredicate: '',
          formatFunction: '',
          formatParameter: '',
          filterPredicate: '',
          cellTemplateUrl: '',
          headerClass: '',
          cellClass: ''
      });
  
      function ColumnProvider(DefaultColumnConfiguration, templateUrlList) {
  
          function Column(config) {
              if (!(this instanceof Column)) {
                  return new Column(config);
              }
              angular.extend(this, config);
          }
  
          this.setDefaultOption = function (option) {
              angular.extend(Column.prototype, option);
          };
  
          DefaultColumnConfiguration.headerTemplateUrl = templateUrlList.defaultHeader;
          this.setDefaultOption(DefaultColumnConfiguration);
  
          this.$get = function () {
              return Column;
          };
      }
  
      ColumnProvider.$inject = ['DefaultColumnConfiguration', 'templateUrlList'];
      smartTableColumnModule.provider('Column', ColumnProvider);
  
      //make it global so it can be tested
      global.ColumnProvider = ColumnProvider;
  })(window, angular);
  
  
  
  /* Directives */
  (function (angular) {
      "use strict";
      angular.module('smartTable.directives', ['smartTable.templateUrlList', 'smartTable.templates'])
          .directive('smartTable', ['templateUrlList', 'DefaultTableConfiguration', function (templateList, defaultConfig) {
              return {
                  restrict: 'EA',
                  scope: {
                      columnCollection: '=columns',
                      dataCollection: '=rows',
                      config: '='
                  },
                  replace: 'true',
                  templateUrl: templateList.smartTable,
                  controller: 'TableCtrl',
                  link: function (scope, element, attr, ctrl) {
  
                      var templateObject;
  
                      scope.$watch('config', function (config) {
                          var newConfig = angular.extend({}, defaultConfig, config),
                              length = scope.columns !== undefined ? scope.columns.length : 0;
  
                          ctrl.setGlobalConfig(newConfig);
  
                          //remove the checkbox column if needed
                          if (newConfig.selectionMode !== 'multiple' || newConfig.displaySelectionCheckbox !== true) {
                              for (var i = length - 1; i >= 0; i--) {
                                  if (scope.columns[i].isSelectionColumn === true) {
                                      ctrl.removeColumn(i);
                                  }
                              }
                          } else {
                              //add selection box column if required
                              ctrl.insertColumn({cellTemplateUrl: templateList.selectionCheckbox, headerTemplateUrl: templateList.selectAllCheckbox, isSelectionColumn: true}, 0);
                          }
                      }, true);
  
                      //insert columns from column config
                      scope.$watch('columnCollection', function (oldValue, newValue) {
  
                          ctrl.clearColumns();
  
                          if (scope.columnCollection) {
                              for (var i = 0, l = scope.columnCollection.length; i < l; i++) {
                                  ctrl.insertColumn(scope.columnCollection[i]);
                              }
                          } else {
                              //or guess data Structure
                              if (scope.dataCollection && scope.dataCollection.length > 0) {
                                  templateObject = scope.dataCollection[0];
                                  angular.forEach(templateObject, function (value, key) {
                                      if (key[0] != '$') {
                                          ctrl.insertColumn({label: key, map: key});
                                      }
                                  });
                              }
                          }
                      }, true);
  
                      //if item are added or removed into the data model from outside the grid
                      scope.$watch('dataCollection.length', function (oldValue, newValue) {
                          if (oldValue !== newValue) {
                              ctrl.sortBy();//it will trigger the refresh... some hack ?
                          }
                      });
                  }
              };
          }])
          //just to be able to select the row
          .directive('smartTableDataRow', function () {
  
              return {
                  require: '^smartTable',
                  restrict: 'C',
                  link: function (scope, element, attr, ctrl) {
                      
                      var _config;
                      if ((_config = scope.config) != null) {
                          if (typeof _config.rowFunction === "function") {
                              _config.rowFunction(scope, element, attr, ctrl);
                          }
                      }
                      
                      element.bind('click', function () {
                          scope.$apply(function () {
                              ctrl.toggleSelection(scope.dataRow);
                          })
                      });
                  }
              };
          })
          //header cell with sorting functionality or put a checkbox if this column is a selection column
          .directive('smartTableHeaderCell',function () {
              return {
                  restrict: 'C',
                  require: '^smartTable',
                  link: function (scope, element, attr, ctrl) {
                      element.bind('click', function () {
                          scope.$apply(function () {
                              ctrl.sortBy(scope.column);
                          });
                      })
                  }
              };
          }).directive('smartTableSelectAll', function () {
              return {
                  restrict: 'C',
                  require: '^smartTable',
                  link: function (scope, element, attr, ctrl) {
                      element.bind('click', function (event) {
                          ctrl.toggleSelectionAll(element[0].checked === true);
                      })
                  }
              };
          })
          //credit to Valentyn shybanov : http://stackoverflow.com/questions/14544741/angularjs-directive-to-stoppropagation
          .directive('stopEvent', function () {
              return {
                  restrict: 'A',
                  link: function (scope, element, attr) {
                      element.bind(attr.stopEvent, function (e) {
                          e.stopPropagation();
                      });
                  }
              }
          })
          //the global filter
          .directive('smartTableGlobalSearch', ['templateUrlList', function (templateList) {
              return {
                  restrict: 'C',
                  require: '^smartTable',
                  scope: {
                      columnSpan: '@'
                  },
                  templateUrl: templateList.smartTableGlobalSearch,
                  replace: false,
                  link: function (scope, element, attr, ctrl) {
  
                      scope.searchValue = '';
  
                      scope.$watch('searchValue', function (value) {
                          //todo perf improvement only filter on blur ?
                          ctrl.search(value);
                      });
                  }
              }
          }])
          //a customisable cell (see templateUrl) and editable
          //TODO check with the ng-include strategy
          .directive('smartTableDataCell', ['$filter', '$http', '$templateCache', '$compile', '$parse', function (filter, http, templateCache, compile, parse) {
              return {
                  restrict: 'C',
                  link: function (scope, element) {
                      var
                          column = scope.column,
                          isSimpleCell = !column.isEditable,
                          row = scope.dataRow,
                          format = filter('format'),
                          getter = parse(column.map),
                          childScope;
  
                      //can be useful for child directives
                      scope.$watch('dataRow', function (value) {
                          scope.formatedValue = format(getter(row), column.formatFunction, column.formatParameter);
                          if (isSimpleCell === true) {
                              element.html(scope.formatedValue);
                          }
                      }, true);
  
                      function defaultContent() {
                          if (column.isEditable) {
                              element.html('<div editable-cell="" row="dataRow" column="column" type="column.type"></div>');
                              compile(element.contents())(scope);
                          } else {
                              element.html(scope.formatedValue);
                          }
                      }
  
                      scope.$watch('column.cellTemplateUrl', function (value) {
  
                          if (value) {
                              //we have to load the template (and cache it) : a kind of ngInclude
                              http.get(value, {cache: templateCache}).success(function (response) {
  
                                  isSimpleCell = false;
  
                                  //create a scope
                                  childScope = scope.$new();
                                  //compile the element with its new content and new scope
                                  element.html(response);
                                  compile(element.contents())(childScope);
                              }).error(defaultContent);
  
                          } else {
                              defaultContent();
                          }
                      });
                  }
              };
          }])
          //directive that allows type to be bound in input
          .directive('inputType', function () {
              return {
                  restrict: 'A',
                  priority: 1,
                  link: function (scope, ielement, iattr) {
                      //force the type to be set before inputDirective is called
                      var type = scope.$eval(iattr.type);
                      iattr.$set('type', type);
                  }
              };
          })
          //an editable content in the context of a cell (see row, column)
          .directive('editableCell', ['templateUrlList', '$parse', function (templateList, parse) {
              return {
                  restrict: 'EA',
                  require: '^smartTable',
                  templateUrl: templateList.editableCell,
                  scope: {
                      row: '=',
                      column: '=',
                      type: '='
                  },
                  replace: true,
                  link: function (scope, element, attrs, ctrl) {
                      var form = angular.element(element.children()[1]),
                          input = angular.element(form.children()[0]),
                          getter = parse(scope.column.map);
  
                      //init values
                      scope.isEditMode = false;
                      scope.$watch('row', function () {
                          scope.value = getter(scope.row);
                      }, true);
  
  
                      scope.submit = function () {
                          //update model if valid
                          if (scope.myForm.$valid === true) {
                              ctrl.updateDataRow(scope.row, scope.column.map, scope.value);
                              ctrl.sortBy();//it will trigger the refresh...  (ie it will sort, filter, etc with the new value)
                          }
                          scope.toggleEditMode();
                      };
  
                      scope.toggleEditMode = function () {
                          scope.value = getter(scope.row);
                          scope.isEditMode = scope.isEditMode !== true;
                      };
  
                      scope.$watch('isEditMode', function (newValue) {
                          if (newValue === true) {
                              input[0].select();
                              input[0].focus();
                          }
                      });
  
                      input.bind('blur', function () {
                          scope.$apply(function () {
                              scope.submit();
                          });
                      });
                  }
              };
          }]);
  })(angular);
  
  /* Filters */
  (function (angular) {
      "use strict";
      angular.module('smartTable.filters', []).
          constant('DefaultFilters', ['currency', 'date', 'json', 'lowercase', 'number', 'uppercase']).
          filter('format', ['$filter', 'DefaultFilters', function (filter, defaultfilters) {
              return function (value, formatFunction, filterParameter) {
  
                  var returnFunction;
  
                  if (formatFunction && angular.isFunction(formatFunction)) {
                      returnFunction = formatFunction;
                  } else {
                      returnFunction = defaultfilters.indexOf(formatFunction) !== -1 ? filter(formatFunction) : function (value) {
                          return value;
                      };
                  }
                  return returnFunction(value, filterParameter);
              };
          }]);
  })(angular);
  
  
  /*table module */
  
  (function (angular) {
      "use strict";
      angular.module('smartTable.table', ['smartTable.column', 'smartTable.utilities', 'smartTable.directives', 'smartTable.filters', 'ui.bootstrap.pagination.smartTable'])
          .constant('DefaultTableConfiguration', {
              selectionMode: 'none',
              isGlobalSearchActivated: false,
              displaySelectionCheckbox: false,
              isPaginationEnabled: true,
              itemsByPage: 10,
              maxSize: 5,
  
              //just to remind available option
              sortAlgorithm: '',
              filterAlgorithm: ''
          })
          .controller('TableCtrl', ['$scope', 'Column', '$filter', '$parse', 'ArrayUtility', 'DefaultTableConfiguration', function (scope, Column, filter, parse, arrayUtility, defaultConfig) {
  
              scope.columns = [];
              scope.dataCollection = scope.dataCollection || [];
              scope.displayedCollection = []; //init empty array so that if pagination is enabled, it does not spoil performances
              scope.numberOfPages = calculateNumberOfPages(scope.dataCollection);
              scope.currentPage = 1;
              scope.holder = {isAllSelected: false};
  
              var predicate = {},
                  lastColumnSort;
  
              function isAllSelected() {
                  var i,
                      l = scope.displayedCollection.length;
                  for (i = 0; i < l; i++) {
                      if (scope.displayedCollection[i].isSelected !== true) {
                          return false;
                      }
                  }
                  return true;
              }
  
              function calculateNumberOfPages(array) {
  
                  if (!angular.isArray(array) || array.length === 0 || scope.itemsByPage < 1) {
                      return 1;
                  }
                  return Math.ceil(array.length / scope.itemsByPage);
              }
  
              function sortDataRow(array, column) {
                  var sortAlgo = (scope.sortAlgorithm && angular.isFunction(scope.sortAlgorithm)) === true ? scope.sortAlgorithm : filter('orderBy');
                  if (column && !(column.reverse === undefined)) {
                      return arrayUtility.sort(array, sortAlgo, column.sortPredicate, column.reverse);
                  } else {
                      return array;
                  }
              }
  
              function selectDataRow(array, selectionMode, index, select) {
  
                  var dataRow, oldValue;
  
                  if ((!angular.isArray(array)) || (selectionMode !== 'multiple' && selectionMode !== 'single')) {
                      return;
                  }
  
                  if (index >= 0 && index < array.length) {
                      dataRow = array[index];
                      if (selectionMode === 'single') {
                          //unselect all the others
                          for (var i = 0, l = array.length; i < l; i++) {
                              oldValue = array[i].isSelected;
                              array[i].isSelected = false;
                              if (oldValue === true) {
                                  scope.$emit('selectionChange', {item: array[i]});
                              }
                          }
                      }
                      dataRow.isSelected = select;
                      scope.holder.isAllSelected = isAllSelected();
                      scope.$emit('selectionChange', {item: dataRow});
                  }
              }
  
              /**
               * set the config (config parameters will be available through scope
               * @param config
               */
              this.setGlobalConfig = function (config) {
                  angular.extend(scope, defaultConfig, config);
              };
  
              /**
               * change the current page displayed
               * @param page
               */
              this.changePage = function (page) {
                  var oldPage = scope.currentPage;
                  if (angular.isNumber(page.page)) {
                      scope.currentPage = page.page;
                      scope.displayedCollection = this.pipe(scope.dataCollection);
                      scope.holder.isAllSelected = isAllSelected();
                      scope.$emit('changePage', {oldValue: oldPage, newValue: scope.currentPage});
                  }
              };
  
              /**
               * set column as the column used to sort the data (if it is already the case, it will change the reverse value)
               * @method sortBy
               * @param column
               */
              this.sortBy = function (column) {
                  var index = scope.columns.indexOf(column);
                  if (index !== -1) {
                      if (column.isSortable === true) {
                          // reset the last column used
                          if (lastColumnSort && lastColumnSort !== column) {
                              lastColumnSort.reverse = undefined;
                          }
                          column.sortPredicate = column.sortPredicate || column.map;
  
                          if (column.reverse === undefined) {
                              column.reverse = false;
                          }
                          else if (column.reverse === false) {
                              column.reverse = true;
                          }
                          else {
                              column.reverse = undefined;
                          }
  
                          lastColumnSort = column;
                      }
                  }
  
                  scope.displayedCollection = this.pipe(scope.dataCollection);
              };
  
              /**
               * set the filter predicate used for searching
               * @param input
               * @param column
               */
              this.search = function (input, column) {
  
                  //update column and global predicate
                  if (column && scope.columns.indexOf(column) !== -1) {
                      predicate[column.map] = input;
                  } else {
                      predicate = {$: input};
                  }
                  scope.displayedCollection = this.pipe(scope.dataCollection);
              };
  
              /**
               * combine sort, search and limitTo operations on an array,
               * @param array
               * @returns Array, an array result of the operations on input array
               */
              this.pipe = function (array) {
                  var filterAlgo = (scope.filterAlgorithm && angular.isFunction(scope.filterAlgorithm)) === true ? scope.filterAlgorithm : filter('filter'),
                      output;
                  //filter and sort are commutative
                  output = sortDataRow(arrayUtility.filter(array, filterAlgo, predicate), lastColumnSort);
                  scope.numberOfPages = calculateNumberOfPages(output);
                  return scope.isPaginationEnabled ? arrayUtility.fromTo(output, (scope.currentPage - 1) * scope.itemsByPage, scope.itemsByPage) : output;
              };
  
              /*////////////
               Column API
               ///////////*/
  
  
              /**
               * insert a new column in scope.collection at index or push at the end if no index
               * @param columnConfig column configuration used to instantiate the new Column
               * @param index where to insert the column (at the end if not specified)
               */
              this.insertColumn = function (columnConfig, index) {
                  var column = new Column(columnConfig);
                  arrayUtility.insertAt(scope.columns, index, column);
              };
  
              /**
               * remove the column at columnIndex from scope.columns
               * @param columnIndex index of the column to be removed
               */
              this.removeColumn = function (columnIndex) {
                  arrayUtility.removeAt(scope.columns, columnIndex);
              };
  
              /**
               * move column located at oldIndex to the newIndex in scope.columns
               * @param oldIndex index of the column before it is moved
               * @param newIndex index of the column after the column is moved
               */
              this.moveColumn = function (oldIndex, newIndex) {
                  arrayUtility.moveAt(scope.columns, oldIndex, newIndex);
              };
  
              /**
               * remove all columns
               */
              this.clearColumns = function () {
                  scope.columns.length = 0;
              };
  
              /*///////////
               ROW API
               */
  
              /**
               * select or unselect the item of the displayedCollection with the selection mode set in the scope
               * @param dataRow
               */
              this.toggleSelection = function (dataRow) {
                  var index = scope.dataCollection.indexOf(dataRow);
                  if (index !== -1) {
                      selectDataRow(scope.dataCollection, scope.selectionMode, index, dataRow.isSelected !== true);
                  }
              };
  
              /**
               * select/unselect all the currently displayed rows
               * @param value if true select, else unselect
               */
              this.toggleSelectionAll = function (value) {
                  var i = 0,
                      l = scope.displayedCollection.length;
  
                  if (scope.selectionMode !== 'multiple') {
                      return;
                  }
                  for (; i < l; i++) {
                      selectDataRow(scope.displayedCollection, scope.selectionMode, i, value === true);
                  }
              };
  
              /**
               * remove the item at index rowIndex from the displayed collection
               * @param rowIndex
               * @returns {*} item just removed or undefined
               */
              this.removeDataRow = function (rowIndex) {
                  var toRemove = arrayUtility.removeAt(scope.displayedCollection, rowIndex);
                  arrayUtility.removeAt(scope.dataCollection, scope.dataCollection.indexOf(toRemove));
              };
  
              /**
               * move an item from oldIndex to newIndex in displayedCollection
               * @param oldIndex
               * @param newIndex
               */
              this.moveDataRow = function (oldIndex, newIndex) {
                  arrayUtility.moveAt(scope.displayedCollection, oldIndex, newIndex);
              };
  
              /**
               * update the model, it can be a non existing yet property
               * @param dataRow the dataRow to update
               * @param propertyName the property on the dataRow ojbect to update
               * @param newValue the value to set
               */
              this.updateDataRow = function (dataRow, propertyName, newValue) {
                  var index = scope.displayedCollection.indexOf(dataRow),
                      getter = parse(propertyName),
                      setter = getter.assign,
                      oldValue;
                  if (index !== -1) {
                      oldValue = getter(scope.displayedCollection[index]);
                      if (oldValue !== newValue) {
                          setter(scope.displayedCollection[index], newValue);
                          scope.$emit('updateDataRow', {item: scope.displayedCollection[index]});
                      }
                  }
              };
          }]);
  })(angular);
  
  
  
  angular.module('smartTable.templates', ['partials/defaultCell.html', 'partials/defaultHeader.html', 'partials/editableCell.html', 'partials/globalSearchCell.html', 'partials/pagination.html', 'partials/selectAllCheckbox.html', 'partials/selectionCheckbox.html', 'partials/smartTable.html']);
  
  angular.module("partials/defaultCell.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("partials/defaultCell.html",
      "{{formatedValue}}");
  }]);
  
  angular.module("partials/defaultHeader.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("partials/defaultHeader.html",
      "<span class=\"header-content\" ng-class=\"{'sort-ascent':column.reverse==false,'sort-descent':column.reverse==true}\">{{column.label}}</span>");
  }]);
  
  angular.module("partials/editableCell.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("partials/editableCell.html",
      "<div ng-dblclick=\"isEditMode || toggleEditMode($event)\">\n" +
      "    <span ng-hide=\"isEditMode\">{{value | format:column.formatFunction:column.formatParameter}}</span>\n" +
      "\n" +
      "    <form ng-submit=\"submit()\" ng-show=\"isEditMode\" name=\"myForm\">\n" +
      "        <input name=\"myInput\" ng-model=\"value\" type=\"type\" input-type/>\n" +
      "    </form>\n" +
      "</div>");
  }]);
  
  angular.module("partials/globalSearchCell.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("partials/globalSearchCell.html",
      "<label>Search :</label>\n" +
      "<input type=\"text\" ng-model=\"searchValue\"/>");
  }]);
  
  angular.module("partials/pagination.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("partials/pagination.html",
      "<div class=\"pagination\">\n" +
      "    <ul class=\"pagination\">\n" +
      "        <li ng-repeat=\"page in pages\" ng-class=\"{active: page.active, disabled: page.disabled}\"><a\n" +
      "                ng-click=\"selectPage(page.number)\">{{page.text}}</a></li>\n" +
      "    </ul>\n" +
      "</div> ");
  }]);
  
  angular.module("partials/selectAllCheckbox.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("partials/selectAllCheckbox.html",
      "<input class=\"smart-table-select-all\"  type=\"checkbox\" ng-model=\"holder.isAllSelected\"/>");
  }]);
  
  angular.module("partials/selectionCheckbox.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("partials/selectionCheckbox.html",
      "<input type=\"checkbox\" ng-model=\"dataRow.isSelected\" stop-event=\"click\"/>");
  }]);
  
  angular.module("partials/smartTable.html", []).run(["$templateCache", function($templateCache) {
    $templateCache.put("partials/smartTable.html",
      "<table class=\"smart-table\">\n" +
      "    <thead>\n" +
      "    <tr class=\"smart-table-global-search-row\" ng-show=\"isGlobalSearchActivated\">\n" +
      "        <td class=\"smart-table-global-search\" column-span=\"{{columns.length}}\" colspan=\"{{columnSpan}}\">\n" +
      "        </td>\n" +
      "    </tr>\n" +
      "    <tr class=\"smart-table-header-row\">\n" +
      "        <th ng-repeat=\"column in columns\" ng-include=\"column.headerTemplateUrl\"\n" +
      "            class=\"smart-table-header-cell {{column.headerClass}}\" scope=\"col\">\n" +
      "        </th>\n" +
      "    </tr>\n" +
      "    </thead>\n" +
      "    <tbody>\n" +
      "    <tr ng-repeat=\"dataRow in displayedCollection\" ng-class=\"{selected:dataRow.isSelected}\"\n" +
      "        class=\"smart-table-data-row\">\n" +
      "        <td ng-repeat=\"column in columns\" class=\"smart-table-data-cell {{column.cellClass}}\"></td>\n" +
      "    </tr>\n" +
      "    </tbody>\n" +
      "    <tfoot ng-show=\"isPaginationEnabled\">\n" +
      "    <tr class=\"smart-table-footer-row\">\n" +
      "        <td colspan=\"{{columns.length}}\">\n" +
      "            <div pagination-smart-table=\"\" num-pages=\"numberOfPages\" max-size=\"maxSize\" current-page=\"currentPage\"></div>\n" +
      "        </td>\n" +
      "    </tr>\n" +
      "    </tfoot>\n" +
      "</table>\n" +
      "\n" +
      "\n" +
      "");
  }]);
  
  (function (angular) {
      "use strict";
      angular.module('smartTable.templateUrlList', [])
          .constant('templateUrlList', {
              smartTable: 'partials/smartTable.html',
              smartTableGlobalSearch: 'partials/globalSearchCell.html',
              editableCell: 'partials/editableCell.html',
              selectionCheckbox: 'partials/selectionCheckbox.html',
              selectAllCheckbox: 'partials/selectAllCheckbox.html',
              defaultHeader: 'partials/defaultHeader.html',
              pagination: 'partials/pagination.html'
          });
  })(angular);
  
  
  (function (angular) {
      "use strict";
      angular.module('smartTable.utilities', [])
  
          .factory('ArrayUtility', function () {
  
              /**
               * remove the item at index from arrayRef and return the removed item
               * @param arrayRef
               * @param index
               * @returns {*}
               */
              var removeAt = function (arrayRef, index) {
                      if (index >= 0 && index < arrayRef.length) {
                          return arrayRef.splice(index, 1)[0];
                      }
                  },
  
                  /**
                   * insert item in arrayRef at index or a the end if index is wrong
                   * @param arrayRef
                   * @param index
                   * @param item
                   */
                  insertAt = function (arrayRef, index, item) {
                      if (index >= 0 && index < arrayRef.length) {
                          arrayRef.splice(index, 0, item);
                      } else {
                          arrayRef.push(item);
                      }
                  },
  
                  /**
                   * move the item at oldIndex to newIndex in arrayRef
                   * @param arrayRef
                   * @param oldIndex
                   * @param newIndex
                   */
                  moveAt = function (arrayRef, oldIndex, newIndex) {
                      var elementToMove;
                      if (oldIndex >= 0 && oldIndex < arrayRef.length && newIndex >= 0 && newIndex < arrayRef.length) {
                          elementToMove = arrayRef.splice(oldIndex, 1)[0];
                          arrayRef.splice(newIndex, 0, elementToMove);
                      }
                  },
  
                  /**
                   * sort arrayRef according to sortAlgorithm following predicate and reverse
                   * @param arrayRef
                   * @param sortAlgorithm
                   * @param predicate
                   * @param reverse
                   * @returns {*}
                   */
                  sort = function (arrayRef, sortAlgorithm, predicate, reverse) {
  
                      if (!sortAlgorithm || !angular.isFunction(sortAlgorithm)) {
                          return arrayRef;
                      } else {
                          return sortAlgorithm(arrayRef, predicate, reverse === true);//excpet if reverse is true it will take it as false
                      }
                  },
  
                  /**
                   * filter arrayRef according with filterAlgorithm and predicate
                   * @param arrayRef
                   * @param filterAlgorithm
                   * @param predicate
                   * @returns {*}
                   */
                  filter = function (arrayRef, filterAlgorithm, predicate) {
                      if (!filterAlgorithm || !angular.isFunction(filterAlgorithm)) {
                          return arrayRef;
                      } else {
                          return filterAlgorithm(arrayRef, predicate);
                      }
                  },
  
                  /**
                   * return an array, part of array ref starting at min and the size of length
                   * @param arrayRef
                   * @param min
                   * @param length
                   * @returns {*}
                   */
                  fromTo = function (arrayRef, min, length) {
  
                      var out = [],
                          limit,
                          start;
  
                      if (!angular.isArray(arrayRef)) {
                          return arrayRef;
                      }
  
                      start = Math.max(min, 0);
                      start = Math.min(start, (arrayRef.length - 1) > 0 ? arrayRef.length - 1 : 0);
  
                      length = Math.max(0, length);
                      limit = Math.min(start + length, arrayRef.length);
  
                      for (var i = start; i < limit; i++) {
                          out.push(arrayRef[i]);
                      }
                      return out;
                  };
  
  
              return {
                  removeAt: removeAt,
                  insertAt: insertAt,
                  moveAt: moveAt,
                  sort: sort,
                  filter: filter,
                  fromTo: fromTo
              };
          });
  })(angular);
  
  
  
  (function (angular) {
      angular.module('ui.bootstrap.pagination.smartTable', ['smartTable.templateUrlList'])
  
          .constant('paginationConfig', {
              boundaryLinks: false,
              directionLinks: true,
              firstText: 'First',
              previousText: '<',
              nextText: '>',
              lastText: 'Last'
          })
  
          .directive('paginationSmartTable', ['paginationConfig', 'templateUrlList', function (paginationConfig, templateUrlList) {
              return {
                  restrict: 'EA',
                  require: '^smartTable',
                  scope: {
                      numPages: '=',
                      currentPage: '=',
                      maxSize: '='
                  },
                  templateUrl: templateUrlList.pagination,
                  replace: true,
                  link: function (scope, element, attrs, ctrl) {
  
                      // Setup configuration parameters
                      var boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;
                      var directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$eval(attrs.directionLinks) : paginationConfig.directionLinks;
                      var firstText = angular.isDefined(attrs.firstText) ? attrs.firstText : paginationConfig.firstText;
                      var previousText = angular.isDefined(attrs.previousText) ? attrs.previousText : paginationConfig.previousText;
                      var nextText = angular.isDefined(attrs.nextText) ? attrs.nextText : paginationConfig.nextText;
                      var lastText = angular.isDefined(attrs.lastText) ? attrs.lastText : paginationConfig.lastText;
  
                      // Create page object used in template
                      function makePage(number, text, isActive, isDisabled) {
                          return {
                              number: number,
                              text: text,
                              active: isActive,
                              disabled: isDisabled
                          };
                      }
  
                      scope.$watch('numPages + currentPage + maxSize', function () {
                          scope.pages = [];
  
                          // Default page limits
                          var startPage = 1, endPage = scope.numPages;
  
                          // recompute if maxSize
                          if (scope.maxSize && scope.maxSize < scope.numPages) {
                              startPage = Math.max(scope.currentPage - Math.floor(scope.maxSize / 2), 1);
                              endPage = startPage + scope.maxSize - 1;
  
                              // Adjust if limit is exceeded
                              if (endPage > scope.numPages) {
                                  endPage = scope.numPages;
                                  startPage = endPage - scope.maxSize + 1;
                              }
                          }
  
                          // Add page number links
                          for (var number = startPage; number <= endPage; number++) {
                              var page = makePage(number, number, scope.isActive(number), false);
                              scope.pages.push(page);
                          }
  
                          // Add previous & next links
                          if (directionLinks) {
                              var previousPage = makePage(scope.currentPage - 1, previousText, false, scope.noPrevious());
                              scope.pages.unshift(previousPage);
  
                              var nextPage = makePage(scope.currentPage + 1, nextText, false, scope.noNext());
                              scope.pages.push(nextPage);
                          }
  
                          // Add first & last links
                          if (boundaryLinks) {
                              var firstPage = makePage(1, firstText, false, scope.noPrevious());
                              scope.pages.unshift(firstPage);
  
                              var lastPage = makePage(scope.numPages, lastText, false, scope.noNext());
                              scope.pages.push(lastPage);
                          }
  
  
                          if (scope.currentPage > scope.numPages) {
                              scope.selectPage(scope.numPages);
                          }
                      });
                      scope.noPrevious = function () {
                          return scope.currentPage === 1;
                      };
                      scope.noNext = function () {
                          return scope.currentPage === scope.numPages;
                      };
                      scope.isActive = function (page) {
                          return scope.currentPage === page;
                      };
  
                      scope.selectPage = function (page) {
                          if (!scope.isActive(page) && page > 0 && page <= scope.numPages) {
                              scope.currentPage = page;
                              ctrl.changePage({ page: page });
                          }
                      };
                  }
              };
          }]);
  })(angular);
  
  