Simple client side knockout pager

by CodeRenaissance

HTML

<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src=" https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-debug.js"></script>
Results
<div id="results">
</div>https://jsfiddle.net/CodeRenaissance/1ujmfx0s/30/#run
<script type="text/javascript">
  //Testing library functions

  function assertEqual(actual, expected, message) {
    expected = ko.utils.unwrapObservable(expected);
    actual = ko.utils.unwrapObservable(actual);

    if (expected !== actual) {
      throw "Assert Equal Failed - expected '" + expected + "' but was '" + actual + (message ? +"' - " + message : "");
    }
  }

  function test(testName, fnTest) {
    var result = "";
    var errMsg = "";

    try {
      fnTest();
    } catch (er) {
      errMsg = er;
    }

    if (errMsg) {
      $('#results').append("<div class='error result'><span class='failFlag'>*</span> " + testName + " - Fail" + "</div>");
      $('#results').append("<div class='errMsg'> " + errMsg + "</div>");
    } else {
      $('#results').append("<div class='success result'><span class='successFlag'>*</span> " + testName + "" + "</div>");
    }
    $('#results').append("<div> " + "</div>");
  }

  function setTestSection(sectionName) {
    $('#results').append("<div class='section'> - - - " + sectionName + " - - - </div>");
  }

  //test("Failure Gets Logged",function(){
  //throw "Error"
  //});

</script>

CSS

.errMsg {
  padding: 0 0 5px 25px;
}

.failFlag {
  background: pink;
  padding: 3px;
  margin: 0 0 0 12px
}

.successFlag {
  background: lime;
  padding: 3px;
  margin: 0 0 0 12px
}

.result {
  margin: 2px 0 7px 5px;
  white-space: nowrap;
}

.section {
  margin: 20px 0 10px 5px;
  white-space:nowrap;
}

JavaScript

function Pager(records, options) {
  records = records || [];
  options = options || {};
  var _pageSizeAccessor = (function(){
  	var pageSizeObservable;
	  if(ko.isWritableObservable(options.pageSize)) {
		  pageSizeObservable = options.pageSize;
	  }
	  else{
		  pageSizeObservable = ko.observable(parseInt(options.pageSize, 10) || 10);
	  }

    return ko.computed({
      read: () => ko.utils.unwrapObservable(pageSizeObservable),
      write: (val) => pageSizeObservable(val)
    });
  }());

  var _currentPageAccessor = (function() {
    var maxPage = ko.computed(() => {
      var recordCount = records.length || 1;
      var pageSize = _pageSizeAccessor();
      return Math.ceil(recordCount / pageSize);
    });

    var currentPageObservable = ko.observable(1);

    var pageComputed = ko.computed({
      read: () => {
        return currentPageObservable();
      },
      write: (val) => {
        var isWithingRange = val >= 1 && val <= maxPage();
        if (isWithingRange || val === 1) { //single source of validation for changes
          currentPageObservable(val);
        }
      }
    });

    pageComputed.maxAllowed = ko.computed(() => maxPage());
    pageComputed.moveFirst = () => pageComputed(1);
    pageComputed.moveFirst.isAllowed = ko.computed(() => pageComputed() > 1);

    pageComputed.moveNext = () => pageComputed(pageComputed() + 1);
    pageComputed.moveNext.isAllowed = ko.computed(() => pageComputed() < maxPage());

    pageComputed.movePrev = () => pageComputed(pageComputed() - 1);
    pageComputed.movePrev.isAllowed = ko.computed(() => pageComputed() > 1);

    pageComputed.moveLast = () => pageComputed(maxPage());
    pageComputed.moveLast.isAllowed = ko.computed(() => pageComputed() < maxPage());
    
    pageComputed.startIndex = ko.computed({
    	deferEvaluation:true,
    	read:()=>{
          var currentPage = _currentPageAccessor();
          var pageSize = _pageSizeAccessor();
          var result = ((currentPage - 1) * pageSize);
  ...