Added Valerie initial version

Redone as I messed up my local repository
This commit is contained in:
Howard Richards
2013-11-21 22:25:38 +00:00
parent 95de4d32dd
commit ccd94b3ef9
3 changed files with 769 additions and 0 deletions

View File

@@ -229,6 +229,7 @@ List of Definitions
* [Underscore.js (Typed)](http://underscorejs.org/) (by [Josh Baldwin](https://github.com/jbaldwin/))
* [Underscore-ko.js](https://github.com/kamranayub/UnderscoreKO) (by [Maurits Elbers](https://github.com/MagicMau))
* [UUID.js](https://github.com/LiosK/UUID.js) (by [Jason Jarrett](https://github.com/staxmanade))
* [Valerie](https://github.com/davewatts/valerie) (by [Howard Richards](https://github.com/conficient))
* [Viewporter](https://github.com/zynga/viewporter) (by [Boris Yankov](https://github.com/borisyankov))
* [Vimeo](http://developer.vimeo.com/player/js-api) (by [Daz Wilkin](https://github.com/DazWilkin/))
* [WebRTC](http://dev.w3.org/2011/webrtc/editor/webrtc.html) (by [Ken Smith](https://github.com/smithkl42))

286
valerie/valerie-tests.ts Normal file
View File

@@ -0,0 +1,286 @@
/// <reference path="valerie.d.ts" />
/// <reference path="../knockout/knockout.d.ts" />
// Tests for valerie.d.ts
// Project: https://github.com/davewatts/valerie
// Definitions by: Howard Richards <https://github.com/conficient>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
/*
Checks the .d.ts definition work. Not a fully comprehensive set of tests yet.
*/
/**
* Simple enum for enum test
*/
enum enumTest { male, female }
/**
* ensure that observable values can validate
*
*/
function ObservableValidationTypes() {
// any
var t0 = ko.observable<any>()
.validate()
.end();
// string
var t1 = ko.observable<string>("")
.validate()
.end();
// number
var t2 = ko.observable<number>(0)
.validate()
.end();
// bool
var t3 = ko.observable<boolean>(true)
.validate()
.end();
// date
var t4 = ko.observable<Date>(new Date())
.validate()
.end();
// enum
var t5 = ko.observable<enumTest>(enumTest.male)
.validate()
.end();
//array
var t6 = ko.observableArray<any>([])
.validate()
.end();
}
function ComputedValidationTests() {
var t1 = ko.computed<string>(function () { return "hello world"; })
.validate()
.end();
}
function RuleTests() {
// various values used in rule tests
var dummyRule: Valerie.IRule = { test: null, defaultOptions: null };
var stringValue = "";
var stringFN = function () { return ""; }
var numberValue = 1;
var numberFN = function () { return 2 }
var booleanValue = false;
var booleanFN = ko.observable(true);
var dateValue = new Date();
var dateFN = function () { return new Date(); }
var anyValue = {};
var anyFN = function () { return {}; }
var arrayValue = [];
var arrayFN = function () { return []; }
var regexpValue = /\d+/;
// rule tests
var test_addRule = ko.observable(stringValue)
.validate()
.addRule(dummyRule)
.end();
var test_applicable = ko.observable(stringValue)
.validate()
.applicable(true)
.applicable(function () { return false; })
.end();
var test_currencyMajor = ko.observable(numberValue)
.validate()
.currencyMajor()
.end();
var test_currencyMinor = ko.observable(numberValue)
.validate()
.currencyMajorMinor()
.end();
var test_date = ko.observable(dateValue)
.validate()
.date()
.end();
var test_during = ko.observable(dateValue)
.validate()
.during(dateValue, dateValue)
.during(dateFN, dateFN)
.during(dateValue, dateFN)
.during(dateFN, dateValue)
.end();
var test_earliest = ko.observable(dateValue)
.validate()
.earliest(dateValue)
.end();
var test_email = ko.observable(stringValue)
.validate()
.email()
.end();
var test_entryformat = ko.observable(stringValue)
.validate()
.entryFormat(stringValue)
.end();
var test_expression = ko.observable(stringValue)
.validate()
.expression(regexpValue)
.expression(stringValue)
.end();
var test_float = ko.observable(numberValue)
.validate()
.float()
.end();
var test_integer = ko.observable(numberValue)
.validate()
.integer()
.end();
var test_latest = ko.observable(dateValue)
.validate()
.latest(dateValue)
.latest(dateFN)
.end();
var test_lengthBetween = ko.observable(stringValue)
.validate()
.lengthBetween(numberValue, numberValue)
.lengthBetween(numberFN, numberFN)
.lengthBetween(numberFN, numberValue)
.lengthBetween(numberValue, numberFN)
.end();
var test_matches = ko.observable(stringValue)
.validate()
.matches(numberValue)
.matches(numberFN)
.end();
var test_maximum = ko.observable(0)
.validate()
.maximum(numberValue)
.maximum(numberFN)
.end();
var test_maximumNumberOfItems = ko.observableArray([])
.validate()
.maximumNumberOfItems(numberValue)
.maximumNumberOfItems(numberFN)
.end();
var test_minimum = ko.observable(numberValue)
.validate()
.minimum(numberValue)
.minimum(numberFN)
.end();
var test_minimumLength = ko.observable("")
.validate()
.minimumLength(numberValue)
.minimumLength(numberFN)
.end();
var test_minimumNumberOfItems = ko.observableArray([])
.validate()
.minimumNumberOfItems(numberValue)
.minimumNumberOfItems(numberFN)
.end();
var test_name = ko.observable(stringValue)
.validate()
.name(stringValue)
.end();
var test_noneOf = ko.observable<any>(numberValue)
.validate()
.noneOf(arrayValue)
.noneOf(arrayFN)
.end();
var test_not = ko.observable(numberValue)
.validate()
.not(anyValue)
.not(anyFN)
.end();
var test_number = ko.observable(numberValue)
.validate()
.number()
.end();
var test_numberOfItems = ko.observableArray(arrayValue)
.validate()
.numberOfItems(numberValue, numberValue)
.numberOfItems(numberFN, numberFN)
.numberOfItems(numberFN, numberValue)
.numberOfItems(numberValue, numberFN)
.end();
var test_oneOf = ko.observable(numberValue)
.validate()
.oneOf(arrayValue)
.oneOf(arrayFN)
.end();
var test_postcode = ko.observable(stringValue)
.validate()
.postcode()
.end();
var test_range = ko.observable(numberValue)
.validate()
.range(numberValue, numberValue)
.range(numberValue, numberFN)
.range(numberFN, numberValue)
.range(numberFN, numberFN)
.end();
var test_required = ko.observable(anyValue)
.validate()
.required()
.end();
var test_ruleMessage = ko.observable(anyValue)
.validate()
.ruleMessage(stringValue)
.end();
var test_string = ko.observable(stringValue)
.validate()
.string()
.end();
var test_validateChildProperties = ko.observable(anyValue)
.validate()
.validateChildProperties()
.end();
var test_valueFormat = ko.observable(anyValue)
.validate()
.valueFormat(stringValue)
.end();
var test_rule = ko.observable(anyValue)
.validate()
.rule(() => { return anyValue; })
.end();
}

482
valerie/valerie.d.ts vendored Normal file
View File

@@ -0,0 +1,482 @@
// Type definitions for valerie
// Project: https://github.com/davewatts/valerie
// Definitions by: Howard Richards <https://github.com/conficient>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
/**
*
* Extensions to KO functions to provide validation
*/
interface KnockoutObservable<T> {
// starts validation for observable
validate(validationOptions?: Valerie.ValidationOptions): Valerie.PropertyValidationState<KnockoutObservable<T>>;
}
interface KnockoutComputed<T> {
// starts validation for observable
validate(validationOptions?: Valerie.ValidationOptions): Valerie.PropertyValidationState<KnockoutComputed<T>>;
}
interface KnockoutObservableArray<T> {
validate(validationOptions?: Valerie.ValidationOptions): Valerie.PropertyValidationState<KnockoutObservableArray<T>>;
// starts model validation for array
validateAsModel(): Valerie.ValidatableModel<KnockoutObservableArray<T>>;
}
/**
* Valerie BindingHandlers
*/
interface KnockoutBindingHandlers {
/**
* Validates entries that can be checked, i.e. check boxes and radio buttons.
* Functions in the same way as the <b>ko.bindingHandlers.checked</b> binding handler, with the following
* alterations:
* <ul>
* <li>registers a blur event handler so validation messages for selections can be displayed</li>
* <li>registers a click event handler so validation state can be marked as touched</i>
* </ul>
* @name ko.bindingHandlers.validatedChecked
*/
validatedChecked: KnockoutBindingHandler;
/**
* Validates options selected in a select list.
* Functions in the same way as the <b>ko.bindingHandlers.selectedOptions</b> binding handler, with the
* following alterations:
* <ul>
* <li>registers a blur event handler so validation messages for selections can be displayed</li>
* <li>registers a click event handler so validation state can be marked as touched</i>
* </ul>
* @name ko.bindingHandlers.validatedSelectedOptions
*/
validatedSelectedOptions: KnockoutBindingHandler;
/**
* Validates entries that can be keyed or selected.
* Functions in the same way as the <b>ko.bindingHandlers.value</b> binding handler, with the following
* alterations:
* <ul>
* <li>registers a blur event handler:
* <ul>
* <li>to display validation messages as entries or selections lose focus</li>
* <li>to reformat successfully parsed textual entries</li>
* </ul>
* </li>
* <li>
* registers a focus event handler to pause the update of any existing visible validation message
* </li>
* <li>
* registers a key-up event handler which validates the entry as it's being entered; this allows other
* entries that are shown conditionally to be available before the user tabs out of this entry
* </li>
* </ul>
* @name ko.bindingHandlers.validatedValue
*/
validatedValue: KnockoutBindingHandler;
/**
* Disables the element when the chosen property or model has failed or is pending validation, enabled
* otherwise.
* @name ko.bindingHandlers.disabledWhenNotValid
*/
disabledWhenNotValid: KnockoutBindingHandler;
/**
* Disables the element when the chosen property or model has been touched and has failed or is pending
* validation, enabled otherwise.<br/>
* @name ko.bindingHandlers.disabledWhenTouchedAndNotValid
*/
disabledWhenTouchedAndNotValid: KnockoutBindingHandler;
/**
* Enables the element when the chosen property or model is applicable, disabled otherwise.
* @name ko.bindingHandlers.enabledWhenApplicable
*/
enabledWhenApplicable: KnockoutBindingHandler;
/**
* Sets the text of the element to be a formatted representation of the specified property.
* @name ko.bindingHandlers.formattedText
*/
formattedText: KnockoutBindingHandler;
/**
* Sets CSS classes on the element based on the validation state of the chosen property or model.</br>
* The names of the CSS classes used are held in the <b>ko.bindingHandlers.validationCss.classNames</b> object,
* by default they are:
* <ul>
* <li><b>failed</b> - if validation failed</li>
* <li><b>focused</b> - if the element is in focus</li>
* <li><b>passed</b> - if validation passed</li>
* <li><b>pending</b> - if validation is pending</li>
* <li><b>required</b> - if an entry is required</li>
* <li><b>showMessage</b> - if a validation message should be shown</li>
* <li><b>touched</b> - set if the model or entry has been "touched"</li>
* <li><b>untouched</b> - set if the model or entry has not been "touched"</li>
* </ul>
* @name ko.bindingHandlers.validationCss
*/
validationCss: KnockoutBindingHandler
/**
* Makes the element behave like a validation message for the chosen property or model:
* <ul>
* <li>makes the element visible if the value is invalid</li>
* <li>sets the text of the element to be the underlying validation state's message</li>
* </ul>
* @name ko.bindingHandlers.validationMessage
*/
validationMessage: KnockoutBindingHandler;
/**
* Sets the text of the element to be the underlying validation state's message.
* @name ko.bindingHandlers.validationMessageText
*/
validationMessageText: KnockoutBindingHandler;
/**
* Sets the text of the element to be the underlying validation state's name.
* @name ko.bindingHandlers.validationName
*/
validationName: KnockoutBindingHandler;
/**
* Makes the element visible if the chosen property or model is applicable, invisible otherwise.
* @name ko.bindingHandlers.visibleWhenApplicable
*/
visibleWhenApplicable: KnockoutBindingHandler;
/**
* Makes the element visible when the entry bound to the chosen property is in focus, invisible otherwise.
* @name ko.bindingHandlers.visibleWhenFocused
*/
visibleWhenFocused: KnockoutBindingHandler;
/**
* Makes the element visible when the chosen property or model has failed validation, invisible otherwise.
* @name ko.bindingHandlers.visibleWhenInvalid
*/
visibleWhenInvalid: KnockoutBindingHandler;
/**
* Makes the element visible when the summary for the chosen model is not empty, invisible otherwise.
* @name ko.bindingHandlers.visibleWhenSummaryNotEmpty
*/
visibleWhenSummaryNotEmpty: KnockoutBindingHandler;
/**
* Makes the element visible if the chosen property or model has been touched, invisible otherwise.
* @name ko.bindingHandlers.visibleWhenTouched
*/
visibleWhenTouched: KnockoutBindingHandler;
/**
* Makes the element visible if the chosen property or model is untouched, invisible otherwise.
* @name ko.bindingHandlers.visibleWhenUntouched
*/
visibleWhenUntouched: KnockoutBindingHandler;
/**
* Makes the element visible if the chosen property or model has passed validation.
* @name ko.bindingHandlers.visibleWhenValid
*/
visibleWhenValid: KnockoutBindingHandler;
}
//
// root valerie namespace - static methods
//
declare var valerie: Valerie.Static;
// additional types for Valerie (all inside this namespace)
declare module Valerie {
//
// Static methods on valerie namespace
//
interface Static {
/**
* Maps a source model to a destination model, including only applicable properties
* @param {Object|Array} sourceModel the source model
* @return {*} the destination model
*/
mapApplicableModel(sourceModel: any): any;
/**
* Maps a source model to a destination model.
* @param {Object|Array} sourceModel the source model
* @param {valerie.includePropertyCallback} [includeWrappedFunction] a function called before each source model
* property is unwrapped, the result of which determines if the property is included in the destination model
* @param {valerie.includePropertyCallback} [includeUnwrappedFunction] a function called after each source
* model property is unwrapped, the result of which determines if the property is included in the destination model
* @return {*} the destination model
*/
mapModel(sourceModel: any,
includeWrappedFunction?: IncludePropertyCallback,
includeUnwrappedFunction?: IncludePropertyCallback): any;
/**
* Makes the passed-in model validatable. After invocation the model will have a validation state.
* <br/><b>fluent</b>
* @param {object|function} model the model to make validatable
* @param {valerie.ModelValidationState.options} [options] the options to use when creating the model's validation
* state
* @return {valerie.ModelValidationState} the validation state belonging to the model
*/
validatableModel(model: any, options?: ValidationOptions): ModelValidationState;
// Makes the passed-in property validatable. After invocation the property will have a validation state.
// (value should be observable or computed)
validatableProperty<T>(value: T, options?: ValidationOptions): PropertyValidationState<T>;
// additional namespaces for static methods:
validationState: ValidationState;
}
// callback interface (see mapModel above)
interface IncludePropertyCallback {
(value: any, sourceModel: any, index: any): boolean;
}
// Constructs the validation state for a model, which may comprise of simple properties and sub-models.
interface ModelValidationState {
// ctor
new: (model: any, options?: ModelValidationStateOptions) => ModelValidationState;
addValidationStates(any): void;
model: any;
options?: ModelValidationStateOptions
}
// Construction options for a model validation state.
interface ModelValidationStateOptions {
applicable(): boolean;
excludeFromSummary: boolean;
failureMessage: string;
name(): string;
paused(): boolean;
}
//
// PropertyValidationState
//
interface PropertyValidationState<T> {
// properties:
// the observable or computed the validation state is for
observableOrComputed: T;
// the options to use when creating the validation state
options: ValidationOptions;
// fluent methods (can be chanined):
addRule(IRule): PropertyValidationState<T>;
applicable(value: boolean): PropertyValidationState<T>;
applicable(fn: () => boolean): PropertyValidationState<T>;
currencyMajor(options?: ValidationOptions): PropertyValidationState<T>;
currencyMajorMinor(options?: ValidationOptions): PropertyValidationState<T>;
date(): PropertyValidationState<T>;
during(earliest: Date, latest: Date, options?: ValidationOptions): PropertyValidationState<T>; // date + date
during(earliest: () => Date, latest: Date, options?: ValidationOptions): PropertyValidationState<T>; // dateFN + date
during(earliest: Date, latest: () => Date, options?: ValidationOptions): PropertyValidationState<T>; // date + dateFN
during(earliest: () => Date, latest: () => Date, options?: ValidationOptions): PropertyValidationState<T>; // dateFN + dateFN
earliest(earliest: Date, options?: ValidationOptions): PropertyValidationState<T>; // date value
earliest(earliest: () => Date, options?: ValidationOptions): PropertyValidationState<T>; // date function
email(): PropertyValidationState<T>;
entryFormat(format: string): PropertyValidationState<T>;
excludeFromSummary(): PropertyValidationState<T>;
expression(regularExpression: RegExp, options?: ValidationOptions): PropertyValidationState<T>; // regex
expression(regularExpressionString: string, options?: ValidationOptions): PropertyValidationState<T>; // regex string
float(options?: ValidationOptions): PropertyValidationState<T>;
integer(options?: ValidationOptions): PropertyValidationState<T>;
latest(latestValueOrFunction: Date, options?: ValidationOptions): PropertyValidationState<T>;
latest(latestValueOrFunction: () => Date, options?: ValidationOptions): PropertyValidationState<T>;
lengthBetween(shortest: number, longest: number, options?: ValidationOptions): PropertyValidationState<T>;
lengthBetween(shortest: number, longest: () => number, options?: ValidationOptions): PropertyValidationState<T>;
lengthBetween(shortest: () => number, longest: number, options?: ValidationOptions): PropertyValidationState<T>;
lengthBetween(shortest: () => number, longest: () => number, options?: ValidationOptions): PropertyValidationState<T>;
matches(permitted: any, options?: ValidationOptions): PropertyValidationState<T>;
matches(permitted: () => any, options?: ValidationOptions): PropertyValidationState<T>;
maximum(maximum: any, options?: ValidationOptions): PropertyValidationState<T>;
maximum(maximum: () => any, options?: ValidationOptions): PropertyValidationState<T>;
maximumLength(longest: number, options?: ValidationOptions): PropertyValidationState<T>;
maximumLength(longest: () => number, options?: ValidationOptions): PropertyValidationState<T>;
maximumNumberOfItems(maximum: number, options?: ValidationOptions): PropertyValidationState<T>;
maximumNumberOfItems(maximum: () => number, options?: ValidationOptions): PropertyValidationState<T>;
minimum(minimumValueOrFunction: any, options?: ValidationOptions): PropertyValidationState<T>;
minimumLength(shortest: number, options?: ValidationOptions): PropertyValidationState<T>;
minimumLength(shortest: () => number, options?: ValidationOptions): PropertyValidationState<T>;
minimumNumberOfItems(minimum: number, options?: ValidationOptions): PropertyValidationState<T>;
minimumNumberOfItems(minimum: () => number, options?: ValidationOptions): PropertyValidationState<T>;
name(value: string): PropertyValidationState<T>;
name(value: () => string): PropertyValidationState<T>;
noneOf(forbiddenValues: any[], options?: ValidationOptions): PropertyValidationState<T>;
noneOf(forbiddenValues: () => any[], options?: ValidationOptions): PropertyValidationState<T>;
not(forbiddenValueOrFunction: any, options?: ValidationOptions): PropertyValidationState<T>;
number(): PropertyValidationState<T>;
numberOfItems(minimumValueOrFunction: any, maximumValueOrFunction: any, options?: ValidationOptions): PropertyValidationState<T>;
oneOf(permittedValues: any[], options?: ValidationOptions): PropertyValidationState<T>;
oneOf(permittedValues: () => any[], options?: ValidationOptions): PropertyValidationState<T>;
postcode(): PropertyValidationState<T>;
range(minimumValueOrFunction: any, maximumValueOrFunction: any, options?: ValidationOptions): PropertyValidationState<T>;
required(valueOrFunction?: any): PropertyValidationState<T>;
rule(testFunction: () => any): PropertyValidationState<T>;
ruleMessage(failureMessageFormat: string): PropertyValidationState<T>;
string(): PropertyValidationState<T>;
valueFormat(format: string): PropertyValidationState<T>;
validateChildProperties(): PropertyValidationState<T>;
// return original observable
end(): T;
// other methods: not returning PropertyValidationState<T>
failed(): boolean;
getName(): string;
isApplicable(): boolean;
isRequired(): boolean;
message(): string;
passed(): boolean;
pending(): boolean;
showMessage(): boolean;
touched(): boolean; // get touched state
touched(value: boolean): boolean; // set touched state
result(): ValidationResult;
}
interface ValidationResult {
state: any; // the result state
failed: boolean; //true if the activity failed validation
passed: boolean; //true if the activity passed validation
pending: boolean; //true if the activity hasn't yet completed
message: string; //a message from the activity
new (state: any, message?: string);
//TODO: not added static members/methods
createFailedResult(message: string): ValidationResult;
}
interface IRule {
defaultOptions: ValidationOptions;
test(value: any): ValidationResult;
}
// The interface for a validation state.
interface IValidationState {
failed(): boolean;
getName(): string;
isApplicable(): boolean;
message(): string;
passed(): boolean;
pending(): boolean;
result(): ValidationResult;
touched(value?: boolean): boolean;
}
interface ValidatableModel<T> {
name: (string) => PropertyValidationState<T>;
// return original observableArray
end: () => T;
}
interface ValidationOptions {
applicable? (): any; // the function used to determine if the property is applicable
converter?: IConverter; // the converter used to parse user entries and format display of the property's value
entryFormat?: string; // the string used to format the property's value for display in a user entry
excludeFromSummary?: boolean; // whether any validation failures for this property are excluded from a summary
invalidFailureMessage?: string; // the message shown when the user has entered an invalid value
missingFailureMessage?: string; // the message shown when a value is required but is missing
name?: () => any; // the function used to determine the name of the property; used in failure messages
required?: () => any; // the function used to determine if a value is required
rules?: any; //Valerie.array.<IRule>; // the chain of rules used to validate the property's value
valueFormat?: string; // the string use to format the property's value for display in a message
}
// The interface for a converter, a pair of functions: format and parse, which work in tandem on a single type of value.
interface IConverter {
format: (value: any, format?: string) => string; // Formats the given value as a string.
parse: (value: string) => any; //Parses the given string as a particular value type.
}
// A helper for parsing and formatting numeric values.
interface NumericHelper {
// Adds thousands separators to the given numeric string.
addThousandsSeparator(numericString: string): string;
// Formats the given numeric value as a string.
format(value: number, format: string): string;
// Initialises the helper
init(decimalSeparator: string,
thousandsSeparator: string,
currencySign: string,
currencyMinorUnitPlaces: number): NumericHelper;
// Informs whether the given numeric string represents a currency value with major units only.
isCurrencyMajor(numericString): boolean;
// Informs whether the given numeric string represents a currency value with major units and optionally minor units.
isCurrencyMajorMinor(numericString): boolean;
// Informs whether the given numeric string represents a non-integer numeric value.
isFloat(numericString: string): boolean;
// Informs whether the given numeric string represents an integer value.
isInteger(numericString: string): boolean;
// Attempts to parse the given numeric string as a number value. The string is unformatted first.
parse(numericString: string): number;
// Unformats a numeric string; removes currency signs, thousands separators and normalises decimal separators.
unformat(numericString: string): string;
}
interface ValidationState {
// Finds and returns the validation states
findIn(model: any,
includeSubModels?: boolean,
recurse?: boolean,
validationStates?: IValidationState[]): IValidationState[];
// Gets the validation state for the given model, observable or computed.
getFor(modelOrObservableOrComputed: any): IValidationState;
// nforms if the given model, observable or computed has a validation state.
has(modelOrObservableOrComputed: any): boolean;
// Sets the validation state for the given model, observable or computed.
setFor(modelOrObservableOrComputed: any, state: IValidationState): void;
}
}
declare module Valerie.Rules {
/*
Todo: add classes in valerie.rules namespace
*/
}