import * as ko from "knockout"; import * as ViewModels from "../Contracts/ViewModels"; export default class EditableUtility { public static observable(initialValue?: T): ViewModels.Editable { var observable: ViewModels.Editable = >ko.observable(initialValue); observable.edits = ko.observableArray([initialValue]); observable.validations = ko.observableArray<(value: T) => boolean>([]); observable.setBaseline = (baseline: T) => { observable(baseline); observable.edits([baseline]); }; observable.getEditableCurrentValue = ko.computed(() => { const edits = (observable.edits && observable.edits()) || []; if (edits.length === 0) { return undefined; } return edits[edits.length - 1]; }); observable.getEditableOriginalValue = ko.computed(() => { const edits = (observable.edits && observable.edits()) || []; if (edits.length === 0) { return undefined; } return edits[0]; }); observable.editableIsDirty = ko.computed(() => { const edits = (observable.edits && observable.edits()) || []; if (edits.length <= 1) { return false; } let current: any = observable.getEditableCurrentValue(); let original: any = observable.getEditableOriginalValue(); switch (typeof current) { case "string": case "undefined": case "number": case "boolean": current = current && current.toString(); break; default: current = JSON.stringify(current); break; } switch (typeof original) { case "string": case "undefined": case "number": case "boolean": original = original && original.toString(); break; default: original = JSON.stringify(original); break; } if (current !== original) { return true; } return false; }); observable.subscribe(edit => { var edits = observable.edits && observable.edits(); if (!edits) { return; } edits.push(edit); observable.edits(edits); }); observable.editableIsValid = ko.observable(true); observable.subscribe(value => { const validations: ((value: T) => boolean)[] = (observable.validations && observable.validations()) || []; const isValid = validations.every(validate => validate(value)); observable.editableIsValid(isValid); }); return observable; } }