Improved semaphore

Semaphore that can be used for resources, e.g. ajax requests. Uses ko.postbox as a pub/sub framework so allows semaphores across view-models to update together.

by nickkell

HTML

<script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.debug.js"></script>
<script src="http://cloud.github.com/downloads/rniemeyer/knockout-postbox/knockout-postbox.js"></script>
<p data-bind="text:semaphore.queued"></p>
<input type="button" value="add" data-bind="click: add" />
<input type="button" value="remove" data-bind="click: remove" />
<div data-bind="if: sub">
<p data-bind="text:sub().semaphore.queued"></p>
</div>
<input type="button" value="add submodel" data-bind="click: addSub" />
<input type="button" value="reset" data-bind="click: semaphore.reset" />
<p data-bind="text: ko.toJSON($root)"></p>

JavaScript

function Semaphore() {
    var self = this,
        length = ko.observable(0),
        add = function(n) {
            length(length() + 1);
        },
        remove = function() {
            if (length() > 0) {
                length(length() - 1);
            }
        };

    ko.postbox.subscribe('semaphore.add', function(n) {
        add();
    });

    ko.postbox.subscribe('semaphore.remove', function(n) {
        remove();
    });

    ko.postbox.subscribe('semaphore.synch', function(n) {
        if ($.isNumeric(n)) {
            length(n);
        }
    });

    self.queued = ko.computed(function() {
        return length() > 0;
    });

    self.count = function() {
        return length();
    };
}

function Sub() {
    var self = this;

    self.semaphore = new Semaphore();
}

function Model() {
    var self = this;

    self.semaphore = new Semaphore();

    self.sub = ko.observable();

    self.add = function() {
        ko.postbox.publish('semaphore.add');
    };

    self.remove = function() {
        ko.postbox.publish('semaphore.remove');
    };

    self.addSub = function() {
        self.sub(new Sub());
        ko.postbox.publish('semaphore.synch', self.semaphore.count());
    };
}

ko.applyBindings(new Model());