How to use computed.subscribe to toggle smart dirty flag

http://www.knockmeout.net/2011/05/creating-smart-dirty-flag-in-knockoutjs.html

by Semmel

HTML

<script src="http://knockoutjs.com/downloads/knockout-2.3.0.js"></script>
<script src="http://underscorejs.org/underscore.js"></script>
string:<input data-bind="value: string"/><br><input type="text"/><br>
switched:<input type="checkbox" data-bind="checked: switched"/><br>
list:<div data-bind="foreach:items">
<div data-bind="text:name"></div>
</div>
isDirty:<input type="checkbox" data-bind="checked: isDirty" disabled="disabled" />
<textarea id="out" cols="20" rows="20" disabled="disabled"></textarea>

CSS

li { padding: 2px; margin: 2px; }
input { width: 75px; }
.dirty { border: solid yellow 2px; }

JavaScript

//not used in this example.  one time flag, that drops its subscriptions after the first change.
function log(text)
{
    document.getElementById('out').value += text + "\n";
    console.log(text);
}
ko.basicDirtyFlag = function(root) {
    var _isDirty = ko.observable(false);
    var _initialState = ko.observable(ko.toJSON(root));

    var result = ko.computed({ 
        read: function()
			{
				//once we detected a change we can give up subscriptions
				// and depend solely on the internal boolean _isDirty
				if (!_isDirty())
				{
                     //just for subscriptions
                    var resultFlag = ko.toJSON(root) !== _initialState();
                    log("subscribing..");
                    log("returning " + resultFlag);
                    return  resultFlag;
				}
                log("returning " + _isDirty() + '\n');
		  		return _isDirty();
            }, 
          write: function(value)
        {
            if (value === false && _isDirty() === true)
            {
                /// reset the flag
                _initialState(ko.toJSON(root));
                
                log("updating internal obs with " + value + "...");
                _isDirty(value);
                log("..done");
                
                toggle2TrueSub = result.subscribe(toggle2True);
            }
            else if (value === true)
            {
                log("updating internal obs with " + value + "...");
                _isDirty(true);
                log("..done");
                //throw new Error("not supported yet");
            }
        }
    }              
    );
    
    function toggle2True()
    {
        log("change in root..");
        if (!_isDirty()) 
        {
            toggle2TrueSub.dispose();
            log("toggling flag.");
            _isDirty(true);
        }
        log("\n");
    }

    var toggle2TrueSub = result.subscribe(toggle2True);

    return result;
}

var ViewModel = function() 
{
    this.string...