inline autocorrection

by bman654

HTML

<button type="button" data-bind="click: toggleStuff">Toggle stuff</button>
<hr />
<div data-bind="if: showStuff">
    <label>Number: <input type="text" data-bind="value: number, autocorrect: numeric" /></label>
    <label>No spaces: <input type="text" data-bind="value: name, autocorrect: nospaces, valueUpdate: 'keyup'" /></label>
    <label>non-negative and less than Number: <input type="text" data-bind="value: number2, autocorrect: clip(0,number())" /></label>
    
    <label>Number (no autocorrection): <input type="text" data-bind="value: number" /></label>
    <label>Name (no autocorrection): <input type="text" data-bind="value: name, valueUpdate: 'keyup'" /></label>
</div>

CSS

label { display:block; }

JavaScript

var key = "__Vkey";

ko.bindingHandlers.value.preprocess = function (valueExpression, bindingName, addBinding) {
    
    var x = "(ko.utils.domData.get($element,'" + key + "')||("+valueExpression+"))";
    console.log("preprocess",bindingName,valueExpression, x);
    addBinding(key, valueExpression);
    return x;
};

ko.bindingHandlers.value.after.push("autocorrect");

ko.bindingHandlers.autocorrect = {
    preprocess: function (valueExpression) {
        //return "(with(window.V){" + valueExpression + "})";
        return "window.V." + valueExpression;
    },
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        console.log("V init", valueAccessor);
        if (allBindings.has("value")) {
            var c = ko.computed({
                disposeWhenNodeIsRemoved: element,
                read: function() {
                    var v = allBindings.get(key);
                    return ko.unwrap(v);
                },
                write: function (value) {
                    var validator = valueAccessor();
                    var oldValue = ko.unwrap(v);
                    var newValue = validator(value, oldValue);
                    var v = allBindings.get(key);
                    console.log("writing", value, newValue);
                    if (ko.isWriteableObservable(v)) {
                        v(newValue);
                        c.notifySubscribers(v()); // temporary
                    }
                }
            });
            
            ko.utils.domData.set(element, key, c);
        }
    }
};


// autocorrectors
window.V = {
    numeric: function (n, o) {
        return parseFloat(n);
    },
    clip: function (min, max) {
        return function (n, o) {
            if (n < min) return min;
            if (n > max) return max;
            return n;
        };
    },
    nospaces: function (n, o) {
        return (""+n).replace(/ /g,"");
    }
};

// viewmodel
var vm = {
    name:...