diff --git a/Definitions/knockout-2.2.d.ts b/Definitions/knockout-2.2.d.ts index 51e15dd0b4..8ba74c9a3a 100644 --- a/Definitions/knockout-2.2.d.ts +++ b/Definitions/knockout-2.2.d.ts @@ -1,216 +1,268 @@ -// Type definitions for Knockout 2.2 -// Project: http://knockoutjs.com -// Definitions by: Boris Yankov -// Definitions: https://github.com/borisyankov/DefinitelyTyped - - -interface KnockoutSubscription { - dispose(): void; -} - -interface KnockoutObservableArrayFunctions { - // General Array functions - indexOf(searchElement, fromIndex?: number): number; - slice(start: number, end?: number): any[]; - splice(start: number): any[]; - splice(start: number, deleteCount: number, ...items: any[]): any[]; - pop(); - push(...items: any[]): void; - shift(); - unshift(...items: any[]): number; - reverse(): any[]; - sort(): void; - sort(compareFunction): void; - - subscribe(callback: (newValue: any[]) => void ): KnockoutSubscription; - - // Ko specific - remove(item): any[]; - removeAll(items: any[]): any[]; - removeAll(): any[]; - - destroy(item): void; - destroyAll(items: any[]): void; - destroyAll(): void; -} - -interface KnockoutObservableArray extends KnockoutObservableArrayFunctions { - - fn: KnockoutObservableArrayFunctions; - - (): KnockoutObservableArray; - (value: any[]): KnockoutObservableArray; -} - -interface KnockoutObservableBase { - - fn; - - extend(source); - subscribe(func: Function): KnockoutSubscription; -} - -interface KnockoutObservableAny extends KnockoutObservableBase { - - (): any; - (value): void; -} - -interface KnockoutObservableString extends KnockoutObservableBase { - (): string; - (value: string): void; -} - - -interface KnockoutObservableNumber extends KnockoutObservableBase { - (): number; - (value: number): void; -} - -interface KnockoutObservableBool extends KnockoutObservableBase { - (): bool; - (value: bool): void; -} - -interface KnockoutObservableDate extends KnockoutObservableBase { - (): Date; - (value: Date): void; -} - -interface KnockoutComputed extends KnockoutObservableBase { - (): KnockoutComputed; - (func: Function, context?: any): KnockoutComputed; - (def: KnockoutComputedDefine): KnockoutComputed; - (options?: any): KnockoutComputed; - - subscribe(callback: (newValue: number) => void ): KnockoutSubscription; - getDependenciesCount(): number; - hasWriteFunction(): bool; -} - -interface KnockoutComputedDefine { - read(): any; - write(any); -} - -interface KnockoutBindingContext { - $parent: any; - $parents: any[]; - $root: any; - $data: any; - $index?: number; - $parentContext?: KnockoutBindingContext; -} - -interface KnockoutBindingHandler { - // TODO: Work out how to define bindingHandlers when not using all the args - // adding element?: any, etc doesnt work... - init(element: any, valueAccessor: () => any, allBindingsAccessor: () => any, viewModel: any, bindingContext: KnockoutBindingContext): void; - update(element: any, valueAccessor: () => any, allBindingsAccessor: () => any, viewModel: any, bindingContext: KnockoutBindingContext): void; - //init: any; - //update: any; - options?: any; -} - -interface KnockoutBindingHandlers { - value: KnockoutBindingHandler; -} - -interface KnockoutMemoization { - memoize(callback); - unmemoize(memoId, callbackParams); - unmemoizeDomNodeAndDescendants(domNode, extraCallbackParamsArray); - parseMemoText(memoText); -} - -interface KnockoutVirtualElements { - allowedBindings; - emptyNode; - firstChild; - insertAfter; - nextSibling; - prepend; - setDomNodeChildren; -} - -interface KnockoutExtenders { - throttle(target: any, timeout: number): KnockoutComputed; - notify(target: any, notifyWhen: string): any; -} - -interface KnockoutUtils { - - fieldsIncludedWithJsonPost: any[]; - - arrayForEach(array: any[], action: (any) => void ): void; - arrayIndexOf(array: any[], item: any): number; - arrayFirst(array: any[], predicate: (item) => bool, predicateOwner?: any): any; - arrayRemoveItem(array: any[], itemToRemove: any): void; - arrayGetDistinctValues(array: any[]): any[]; - arrayMap(array: any[], mapping: (item) => any): any[]; - arrayFilter(array: any[], predicate: (item) => bool): any[]; - arrayPushAll(array: any[], valuesToPush: any[]): any[]; - - extend(target, source); - - emptyDomNode(domNode): void; - moveCleanedNodesToContainerElement(nodes: any[]): HTMLElement; - cloneNodes(nodesArray: any[], shouldCleanNodes: bool): any[]; - setDomNodeChildren(domNode: any, childNodes: any[]): void; - replaceDomNodes(nodeToReplaceOrNodeArray: any, newNodesArray: any[]): void; - setOptionNodeSelectionState(optionNode: any, isSelected: bool): void; - stringTrim(str: string): string; - stringTokenize(str: string, delimiter: string): string; - stringStartsWith(str: string, startsWith: string): string; - domNodeIsContainedBy(node: any, containedByNode: any): bool; - domNodeIsAttachedToDocument(node: any): bool; - tagNameLower(element: any): string; - registerEventHandler(element: any, eventType: any, handler: Function): void; - triggerEvent(element: any, eventType: any): void; - unwrapObservable(value: any): any; - toggleDomNodeCssClass(node: any, className: string, shouldHaveClass: bool): void; - setTextContent(element: any, textContent: string): void; - setElementName(element: any, name: string): void; - ensureSelectElementIsRenderedCorrectly(selectElement); - forceRefresh(node: any): void; - ensureSelectElementIsRenderedCorrectly(selectElement: any): void; - range(min: any, max: any): any; - makeArray(arrayLikeObject: any): any[]; - getFormFields(form: any, fieldName: string): any[]; - parseJson(jsonString: string): any; - stringifyJson(data: any, replacer: Function, space: string): string; - postJson(urlOrForm: any, data: any, options: any): void; - - domNodeDisposal; -} - - -interface KnockoutStatic { - utils: KnockoutUtils; - memoization: KnockoutMemoization; - bindingHandlers: KnockoutBindingHandlers; - - computed: KnockoutComputed; - observableArray: KnockoutObservableArray; - virtualElements: KnockoutVirtualElements; - extenders: KnockoutExtenders; - - applyBindings(viewModel: any, rootNode?: any): void; - applyBindingsToDescendants(viewModel: any, rootNode: any): void; - - observable(value: string): KnockoutObservableString; - observable(value: Date): KnockoutObservableDate; - observable(value: number): KnockoutObservableNumber; - observable(value: bool): KnockoutObservableBool; - observable(value?: any): KnockoutObservableAny; - - contextFor(node: any): any; - isSubscribable(instance: any): bool; - subscribable(): void; - toJSON(viewModel: any, replacer?: Function, space?: any): string; - toJS(viewModel: any): any; - isObservable(instance: any): bool; - dataFor(node: any): any; -} - +// Type definitions for Knockout 2.2 +// Project: http://knockoutjs.com +// Definitions by: Boris Yankov +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +interface KnockoutSubscribableFunctions { + extend(source); + subscribe(callback: (newValue: any[]) => void, target?:any, topic?: string): KnockoutSubscription; + notifySubscribers(valueToWrite, topic?: string); + dispose(): void; +} + +interface KnockoutComputedFunctions extends KnockoutSubscribableFunctions { + getDependenciesCount(): number; + hasWriteFunction(): bool; +} + +interface KnockoutObservableFunctions extends KnockoutSubscribableFunctions { +} + +interface KnockoutObservableArrayFunctions extends KnockoutObservableFunctions { + // General Array functions + indexOf(searchElement, fromIndex?: number): number; + slice(start: number, end?: number): any[]; + splice(start: number): any[]; + splice(start: number, deleteCount: number, ...items: any[]): any[]; + pop(); + push(...items: any[]): void; + shift(); + unshift(...items: any[]): number; + reverse(): any[]; + sort(): void; + sort(compareFunction): void; + + // Ko specific + remove(item): any[]; + removeAll(items: any[]): any[]; + removeAll(): any[]; + + destroy(item): void; + destroyAll(items: any[]): void; + destroyAll(): void; +} + +interface KnockoutSubscribableStatic { + fn: KnockoutSubscribableFunctions; + + new (): KnockoutSubscription; +} + +interface KnockoutSubscription extends KnockoutSubscribableFunctions { +} + +interface KnockoutComputedStatic { + fn: KnockoutComputedFunctions; + + (): KnockoutComputed; + (func: Function, context?: any): KnockoutComputed; + (def: KnockoutComputedDefine): KnockoutComputed; + (options?: any): KnockoutComputed; +} + +interface KnockoutComputed extends KnockoutComputedFunctions { + (): any; + (value: any): void; +} + +interface KnockoutObservableArrayStatic { + + fn: KnockoutObservableArrayFunctions; + + (): KnockoutObservableArray; + (value: any[]): KnockoutObservableArray; +} + +interface KnockoutObservableArray extends KnockoutObservableArrayFunctions { + (): any[]; + (value: any[]): void; +} + +interface KnockoutObservableStatic { + fn: KnockoutObservableFunctions; + + (value: string): KnockoutObservableString; + (value: Date): KnockoutObservableDate; + (value: number): KnockoutObservableNumber; + (value: bool): KnockoutObservableBool; + (value?: any): KnockoutObservableAny; +} + +interface KnockoutObservableBase extends KnockoutObservableFunctions { +} + +interface KnockoutObservableAny extends KnockoutObservableBase { + + (): any; + (value): void; +} + +interface KnockoutObservableString extends KnockoutObservableBase { + (): string; + (value: string): void; +} + + +interface KnockoutObservableNumber extends KnockoutObservableBase { + (): number; + (value: number): void; +} + +interface KnockoutObservableBool extends KnockoutObservableBase { + (): bool; + (value: bool): void; +} + +interface KnockoutObservableDate extends KnockoutObservableBase { + (): Date; + (value: Date): void; +} + +interface KnockoutComputedDefine { + read(): any; + write(any); +} + +interface KnockoutBindingContext { + $parent: any; + $parents: any[]; + $root: any; + $data: any; + $index?: number; + $parentContext?: KnockoutBindingContext; + + extend(any): any; + createChildContext(any): any; +} + +interface KnockoutBindingHandler { + init?(element: any, valueAccessor: () => any, allBindingsAccessor: () => any, viewModel: any, bindingContext: KnockoutBindingContext): void; + update?(element: any, valueAccessor: () => any, allBindingsAccessor: () => any, viewModel: any, bindingContext: KnockoutBindingContext): void; + options?: any; +} + +interface KnockoutBindingHandlers { + // Controlling text and appearance + visible: KnockoutBindingHandler; + text: KnockoutBindingHandler; + html: KnockoutBindingHandler; + css: KnockoutBindingHandler; + style: KnockoutBindingHandler; + attr: KnockoutBindingHandler; + + // Control Flow + foreach: KnockoutBindingHandler; + if: KnockoutBindingHandler; + ifnot: KnockoutBindingHandler; + with: KnockoutBindingHandler; + + // Working with form fields + click: KnockoutBindingHandler; + event: KnockoutBindingHandler; + submit: KnockoutBindingHandler; + enable: KnockoutBindingHandler; + disable: KnockoutBindingHandler; + value: KnockoutBindingHandler; + hasfocus: KnockoutBindingHandler; + checked: KnockoutBindingHandler; + options: KnockoutBindingHandler; + selectedOptions: KnockoutBindingHandler; + uniqueName: KnockoutBindingHandler; + + // Rendering templates + template: KnockoutBindingHandler; +} + +interface KnockoutMemoization { + memoize(callback); + unmemoize(memoId, callbackParams); + unmemoizeDomNodeAndDescendants(domNode, extraCallbackParamsArray); + parseMemoText(memoText); +} + +interface KnockoutVirtualElements { + allowedBindings; + emptyNode; + firstChild; + insertAfter; + nextSibling; + prepend; + setDomNodeChildren; +} + +interface KnockoutExtenders { + throttle(target: any, timeout: number): KnockoutComputed; + notify(target: any, notifyWhen: string): any; +} + +interface KnockoutUtils { + + fieldsIncludedWithJsonPost: any[]; + + arrayForEach(array: any[], action: (any) => void ): void; + arrayIndexOf(array: any[], item: any): number; + arrayFirst(array: any[], predicate: (item) => bool, predicateOwner?: any): any; + arrayRemoveItem(array: any[], itemToRemove: any): void; + arrayGetDistinctValues(array: any[]): any[]; + arrayMap(array: any[], mapping: (item) => any): any[]; + arrayFilter(array: any[], predicate: (item) => bool): any[]; + arrayPushAll(array: any[], valuesToPush: any[]): any[]; + + extend(target, source); + + emptyDomNode(domNode): void; + moveCleanedNodesToContainerElement(nodes: any[]): HTMLElement; + cloneNodes(nodesArray: any[], shouldCleanNodes: bool): any[]; + setDomNodeChildren(domNode: any, childNodes: any[]): void; + replaceDomNodes(nodeToReplaceOrNodeArray: any, newNodesArray: any[]): void; + setOptionNodeSelectionState(optionNode: any, isSelected: bool): void; + stringTrim(str: string): string; + stringTokenize(str: string, delimiter: string): string; + stringStartsWith(str: string, startsWith: string): string; + domNodeIsContainedBy(node: any, containedByNode: any): bool; + domNodeIsAttachedToDocument(node: any): bool; + tagNameLower(element: any): string; + registerEventHandler(element: any, eventType: any, handler: Function): void; + triggerEvent(element: any, eventType: any): void; + unwrapObservable(value: any): any; + toggleDomNodeCssClass(node: any, className: string, shouldHaveClass: bool): void; + setTextContent(element: any, textContent: string): void; + setElementName(element: any, name: string): void; + ensureSelectElementIsRenderedCorrectly(selectElement); + forceRefresh(node: any): void; + ensureSelectElementIsRenderedCorrectly(selectElement: any): void; + range(min: any, max: any): any; + makeArray(arrayLikeObject: any): any[]; + getFormFields(form: any, fieldName: string): any[]; + parseJson(jsonString: string): any; + stringifyJson(data: any, replacer: Function, space: string): string; + postJson(urlOrForm: any, data: any, options: any): void; + + domNodeDisposal; +} + + +interface KnockoutStatic { + utils: KnockoutUtils; + memoization: KnockoutMemoization; + bindingHandlers: KnockoutBindingHandlers; + virtualElements: KnockoutVirtualElements; + extenders: KnockoutExtenders; + + applyBindings(viewModel: any, rootNode?: any): void; + applyBindingsToDescendants(viewModel: any, rootNode: any): void; + + subscribable: KnockoutSubscribableStatic; + observable: KnockoutObservableStatic; + computed: KnockoutComputedStatic; + observableArray: KnockoutObservableArrayStatic; + + contextFor(node: any): any; + isSubscribable(instance: any): bool; + toJSON(viewModel: any, replacer?: Function, space?: any): string; + toJS(viewModel: any): any; + isObservable(instance: any): bool; + dataFor(node: any): any; +} + declare var ko: KnockoutStatic; \ No newline at end of file diff --git a/Tests/knockout-tests.ts b/Tests/knockout-tests.ts index a2c7e8d8d6..24fe24705b 100644 --- a/Tests/knockout-tests.ts +++ b/Tests/knockout-tests.ts @@ -1,517 +1,558 @@ -/// -/// - -declare var $; - -function test_creatingVMs() { - var myViewModel = { - personName: ko.observable('Bob'), - personAge: ko.observable(123) - }; - ko.applyBindings(myViewModel); - ko.applyBindings(myViewModel, document.getElementById('someElementId')); - - myViewModel.personName(); - myViewModel.personName('Mary'); - myViewModel.personName('Mary').personAge(50); - - myViewModel.personName.subscribe(function (newValue) { - alert("The person's new name is " + newValue); - }); - - var subscription = myViewModel.personName.subscribe(function (newValue) { }); - subscription.dispose(); -} - -function test_computed() { - function AppViewModel() { - var self = this; - - self.firstName = ko.observable('Bob'); - self.lastName = ko.observable('Smith'); - self.fullName = ko.computed(function () { - return self.firstName() + " " + self.lastName(); - }); - } - - function MyViewModel() { - this.firstName = ko.observable('Planet'); - this.lastName = ko.observable('Earth'); - - this.fullName = ko.computed({ - read: function () { - return this.firstName() + " " + this.lastName(); - }, - write: function (value) { - var lastSpacePos = value.lastIndexOf(" "); - if (lastSpacePos > 0) { - this.firstName(value.substring(0, lastSpacePos)); - this.lastName(value.substring(lastSpacePos + 1)); - } - }, - owner: this - }); - } - - function MyViewModel() { - this.price = ko.observable(25.99); - - this.formattedPrice = ko.computed({ - read: function () { - return '$' + this.price().toFixed(2); - }, - write: function (value) { - value = parseFloat(value.replace(/[^\.\d]/g, "")); - this.price(isNaN(value) ? 0 : value); - }, - owner: this - }); - } - - function MyViewModel() { - this.acceptedNumericValue = ko.observable(123); - this.lastInputWasValid = ko.observable(true); - - this.attemptedValue = ko.computed({ - read: this.acceptedNumericValue, - write: function (value) { - if (isNaN(value)) - this.lastInputWasValid(false); - else { - this.lastInputWasValid(true); - this.acceptedNumericValue(value); - } - }, - owner: this - }); - } - - ko.applyBindings(new MyViewModel()); -} - - -function test_observableArrays() { - var myObservableArray = ko.observableArray(); - myObservableArray.push('Some value'); - var anotherObservableArray = ko.observableArray([ - { name: "Bungle", type: "Bear" }, - { name: "George", type: "Hippo" }, - { name: "Zippy", type: "Unknown" } - ]); - - myObservableArray().length; - myObservableArray()[0]; - - myObservableArray.indexOf('Blah'); - myObservableArray.push('Some new value'); - myObservableArray.pop(); - myObservableArray.unshift('Some new value'); - myObservableArray.shift(); - myObservableArray.reverse(); - myObservableArray.sort(function (left, right) { return left.lastName == right.lastName ? 0 : (left.lastName < right.lastName ? -1 : 1) }); - myObservableArray.splice(1, 3); - - var someItem: KnockoutObservableArray; - myObservableArray.remove(someItem); - myObservableArray.remove(function (item) { return item.age < 18 }); - myObservableArray.removeAll(['Chad', 132, undefined]); - myObservableArray.removeAll(); - myObservableArray.destroy(someItem); - myObservableArray.destroy(function (someItem) { return someItem.age < 18 }); - myObservableArray.destroyAll(['Chad', 132, undefined]); - myObservableArray.destroyAll(); -} - -function test_bindings() { - var currentProfit = ko.observable(150000); - ko.applyBindings({ - people: [ - { firstName: 'Bert', lastName: 'Bertington' }, - { firstName: 'Charles', lastName: 'Charlesforth' }, - { firstName: 'Denise', lastName: 'Dentiste' } - ] - }); - var viewModel = { availableCountries: ko.observableArray(['France', 'Germany', 'Spain']) }; - viewModel.availableCountries.push('China'); - - ko.bindingHandlers.yourBindingName = { - init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - }, - update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - } - }; - ko.bindingHandlers.slideVisible = { - update: function (element, valueAccessor, allBindingsAccessor) { - var value = valueAccessor(), allBindings = allBindingsAccessor(); - var valueUnwrapped = ko.utils.unwrapObservable(value); - var duration = allBindings.slideDuration || 400; - if (valueUnwrapped == true) - $(element).slideDown(duration); - else - $(element).slideUp(duration); - }, - init: function (element, valueAccessor) { - var value = ko.utils.unwrapObservable(valueAccessor()); - $(element).toggle(value); - }, - update: function (element, valueAccessor, allBindingsAccessor) { - } - }; - ko.bindingHandlers.hasFocus = { - init: function (element, valueAccessor) { - $(element).focus(function () { - var value = valueAccessor(); - value(true); - }); - $(element).blur(function () { - var value = valueAccessor(); - value(false); - }); - }, - update: function (element, valueAccessor) { - var value = valueAccessor(); - if (ko.utils.unwrapObservable(value)) - element.focus(); - else - element.blur(); - } - }; - ko.bindingHandlers.allowBindings = { - init: function (elem, valueAccessor) { - var shouldAllowBindings = ko.utils.unwrapObservable(valueAccessor()); - return { controlsDescendantBindings: !shouldAllowBindings }; - } - }; - ko.bindingHandlers.withProperties = { - init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - var newProperties = valueAccessor(), - innerBindingContext = bindingContext.extend(newProperties); - ko.applyBindingsToDescendants(innerBindingContext, element); - return { controlsDescendantBindings: true }; - } - }; - ko.bindingHandlers.withProperties = { - init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { - var newProperties = valueAccessor(), - childBindingContext = bindingContext.createChildContext(viewModel); - ko.utils.extend(childBindingContext, newProperties); - ko.applyBindingsToDescendants(childBindingContext, element); - return { controlsDescendantBindings: true }; - } - }; - ko.bindingHandlers.randomOrder = { - init: function (elem, valueAccessor) { - var child = ko.virtualElements.firstChild(elem), - childElems = []; - while (child) { - childElems.push(child); - child = ko.virtualElements.nextSibling(child); - } - ko.virtualElements.emptyNode(elem); - while (childElems.length) { - var randomIndex = Math.floor(Math.random() * childElems.length), - chosenChild = childElems.splice(randomIndex, 1); - ko.virtualElements.prepend(elem, chosenChild[0]); - } - } - }; - - var node, containerElem, nodeToInsert, insertAfter, nodeToPrepend, arrayOfNodes; - ko.virtualElements.allowedBindings.mySuperBinding = true; - ko.virtualElements.emptyNode(containerElem); - ko.virtualElements.firstChild(containerElem); - ko.virtualElements.insertAfter(containerElem, nodeToInsert, insertAfter); - ko.virtualElements.nextSibling(node); - ko.virtualElements.prepend(containerElem, nodeToPrepend); - ko.virtualElements.setDomNodeChildren(containerElem, arrayOfNodes); -} - -function test_more() { - var viewModel = { - firstName: ko.observable("Bert"), - lastName: ko.observable("Smith"), - pets: ko.observableArray(["Cat", "Dog", "Fish"]), - type: "Customer", - hasALotOfPets: any - }; - viewModel.hasALotOfPets = ko.computed(function () { - return this.pets().length > 2 - }, viewModel); - var plainJs = ko.toJS(viewModel); - - ko.extenders.logChange = function (target, option) { - target.subscribe(function (newValue) { - console.log(option + ": " + newValue); - }); - return target; - }; - - ko.extenders.numeric = function (target, precision) { - var result = ko.computed({ - read: target, - write: function (newValue) { - var current = target(), - roundingMultiplier = Math.pow(10, precision), - newValueAsNum = isNaN(newValue) ? 0 : parseFloat(+newValue), - valueToWrite = Math.round(newValueAsNum * roundingMultiplier) / roundingMultiplier; - - if (valueToWrite !== current) { - target(valueToWrite); - } else { - if (newValue !== current) { - target.notifySubscribers(valueToWrite); - } - } - } - }); - - result(target()); - - return result; - }; - - function AppViewModel(one, two) { - this.myNumberOne = ko.observable(one).extend({ numeric: 0 }); - this.myNumberTwo = ko.observable(two).extend({ numeric: 2 }); - } - - ko.applyBindings(new AppViewModel(221.2234, 123.4525)); - - ko.extenders.required = function (target, overrideMessage) { - - target.hasError = ko.observable(); - target.validationMessage = ko.observable(); - - function validate(newValue) { - target.hasError(newValue ? false : true); - target.validationMessage(newValue ? "" : overrideMessage || "This field is required"); - } - - validate(target()); - - target.subscribe(validate); - - return target; - }; - - function AppViewModel(first, last) { - this.firstName = ko.observable(first).extend({ required: "Please enter a first name" }); - this.lastName = ko.observable(last).extend({ required: "" }); - } - - ko.applyBindings(new AppViewModel("Bob", "Smith")); - - var first; - this.firstName = ko.observable(first).extend({ required: "Please enter a first name", logChange: "first name" }); - - var upperCaseName = ko.computed(function () { - return name().toUpperCase(); - }).extend({ throttle: 500 }); - - function AppViewModel() { - this.instantaneousValue = ko.observable(); - this.throttledValue = ko.computed(this.instantaneousValue) - .extend({ throttle: 400 }); - - this.loggedValues = ko.observableArray([]); - this.throttledValue.subscribe(function (val) { - if (val !== '') - this.loggedValues.push(val); - }, this); - } - - function GridViewModel() { - this.pageSize = ko.observable(20); - this.pageIndex = ko.observable(1); - this.currentPageData = ko.observableArray(); - - ko.computed(function () { - var params = { page: this.pageIndex(), size: this.pageSize() }; - $.getJSON('/Some/Json/Service', params, this.currentPageData); - }, this); - } - this.setPageSize = function (newPageSize) { - this.pageSize(newPageSize); - this.pageIndex(1); - } - - ko.computed(function () { - var params = { page: this.pageIndex(), size: this.pageSize() }; - $.getJSON('/Some/Json/Service', params, this.currentPageData); - }, this).extend({ throttle: 1 }); - - $(".remove").click(function () { - viewModel.items.remove(ko.dataFor(this)); - }); - $(".remove").live("click", function () { - viewModel.items.remove(ko.dataFor(this)); - }); - - $("#people").delegate(".remove", "click", function () { - - var context = ko.contextFor(this), - parentArray = context.$parent.people || context.$parent.children; - - parentArray.remove(context.$data); - - return false; - }); - $("#people").delegate(".add", "click", function () { - var context = ko.contextFor(this), - childName = context.$data.name() + " child", - parentArray = context.$data.people || context.$data.children; - - context.$root.addChild(childName, parentArray); - - return false; - }); - ko.observableArray.fn.filterByProperty = function (propName, matchValue) { - return ko.computed(function () { - var allItems = this(), matchingItems = []; - for (var i = 0; i < allItems.length; i++) { - var current = allItems[i]; - if (ko.utils.unwrapObservable(current[propName]) === matchValue) - matchingItems.push(current); - } - return matchingItems; - }, this); - } - function Task(title, done) { - this.title = ko.observable(title); - this.done = ko.observable(done); - } - - function AppViewModel() { - this.tasks = ko.observableArray([ - new Task('Find new desktop background', true), - new Task('Put shiny stickers on laptop', false), - new Task('Request more reggae music in the office', true) - ]); - - this.doneTasks = this.tasks.filterByProperty("done", true); - } - - ko.applyBindings(new AppViewModel()); - this.doneTasks = ko.computed(function () { - var all = this.tasks(), done = []; - for (var i = 0; i < all.length; i++) - if (all[i].done()) - done.push(all[i]); - return done; - }, this); -} - -function test_mappingplugin() { - var viewModel = { - serverTime: ko.observable(), - numUsers: ko.observable() - } - var data = { - serverTime: '2010-01-07', - numUsers: 3 - }; - viewModel.serverTime(data.serverTime); - viewModel.numUsers(data.numUsers); - - var viewModel = ko.mapping.fromJS(data); - ko.mapping.fromJS(data, viewModel); - var unmapped = ko.mapping.toJS(viewModel); - - var viewModel = ko.mapping.fromJS(data); - ko.mapping.fromJS(data, viewModel); - - var myChildModel = function (data) { - ko.mapping.fromJS(data, {}, this); - - this.nameLength = ko.computed(function () { - return this.name().length; - }, this); - } - - var oldOptions = ko.mapping.defaultOptions().include; - ko.mapping.defaultOptions().include = ["alwaysIncludeThis"]; - - var oldOptions = ko.mapping.defaultOptions().copy; - ko.mapping.defaultOptions().copy = ["alwaysCopyThis"]; - - var someObject; - ko.mapping.fromJS(data, {}, someObject); - ko.mapping.fromJS(data, {}, this); - - var alice, aliceMappingOptions, bob, bobMappingOptions; - var viewModel = ko.mapping.fromJS(alice, aliceMappingOptions); - ko.mapping.fromJS(bob, bobMappingOptions, viewModel); - - var obj; - var result = ko.mapping.fromJS(obj, { - key: function (item) { - return ko.utils.unwrapObservable(item.id); - } - }); - - result.mappedRemove({ id: 2 }); - var newItem = result.mappedCreate({ id: 3 }); -} - -function test_misc() { - var postbox = new ko.subscribable(); - postbox.subscribe(callback, target, topic); - - postbox.subscribe(function (newValue) { - this.latestTopic(newValue); - }, vm, "mytopic"); - postbox.notifySubscribers(value, "mytopic"); - - ko.subscribable.fn.publishOn = function (topic) { - this.subscribe(function (newValue) { - postbox.notifySubscribers(newValue, topic); - }); - - return this; - }; - - this.myObservable = ko.observable("myValue").publishOn("myTopic"); - - ko.subscribable.fn.subscribeTo = function (topic) { - postbox.subscribe(this, null, topic); - - return this; - }; - - this.observableFromAnotherVM = ko.observable().subscribeTo("myTopic"); - - postbox.subscribe(function (newValue) { - this(newValue); - }, this, topic); - - ko.bindingHandlers.isolatedOptions = { - init: function (element, valueAccessor) { - var args = arguments; - ko.computed({ - read: function () { - ko.utils.unwrapObservable(valueAccessor()); - ko.bindingHandlers.options.update.apply(this, args); - }, - owner: this, - disposeWhenNodeIsRemoved: element - }); - } - }; - - ko.subscribable.fn.publishOn = function (topic) { - this.subscribe(function (newValue) { - postbox.notifySubscribers(newValue, topic); - }); - - return this; - }; - - this.myObservable = ko.observable("myValue").publishOn("myTopic"); - - var x = ko.observableArray([1, 2, 3]); - - var element; - ko.utils.domNodeDisposal.addDisposeCallback(element, function () { - $(element).datepicker("destroy"); - }); +/// +/// + +declare var $; + +function test_creatingVMs() { + var myViewModel = { + personName: ko.observable('Bob'), + personAge: ko.observable(123) + }; + ko.applyBindings(myViewModel); + ko.applyBindings(myViewModel, document.getElementById('someElementId')); + + myViewModel.personName(); + myViewModel.personName('Mary'); + myViewModel.personAge(50); + + myViewModel.personName.subscribe(function (newValue) { + alert("The person's new name is " + newValue); + }); + + var subscription = myViewModel.personName.subscribe(function (newValue) { }); + subscription.dispose(); +} + +function test_computed() { + function AppViewModel() { + var self = this; + + self.firstName = ko.observable('Bob'); + self.lastName = ko.observable('Smith'); + self.fullName = ko.computed(function () { + return self.firstName() + " " + self.lastName(); + }); + } + + function MyViewModel() { + this.firstName = ko.observable('Planet'); + this.lastName = ko.observable('Earth'); + + this.fullName = ko.computed({ + read: function () { + return this.firstName() + " " + this.lastName(); + }, + write: function (value) { + var lastSpacePos = value.lastIndexOf(" "); + if (lastSpacePos > 0) { + this.firstName(value.substring(0, lastSpacePos)); + this.lastName(value.substring(lastSpacePos + 1)); + } + }, + owner: this + }); + } + + function MyViewModel() { + this.price = ko.observable(25.99); + + this.formattedPrice = ko.computed({ + read: function () { + return '$' + this.price().toFixed(2); + }, + write: function (value) { + value = parseFloat(value.replace(/[^\.\d]/g, "")); + this.price(isNaN(value) ? 0 : value); + }, + owner: this + }); + } + + function MyViewModel() { + this.acceptedNumericValue = ko.observable(123); + this.lastInputWasValid = ko.observable(true); + + this.attemptedValue = ko.computed({ + read: this.acceptedNumericValue, + write: function (value) { + if (isNaN(value)) + this.lastInputWasValid(false); + else { + this.lastInputWasValid(true); + this.acceptedNumericValue(value); + } + }, + owner: this + }); + } + + ko.applyBindings(new MyViewModel()); +} + + +function test_observableArrays() { + var myObservableArray = ko.observableArray(); + myObservableArray.push('Some value'); + var anotherObservableArray = ko.observableArray([ + { name: "Bungle", type: "Bear" }, + { name: "George", type: "Hippo" }, + { name: "Zippy", type: "Unknown" } + ]); + + myObservableArray().length; + myObservableArray()[0]; + + myObservableArray.indexOf('Blah'); + myObservableArray.push('Some new value'); + myObservableArray.pop(); + myObservableArray.unshift('Some new value'); + myObservableArray.shift(); + myObservableArray.reverse(); + myObservableArray.sort(function (left, right) { return left.lastName == right.lastName ? 0 : (left.lastName < right.lastName ? -1 : 1) }); + myObservableArray.splice(1, 3); + + myObservableArray.remove('Blah'); + myObservableArray.remove(function (item) { return item.age < 18 }); + myObservableArray.removeAll(['Chad', 132, undefined]); + myObservableArray.removeAll(); + myObservableArray.destroy('Blah'); + myObservableArray.destroy(function (someItem) { return someItem.age < 18 }); + myObservableArray.destroyAll(['Chad', 132, undefined]); + myObservableArray.destroyAll(); + + ko.utils.arrayForEach(myObservableArray(), function (item) { }); +} + +// You have to extend knockout for your own handlers +interface KnockoutBindingHandlers { + yourBindingName: KnockoutBindingHandler; + slideVisible: KnockoutBindingHandler; + hasFocus: KnockoutBindingHandler; + allowBindings: KnockoutBindingHandler; + withProperties: KnockoutBindingHandler; + randomOrder: KnockoutBindingHandler; +} + +function test_bindings() { + var currentProfit = ko.observable(150000); + ko.applyBindings({ + people: [ + { firstName: 'Bert', lastName: 'Bertington' }, + { firstName: 'Charles', lastName: 'Charlesforth' }, + { firstName: 'Denise', lastName: 'Dentiste' } + ] + }); + var viewModel = { availableCountries: ko.observableArray(['France', 'Germany', 'Spain']) }; + viewModel.availableCountries.push('China'); + + ko.bindingHandlers.yourBindingName = { + init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { + }, + update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { + } + }; + ko.bindingHandlers.slideVisible = { + update: function (element, valueAccessor, allBindingsAccessor) { + var value = valueAccessor(), allBindings = allBindingsAccessor(); + var valueUnwrapped = ko.utils.unwrapObservable(value); + var duration = allBindings.slideDuration || 400; + if (valueUnwrapped == true) + $(element).slideDown(duration); + else + $(element).slideUp(duration); + }, + init: function (element, valueAccessor) { + var value = ko.utils.unwrapObservable(valueAccessor()); + $(element).toggle(value); + }, + update: function (element, valueAccessor, allBindingsAccessor) { + } + }; + ko.bindingHandlers.hasFocus = { + init: function (element, valueAccessor) { + $(element).focus(function () { + var value = valueAccessor(); + value(true); + }); + $(element).blur(function () { + var value = valueAccessor(); + value(false); + }); + }, + update: function (element, valueAccessor) { + var value = valueAccessor(); + if (ko.utils.unwrapObservable(value)) + element.focus(); + else + element.blur(); + } + }; + ko.bindingHandlers.allowBindings = { + init: function (elem, valueAccessor) { + var shouldAllowBindings = ko.utils.unwrapObservable(valueAccessor()); + return { controlsDescendantBindings: !shouldAllowBindings }; + } + }; + ko.bindingHandlers.withProperties = { + init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { + var newProperties = valueAccessor(), + innerBindingContext = bindingContext.extend(newProperties); + ko.applyBindingsToDescendants(innerBindingContext, element); + return { controlsDescendantBindings: true }; + } + }; + ko.bindingHandlers.withProperties = { + init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) { + var newProperties = valueAccessor(), + childBindingContext = bindingContext.createChildContext(viewModel); + ko.utils.extend(childBindingContext, newProperties); + ko.applyBindingsToDescendants(childBindingContext, element); + return { controlsDescendantBindings: true }; + } + }; + ko.bindingHandlers.randomOrder = { + init: function (elem, valueAccessor) { + var child = ko.virtualElements.firstChild(elem), + childElems = []; + while (child) { + childElems.push(child); + child = ko.virtualElements.nextSibling(child); + } + ko.virtualElements.emptyNode(elem); + while (childElems.length) { + var randomIndex = Math.floor(Math.random() * childElems.length), + chosenChild = childElems.splice(randomIndex, 1); + ko.virtualElements.prepend(elem, chosenChild[0]); + } + } + }; + + var node, containerElem, nodeToInsert, insertAfter, nodeToPrepend, arrayOfNodes; + ko.virtualElements.allowedBindings.mySuperBinding = true; + ko.virtualElements.emptyNode(containerElem); + ko.virtualElements.firstChild(containerElem); + ko.virtualElements.insertAfter(containerElem, nodeToInsert, insertAfter); + ko.virtualElements.nextSibling(node); + ko.virtualElements.prepend(containerElem, nodeToPrepend); + ko.virtualElements.setDomNodeChildren(containerElem, arrayOfNodes); +} + +// Have to define your own extenders +interface KnockoutExtenders { + logChange(target, option); + numeric(target, precision); + required(target, overrideMessage); +} + +interface KnockoutObservableArrayFunctions { + filterByProperty(propName, matchValue): KnockoutComputed; +} + +declare var validate; + +function test_more() { + var viewModel = { + firstName: ko.observable("Bert"), + lastName: ko.observable("Smith"), + pets: ko.observableArray(["Cat", "Dog", "Fish"]), + type: "Customer", + hasALotOfPets: null + }; + viewModel.hasALotOfPets = ko.computed(function () { + return this.pets().length > 2 + }, viewModel); + var plainJs = ko.toJS(viewModel); + + ko.extenders.logChange = function (target, option) { + target.subscribe(function (newValue) { + console.log(option + ": " + newValue); + }); + return target; + }; + + ko.extenders.numeric = function (target, precision) { + var result = ko.computed({ + read: target, + write: function (newValue) { + var current = target(), + roundingMultiplier = Math.pow(10, precision), + newValueAsNum = isNaN(newValue) ? 0 : parseFloat(newValue), + valueToWrite = Math.round(newValueAsNum * roundingMultiplier) / roundingMultiplier; + + if (valueToWrite !== current) { + target(valueToWrite); + } else { + if (newValue !== current) { + target.notifySubscribers(valueToWrite); + } + } + } + }); + + result(target()); + + return result; + }; + + function AppViewModel(one, two) { + this.myNumberOne = ko.observable(one).extend({ numeric: 0 }); + this.myNumberTwo = ko.observable(two).extend({ numeric: 2 }); + } + + ko.applyBindings(new AppViewModel(221.2234, 123.4525)); + + ko.extenders.required = function (target, overrideMessage) { + + target.hasError = ko.observable(); + target.validationMessage = ko.observable(); + + function validate(newValue) { + target.hasError(newValue ? false : true); + target.validationMessage(newValue ? "" : overrideMessage || "This field is required"); + } + + validate(target()); + + target.subscribe(validate); + + return target; + }; + + function AppViewModel(first, last) { + this.firstName = ko.observable(first).extend({ required: "Please enter a first name" }); + this.lastName = ko.observable(last).extend({ required: "" }); + } + + ko.applyBindings(new AppViewModel("Bob", "Smith")); + + var first; + this.firstName = ko.observable(first).extend({ required: "Please enter a first name", logChange: "first name" }); + + var upperCaseName = ko.computed(function () { + return name.toUpperCase(); + }).extend({ throttle: 500 }); + + function AppViewModel() { + this.instantaneousValue = ko.observable(); + this.throttledValue = ko.computed(this.instantaneousValue) + .extend({ throttle: 400 }); + + this.loggedValues = ko.observableArray([]); + this.throttledValue.subscribe(function (val) { + if (val !== '') + this.loggedValues.push(val); + }, this); + } + + function GridViewModel() { + this.pageSize = ko.observable(20); + this.pageIndex = ko.observable(1); + this.currentPageData = ko.observableArray(); + + ko.computed(function () { + var params = { page: this.pageIndex(), size: this.pageSize() }; + $.getJSON('/Some/Json/Service', params, this.currentPageData); + }, this); + } + this.setPageSize = function (newPageSize) { + this.pageSize(newPageSize); + this.pageIndex(1); + } + + ko.computed(function () { + var params = { page: this.pageIndex(), size: this.pageSize() }; + $.getJSON('/Some/Json/Service', params, this.currentPageData); + }, this).extend({ throttle: 1 }); + + $(".remove").click(function () { + viewModel.pets.remove(ko.dataFor(this)); + }); + $(".remove").live("click", function () { + viewModel.pets.remove(ko.dataFor(this)); + }); + + $("#people").delegate(".remove", "click", function () { + + var context = ko.contextFor(this), + parentArray = context.$parent.people || context.$parent.children; + + parentArray.remove(context.$data); + + return false; + }); + $("#people").delegate(".add", "click", function () { + var context = ko.contextFor(this), + childName = context.$data.name() + " child", + parentArray = context.$data.people || context.$data.children; + + context.$root.addChild(childName, parentArray); + + return false; + }); + ko.observableArray.fn.filterByProperty = function (propName, matchValue) { + return ko.computed(function () { + var allItems = this(), matchingItems = []; + for (var i = 0; i < allItems.length; i++) { + var current = allItems[i]; + if (ko.utils.unwrapObservable(current[propName]) === matchValue) + matchingItems.push(current); + } + return matchingItems; + }, this); + } + function Task(title, done) { + this.title = ko.observable(title); + this.done = ko.observable(done); + } + + function AppViewModel() { + this.tasks = ko.observableArray([ + new Task('Find new desktop background', true), + new Task('Put shiny stickers on laptop', false), + new Task('Request more reggae music in the office', true) + ]); + + this.doneTasks = this.tasks.filterByProperty("done", true); + } + + ko.applyBindings(new AppViewModel()); + this.doneTasks = ko.computed(function () { + var all = this.tasks(), done = []; + for (var i = 0; i < all.length; i++) + if (all[i].done()) + done.push(all[i]); + return done; + }, this); +} + +function test_mappingplugin() { + var viewModel = { + serverTime: ko.observable(), + numUsers: ko.observable() + } + var data = { + serverTime: '2010-01-07', + numUsers: 3 + }; + viewModel.serverTime(data.serverTime); + viewModel.numUsers(data.numUsers); + + var viewModel = ko.mapping.fromJS(data); + ko.mapping.fromJS(data, viewModel); + var unmapped = ko.mapping.toJS(viewModel); + + var viewModel = ko.mapping.fromJS(data); + ko.mapping.fromJS(data, viewModel); + + var myChildModel = function (data) { + ko.mapping.fromJS(data, {}, this); + + this.nameLength = ko.computed(function () { + return this.name().length; + }, this); + } + + var oldOptions = ko.mapping.defaultOptions().include; + ko.mapping.defaultOptions().include = ["alwaysIncludeThis"]; + + var oldOptions = ko.mapping.defaultOptions().copy; + ko.mapping.defaultOptions().copy = ["alwaysCopyThis"]; + + var someObject; + ko.mapping.fromJS(data, {}, someObject); + ko.mapping.fromJS(data, {}, this); + + var alice, aliceMappingOptions, bob, bobMappingOptions; + var viewModel = ko.mapping.fromJS(alice, aliceMappingOptions); + ko.mapping.fromJS(bob, bobMappingOptions, viewModel); + + var obj; + var result = ko.mapping.fromJS(obj, { + key: function (item) { + return ko.utils.unwrapObservable(item.id); + } + }); + + result.mappedRemove({ id: 2 }); + var newItem = result.mappedCreate({ id: 3 }); +} + +// Define your own functions +interface KnockoutSubscribableFunctions { + publishOn(topic: string): any; + subscribeTo(topic: string): any; +} + +interface KnockoutBindingHandlers { + isolatedOptions: KnockoutBindingHandler; +} + +function test_misc() { + // define dummy vars + var callback: any; + var target: any; + var topic: any; + var vm: any; + var value: any; + + var postbox = new ko.subscribable(); + postbox.subscribe(callback, target, topic); + + postbox.subscribe(function (newValue) { + this.latestTopic(newValue); + }, vm, "mytopic"); + postbox.notifySubscribers(value, "mytopic"); + + ko.subscribable.fn.publishOn = function (topic) { + this.subscribe(function (newValue) { + postbox.notifySubscribers(newValue, topic); + }); + + return this; + }; + + this.myObservable = ko.observable("myValue").publishOn("myTopic"); + + ko.subscribable.fn.subscribeTo = function (topic) { + postbox.subscribe(this, null, topic); + + return this; + }; + + this.observableFromAnotherVM = ko.observable().subscribeTo("myTopic"); + + postbox.subscribe(function (newValue) { + this(newValue); + }, this, topic); + + ko.bindingHandlers.isolatedOptions = { + init: function (element, valueAccessor) { + var args = arguments; + ko.computed({ + read: function () { + ko.utils.unwrapObservable(valueAccessor()); + ko.bindingHandlers.options.update.apply(this, args); + }, + owner: this, + disposeWhenNodeIsRemoved: element + }); + } + }; + + ko.subscribable.fn.publishOn = function (topic) { + this.subscribe(function (newValue) { + postbox.notifySubscribers(newValue, topic); + }); + + return this; + }; + + this.myObservable = ko.observable("myValue").publishOn("myTopic"); + + var x = ko.observableArray([1, 2, 3]); + + var element; + ko.utils.domNodeDisposal.addDisposeCallback(element, function () { + $(element).datepicker("destroy"); + }); } \ No newline at end of file