ko.datasource - with ko.mapping

Datasource Components for KnockoutJs for paging, sorting and filtering remote sources.

HTML

<script src="http://rniemeyer.github.com/KnockMeOut/Scripts/knockout-latest.debug.js"></script>
<script src="https://raw.github.com/CraigCav/ko.datasource/master/ko.datasource.js"></script>
<script src="https://dl.dropbox.com/u/16870890/knockout.mapping-latest.debug.js"></script>
<table>
    <thead>
        <tr>
            <th>Id</th>
            <th>Name</th>
            <th>Sales</th>
            <th>Price</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: items">
        <tr>
            <td data-bind="text: id"></td>
            <td><div data-bind="clickToEdit: name"></div></td>
            <td data-bind="text: sales"></td>
            <td data-bind="text: price"></td>
        </tr>
    </tbody>            
</table>

<span id="pager">
    <button data-bind="click: items.pager.first">First</button>
    <button data-bind="click: items.pager.previous">Prev</button>
    <span class="summary">Page 
        <span data-bind="text: items.pager.page"></span> of 
        <span data-bind="text: items.pager.totalPages"></span></span>
    <button data-bind="click: items.pager.next">Next</button>
    <button data-bind="click: items.pager.last">Last</button>
</span>

CSS

th, td { padding: 0.5em; }
.summary { margin: 0px 10px;}
a {
color: #0088cc;
text-decoration: none;
}
a:hover,
a:focus {
  color: #005580;
  text-decoration: underline;
}

JavaScript

/*! https://github.com/CraigCav/ko.datasource */
(function ( ko ) {
    function datasource( source, target ) {
        var _target = target || ko.observable(),
            paused = true,
            trigger = ko.observable( false ),
            loading = ko.observable( false ),
            result = ko.computed( {
                read: function () {
                    if ( paused ) {
                        paused = false;
                        trigger( true );
                    }
                    return _target();
                },
                write: function ( newValue ) {
                    _target( newValue );
                    loading( false );
                },
                deferEvaluation: true
            } );

        ko.computed( function () {
            if ( !trigger() ) return;
            loading( true );
            source.call( result );
        } );

        result.refresh = function () {
            trigger( trigger() + 1 );
        };

        result.loading = loading;

        return result;
    }

    function Pager( limit ) {
        this.page = ko.observable( 1 );
        this.totalCount = ko.observable( 0 );
        this.limit = ko.observable( limit );

        this.totalPages = ko.computed( function () {
            var count = Math.ceil( ko.utils.unwrapObservable( this.totalCount ) / ko.utils.unwrapObservable( this.limit ) )
            return count == 0 ? 1 : count;
        }, this );

        this.pages = ko.computed(function () {
            var a = [];
            var count = this.totalPages();
            for (var p = 0; p < count; p++) {
                a.push(p + 1);
            }
            return a;
        }, this);

        this.next = function () {
            var currentPage = this.page();
            this.page( currentPage + 1 );
        } .bind( this );

        this.previous = function () {
            var currentPage = this.page();
            this.page( currentPage === 0 ? 0 : currentPage - 1 );
   ...