├── .travis.yml ├── src ├── version.js ├── binding │ ├── defaultBindings │ │ ├── click.js │ │ ├── uniqueName.js │ │ ├── visible.js │ │ ├── html.js │ │ ├── style.js │ │ ├── text.js │ │ ├── enableDisable.js │ │ ├── css.js │ │ ├── submit.js │ │ ├── selectedOptions.js │ │ ├── attr.js │ │ ├── ifIfnotWith.js │ │ ├── foreach.js │ │ ├── event.js │ │ ├── hasfocus.js │ │ ├── value.js │ │ └── checked.js │ ├── bindingProvider.js │ ├── selectExtensions.js │ └── editDetection │ │ └── compareArrays.js ├── namespace.js ├── google-closure-compiler-utils.js ├── templating │ ├── native │ │ └── nativeTemplateEngine.js │ ├── templateEngine.js │ ├── templateRewriting.js │ ├── jquery.tmpl │ │ └── jqueryTmplTemplateEngine.js │ └── templateSources.js ├── subscribables │ ├── dependencyDetection.js │ ├── observable.js │ ├── subscribable.js │ ├── extenders.js │ ├── mappingHelpers.js │ ├── observableArray.changeTracking.js │ └── observableArray.js ├── utils.domData.js ├── memoization.js ├── utils.domNodeDisposal.js └── utils.domManipulation.js ├── spec ├── lib │ ├── jasmine.extensions.css │ ├── innershiv.js │ ├── jasmine-1.2.0 │ │ ├── MIT.LICENSE │ │ ├── jasmine-tap.js │ │ └── jasmine.css │ └── jasmine.extensions.js ├── defaultBindings │ ├── uniqueNameBehaviors.js │ ├── submitBehaviors.js │ ├── styleBehaviors.js │ ├── clickBehaviors.js │ ├── visibleBehaviors.js │ ├── enableDisableBehaviors.js │ ├── htmlBehaviors.js │ ├── attrBehaviors.js │ ├── cssBehaviors.js │ ├── ifnotBehaviors.js │ ├── hasfocusBehaviors.js │ ├── textBehaviors.js │ ├── eventBehaviors.js │ ├── ifBehaviors.js │ └── selectedOptionsBehaviors.js ├── extenderBehaviors.js ├── dependentObservableDomBehaviors.js ├── utilsBehaviors.js ├── memoizationBehaviors.js ├── jsonPostingBehaviors.js ├── runner.node.js ├── bindingPreprocessingBehaviors.js ├── domNodeDisposalBehaviors.js ├── arrayEditDetectionBehaviors.js ├── subscribableBehaviors.js ├── nodePreprocessingBehaviors.js ├── runner.phantom.js └── runner.html ├── .npmignore ├── .gitignore ├── package.json ├── README.md └── Gruntfile.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | -------------------------------------------------------------------------------- /src/version.js: -------------------------------------------------------------------------------- 1 | ko.version = "##VERSION##"; 2 | 3 | ko.exportSymbol('version', ko.version); 4 | -------------------------------------------------------------------------------- /spec/lib/jasmine.extensions.css: -------------------------------------------------------------------------------- 1 | #HTMLReporter { 2 | position: absolute; 3 | background: #EEE; 4 | } 5 | -------------------------------------------------------------------------------- /src/binding/defaultBindings/click.js: -------------------------------------------------------------------------------- 1 | // 'click' is just a shorthand for the usual full-length event:{click:handler} 2 | makeEventHandlerShortcut('click'); 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Only package the build output for npm. Developers who want sources/tests can get them from the KO repo. 2 | * 3 | !build/output/knockout-latest.js 4 | !build/output/knockout-latest.debug.js 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.swp 3 | *.csproj.user 4 | bin 5 | obj 6 | *.pdb 7 | _ReSharper* 8 | *.ReSharper.user 9 | *.ReSharper 10 | desktop.ini 11 | .eprj 12 | perf/* 13 | *.orig 14 | 15 | .DS_Store 16 | npm-debug.log 17 | node_modules -------------------------------------------------------------------------------- /src/namespace.js: -------------------------------------------------------------------------------- 1 | // Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler). 2 | // In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable. 3 | var ko = typeof koExports !== 'undefined' ? koExports : {}; 4 | -------------------------------------------------------------------------------- /src/binding/defaultBindings/uniqueName.js: -------------------------------------------------------------------------------- 1 | ko.bindingHandlers['uniqueName'] = { 2 | 'init': function (element, valueAccessor) { 3 | if (valueAccessor()) { 4 | var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex); 5 | ko.utils.setElementName(element, name); 6 | } 7 | } 8 | }; 9 | ko.bindingHandlers['uniqueName'].currentIndex = 0; 10 | -------------------------------------------------------------------------------- /src/binding/defaultBindings/visible.js: -------------------------------------------------------------------------------- 1 | ko.bindingHandlers['visible'] = { 2 | 'update': function (element, valueAccessor) { 3 | var value = ko.utils.unwrapObservable(valueAccessor()); 4 | var isCurrentlyVisible = !(element.style.display == "none"); 5 | if (value && !isCurrentlyVisible) 6 | element.style.display = ""; 7 | else if ((!value) && isCurrentlyVisible) 8 | element.style.display = "none"; 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /src/binding/defaultBindings/html.js: -------------------------------------------------------------------------------- 1 | ko.bindingHandlers['html'] = { 2 | 'init': function() { 3 | // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications) 4 | return { 'controlsDescendantBindings': true }; 5 | }, 6 | 'update': function (element, valueAccessor) { 7 | // setHtml will unwrap the value if needed 8 | ko.utils.setHtml(element, valueAccessor()); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /src/binding/defaultBindings/style.js: -------------------------------------------------------------------------------- 1 | ko.bindingHandlers['style'] = { 2 | 'update': function (element, valueAccessor) { 3 | var value = ko.utils.unwrapObservable(valueAccessor() || {}); 4 | ko.utils.objectForEach(value, function(styleName, styleValue) { 5 | styleValue = ko.utils.unwrapObservable(styleValue); 6 | element.style[styleName] = styleValue || ""; // Empty string removes the value, whereas null/undefined have no effect 7 | }); 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/binding/defaultBindings/text.js: -------------------------------------------------------------------------------- 1 | ko.bindingHandlers['text'] = { 2 | 'init': function() { 3 | // Prevent binding on the dynamically-injected text node (as developers are unlikely to expect that, and it has security implications). 4 | // It should also make things faster, as we no longer have to consider whether the text node might be bindable. 5 | return { 'controlsDescendantBindings': true }; 6 | }, 7 | 'update': function (element, valueAccessor) { 8 | ko.utils.setTextContent(element, valueAccessor()); 9 | } 10 | }; 11 | ko.virtualElements.allowedBindings['text'] = true; 12 | -------------------------------------------------------------------------------- /spec/defaultBindings/uniqueNameBehaviors.js: -------------------------------------------------------------------------------- 1 | describe('Binding: Unique Name', function() { 2 | beforeEach(jasmine.prepareTestNode); 3 | 4 | it('Should apply a different name to each element', function () { 5 | testNode.innerHTML = "
"; 6 | ko.applyBindings({}, testNode); 7 | 8 | expect(testNode.childNodes[0].name.length > 0).toEqual(true); 9 | expect(testNode.childNodes[1].name.length > 0).toEqual(true); 10 | expect(testNode.childNodes[0].name == testNode.childNodes[1].name).toEqual(false); 11 | }); 12 | }); -------------------------------------------------------------------------------- /src/binding/defaultBindings/enableDisable.js: -------------------------------------------------------------------------------- 1 | ko.bindingHandlers['enable'] = { 2 | 'update': function (element, valueAccessor) { 3 | var value = ko.utils.unwrapObservable(valueAccessor()); 4 | if (value && element.disabled) 5 | element.removeAttribute("disabled"); 6 | else if ((!value) && (!element.disabled)) 7 | element.disabled = true; 8 | } 9 | }; 10 | 11 | ko.bindingHandlers['disable'] = { 12 | 'update': function (element, valueAccessor) { 13 | ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) }); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/google-closure-compiler-utils.js: -------------------------------------------------------------------------------- 1 | // Google Closure Compiler helpers (used only to make the minified file smaller) 2 | ko.exportSymbol = function(koPath, object) { 3 | var tokens = koPath.split("."); 4 | 5 | // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable) 6 | // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko) 7 | var target = ko; 8 | 9 | for (var i = 0; i < tokens.length - 1; i++) 10 | target = target[tokens[i]]; 11 | target[tokens[tokens.length - 1]] = object; 12 | }; 13 | ko.exportProperty = function(owner, publicName, object) { 14 | owner[publicName] = object; 15 | }; 16 | -------------------------------------------------------------------------------- /spec/defaultBindings/submitBehaviors.js: -------------------------------------------------------------------------------- 1 | describe('Binding: Submit', function() { 2 | beforeEach(jasmine.prepareTestNode); 3 | 4 | it('Should invoke the supplied function on submit and prevent default action, using model as \'this\' param and the form node as a param to the handler', function () { 5 | var firstParamStored; 6 | var model = { wasCalled: false, doCall: function (firstParam) { this.wasCalled = true; firstParamStored = firstParam; } }; 7 | testNode.innerHTML = ""; 8 | var formNode = testNode.childNodes[0]; 9 | ko.applyBindings(model, testNode); 10 | ko.utils.triggerEvent(testNode.childNodes[0], "submit"); 11 | expect(model.wasCalled).toEqual(true); 12 | expect(firstParamStored).toEqual(formNode); 13 | }); 14 | }); -------------------------------------------------------------------------------- /spec/defaultBindings/styleBehaviors.js: -------------------------------------------------------------------------------- 1 | describe('Binding: CSS style', function() { 2 | beforeEach(jasmine.prepareTestNode); 3 | 4 | it('Should give the element the specified CSS style value', function () { 5 | var myObservable = new ko.observable("red"); 6 | testNode.innerHTML = "