KnockoutJS bitField binding

by sukobuto

HTML

<link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css">
<script src="http://sukobuto.com/files/js/knockout-es5.min.js"></script>
<h1>KnockoutJS bitField binding</h1>

<h2>Weekdays</h2>
<ul data-bind="foreach: ko.utils.range(0,6)">
    <li>
        <label>
            <input type="checkbox"
                   data-bind="bitField: $root.week_days, checkedFlag: $root.weekday_flags[$data]"/>
            <span data-bind="text: $root.weekday_labels[$data]"> </span>
        </label>
    </li>
</ul>

Value: 
<input type="number" data-bind="value: week_days"/>
0b<span data-bind="text: week_days_bin" class="digits"> </span>

<h3>Toggle button style</h3>
<div class="btn-group" data-bind="foreach: ko.utils.range(0,6)">
    <label class="btn btn-primary" data-bind="css: { active: $parent.week_days() & $parent.weekday_flags[$data] }">
        <input type="checkbox" style="display: none;"
        data-bind="bitField: $root.week_days, checkedFlag: $root.weekday_flags[$data]"/>
        <span data-bind="text: $root.weekday_labels_2[$data]"> </span>
    </label>
</div>

<h2>Linux File Mode</h2>
<ul data-bind="foreach: file_modes">
    <li>
        <label>
            <input type="checkbox" data-bind="bitField: $root.file_mode, checkedFlag: flag"/>
            <span data-bind="text: label"> </span>
        </label>
    </li>
</ul>

Value:
<input type="number" data-bind="value: file_mode"/>
0o<span data-bind="text: file_mode_oct" class="digits"> </span>

CSS

.digits {
    color: blue;
}

JavaScript

// bitField binding
ko.bindingHandlers['bitField'] = {
    'after': ['value', 'attr'],
    'init': function (element, valueAccessor, allBindings) {
		var propWriters = allBindings()['_ko_property_writers'];
        
        function checkedFlag() {
            return allBindings['has']('checkedFlag')
                ? ko.unwrap(allBindings.get('checkedFlag'))
                : element.value;
        }
        
        function updateModel() {
            // This updates the model value from the view value
            // It runs in response to DOM events (click) and changes in checkedFlag.
            var isChecked = element.checked,
                elemValue = checkedFlag();
            
            // When we're first setting up this computed, don't change any model state.
            if (!shouldSet) {
                return;
            }
            
            var modelValue = valueAccessor();
            var unwrapped = ko.unwrap(modelValue);
            if (oldElemValue !== elemValue) {
                if (isChecked) {
                    if (oldElemValue !== undefined) {
                        unwrapped &= ~oldElemValue;
                    }
                    unwrapped |= elemValue;
                }
                oldElemValue = elemValue;
            } else {
                if (isChecked) {
                    unwrapped |= elemValue;
                } else {
                    unwrapped &= ~elemValue;
                }
            }
            if (ko.isObservable(modelValue)) {
                modelValue(unwrapped);
            } else if (propWriters['bitField']) {
                propWriters.bitField(unwrapped);
            } else {
                valueAccessor(unwrapped);
            }
        }
        
        function updateView() {
            // This updates the view value from the model value.
            // It runs in response to changes in the bound (checked) value.
            var modelValue = ko.unwrap(valueAccessor());
           ...