├── README.md ├── spec ├── lib │ ├── jasmine.extensions.css │ ├── jasmine.extensions.js │ ├── jasmine.css │ ├── jasmine-html.js │ ├── knockout-3.0.0.js │ └── jasmine.js ├── runner.html └── repeatBinding.js ├── index.html └── knockout-repeat.js /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mbest/knockout-repeat/HEAD/README.md -------------------------------------------------------------------------------- /spec/lib/jasmine.extensions.css: -------------------------------------------------------------------------------- 1 | #HTMLReporter { 2 | position: absolute; 3 | background: #EEE; 4 | } 5 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mbest/knockout-repeat @ GitHub 6 | 7 | 30 | 31 | 32 | 33 |
34 | 35 |

Repeat binding for Knockout

36 | 37 |
38 | Repeat binding for Knockout 39 |
40 | 41 |

Download

42 |

knockout-repeat.js

43 | 44 |

Dependencies

45 |

Knockout

46 | 47 |

License

48 |

MIT (http://www.opensource.org/licenses/mit-license.php)

49 | 50 |

Author

51 |

Michael Best

52 | 53 | 56 | 57 |
58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /spec/runner.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Knockout-repeat test runner 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /spec/lib/jasmine.extensions.js: -------------------------------------------------------------------------------- 1 | jasmine.Spec.prototype.restoreAfter = function(object, propertyName) { 2 | var originalValue = object[propertyName]; 3 | this.after(function() { 4 | object[propertyName] = originalValue; 5 | }); 6 | }; 7 | 8 | jasmine.Matchers.prototype.toEqualOneOf = function (expectedPossibilities) { 9 | for (var i = 0; i < expectedPossibilities.length; i++) { 10 | if (this.env.equals_(this.actual, expectedPossibilities[i])) { 11 | return true; 12 | } 13 | } 14 | return false; 15 | }; 16 | 17 | jasmine.Matchers.prototype.toContainHtml = function (expectedHtml) { 18 | var cleanedHtml = this.actual.innerHTML.toLowerCase().replace(/\r\n/g, ""); 19 | // IE < 9 strips whitespace immediately following comment nodes. Normalize by doing the same on all browsers. 20 | cleanedHtml = cleanedHtml.replace(/()\s*/g, "$1"); 21 | expectedHtml = expectedHtml.replace(/()\s*/g, "$1"); 22 | // Also remove __ko__ expando properties (for DOM data) - most browsers hide these anyway but IE < 9 includes them in innerHTML 23 | cleanedHtml = cleanedHtml.replace(/ __ko__\d+=\"(ko\d+|null)\"/g, ""); 24 | this.actual = cleanedHtml; // Fix explanatory message 25 | return cleanedHtml === expectedHtml; 26 | }; 27 | 28 | jasmine.nodeText = function(node) { 29 | return 'textContent' in node ? node.textContent : node.innerText; 30 | } 31 | 32 | jasmine.Matchers.prototype.toContainText = function (expectedText) { 33 | var actualText = jasmine.nodeText(this.actual); 34 | var cleanedActualText = actualText.replace(/\r\n/g, "\n"); 35 | this.actual = cleanedActualText; // Fix explanatory message 36 | return cleanedActualText === expectedText; 37 | }; 38 | 39 | jasmine.Matchers.prototype.toHaveOwnProperties = function (expectedProperties) { 40 | var ownProperties = []; 41 | for (var prop in this.actual) { 42 | if (this.actual.hasOwnProperty(prop)) { 43 | ownProperties.push(prop); 44 | } 45 | } 46 | return this.env.equals_(ownProperties, expectedProperties); 47 | }; 48 | 49 | jasmine.Matchers.prototype.toHaveTexts = function (expectedTexts) { 50 | var texts = ko.utils.arrayMap(this.actual.childNodes, jasmine.nodeText); 51 | this.actual = texts; // Fix explanatory message 52 | return this.env.equals_(texts, expectedTexts); 53 | }; 54 | 55 | jasmine.Matchers.prototype.toHaveValues = function (expectedValues) { 56 | var values = ko.utils.arrayMap(this.actual.childNodes, function (node) { return node.value; }); 57 | this.actual = values; // Fix explanatory message 58 | return this.env.equals_(values, expectedValues); 59 | }; 60 | 61 | jasmine.Matchers.prototype.toHaveSelectedValues = function (expectedValues) { 62 | var selectedNodes = ko.utils.arrayFilter(this.actual.childNodes, function (node) { return node.selected; }), 63 | selectedValues = ko.utils.arrayMap(selectedNodes, function (node) { return ko.selectExtensions.readValue(node); }); 64 | this.actual = selectedValues; // Fix explanatory message 65 | return this.env.equals_(selectedValues, expectedValues); 66 | }; 67 | 68 | jasmine.prepareTestNode = function() { 69 | // The bindings specs make frequent use of this utility function to set up 70 | // a clean new DOM node they can execute code against 71 | var existingNode = document.getElementById("testNode"); 72 | if (existingNode != null) 73 | existingNode.parentNode.removeChild(existingNode); 74 | testNode = document.createElement("div"); 75 | testNode.id = "testNode"; 76 | document.body.appendChild(testNode); 77 | }; 78 | -------------------------------------------------------------------------------- /spec/lib/jasmine.css: -------------------------------------------------------------------------------- 1 | body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } 2 | 3 | #HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } 4 | #HTMLReporter a { text-decoration: none; } 5 | #HTMLReporter a:hover { text-decoration: underline; } 6 | #HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } 7 | #HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } 8 | #HTMLReporter #jasmine_content { position: fixed; right: 100%; } 9 | #HTMLReporter .version { color: #aaaaaa; } 10 | #HTMLReporter .banner { margin-top: 14px; } 11 | #HTMLReporter .duration { color: #aaaaaa; float: right; } 12 | #HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } 13 | #HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } 14 | #HTMLReporter .symbolSummary li.passed { font-size: 14px; } 15 | #HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } 16 | #HTMLReporter .symbolSummary li.failed { line-height: 9px; } 17 | #HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } 18 | #HTMLReporter .symbolSummary li.skipped { font-size: 14px; } 19 | #HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } 20 | #HTMLReporter .symbolSummary li.pending { line-height: 11px; } 21 | #HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } 22 | #HTMLReporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } 23 | #HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } 24 | #HTMLReporter .runningAlert { background-color: #666666; } 25 | #HTMLReporter .skippedAlert { background-color: #aaaaaa; } 26 | #HTMLReporter .skippedAlert:first-child { background-color: #333333; } 27 | #HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } 28 | #HTMLReporter .passingAlert { background-color: #a6b779; } 29 | #HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } 30 | #HTMLReporter .failingAlert { background-color: #cf867e; } 31 | #HTMLReporter .failingAlert:first-child { background-color: #b03911; } 32 | #HTMLReporter .results { margin-top: 14px; } 33 | #HTMLReporter #details { display: none; } 34 | #HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } 35 | #HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } 36 | #HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } 37 | #HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } 38 | #HTMLReporter.showDetails .summary { display: none; } 39 | #HTMLReporter.showDetails #details { display: block; } 40 | #HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } 41 | #HTMLReporter .summary { margin-top: 14px; } 42 | #HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } 43 | #HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } 44 | #HTMLReporter .summary .specSummary.failed a { color: #b03911; } 45 | #HTMLReporter .description + .suite { margin-top: 0; } 46 | #HTMLReporter .suite { margin-top: 14px; } 47 | #HTMLReporter .suite a { color: #333333; } 48 | #HTMLReporter #details .specDetail { margin-bottom: 28px; } 49 | #HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } 50 | #HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } 51 | #HTMLReporter .resultMessage span.result { display: block; } 52 | #HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } 53 | 54 | #TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } 55 | #TrivialReporter a:visited, #TrivialReporter a { color: #303; } 56 | #TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } 57 | #TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } 58 | #TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } 59 | #TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } 60 | #TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } 61 | #TrivialReporter .runner.running { background-color: yellow; } 62 | #TrivialReporter .options { text-align: right; font-size: .8em; } 63 | #TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } 64 | #TrivialReporter .suite .suite { margin: 5px; } 65 | #TrivialReporter .suite.passed { background-color: #dfd; } 66 | #TrivialReporter .suite.failed { background-color: #fdd; } 67 | #TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } 68 | #TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } 69 | #TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } 70 | #TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } 71 | #TrivialReporter .spec.skipped { background-color: #bbb; } 72 | #TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } 73 | #TrivialReporter .passed { background-color: #cfc; display: none; } 74 | #TrivialReporter .failed { background-color: #fbb; } 75 | #TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } 76 | #TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } 77 | #TrivialReporter .resultMessage .mismatch { color: black; } 78 | #TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } 79 | #TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } 80 | #TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } 81 | #TrivialReporter #jasmine_content { position: fixed; right: 100%; } 82 | #TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } 83 | -------------------------------------------------------------------------------- /knockout-repeat.js: -------------------------------------------------------------------------------- 1 | // REPEAT binding for Knockout http://knockoutjs.com/ 2 | // (c) Michael Best 3 | // License: MIT (http://www.opensource.org/licenses/mit-license.php) 4 | // Version 2.1.0 5 | 6 | (function(factory) { 7 | if (typeof define === 'function' && define.amd) { 8 | // [1] AMD anonymous module 9 | define(['knockout'], factory); 10 | } else if (typeof exports === 'object') { 11 | // [2] commonJS 12 | factory(require('knockout')); 13 | } else { 14 | // [3] No module loader (plain