├── .gitignore ├── .jshintrc ├── koans ├── Answers │ ├── lesson1-observablestreams.js │ ├── lesson2-ComposableObservations.js │ ├── lesson3-Time.js │ ├── lesson4-Events.js │ └── lesson6-AdvancedStreams.js ├── issue.js ├── lesson1-observablestreams.js ├── lesson2-ComposableObservations.js ├── lesson3-Time.js ├── lesson4-Events.js └── lesson6-AdvancedStreams.js ├── lib ├── IxJs │ ├── .gitattributes │ ├── .gitignore │ ├── ixjs.js │ ├── l2o.js │ └── tests │ │ ├── L2O.html │ │ ├── aggregates.js │ │ ├── creation.js │ │ └── vendor │ │ ├── qunit-1.9.0.css │ │ ├── qunit-1.9.0.js │ │ └── qunit-clib.js ├── PermutationsAndShuffles.cs.linq ├── PermutationsAndShuffles.js ├── ixjs.js ├── jquery-1.5.1.js ├── koanutils.js ├── l2o.js ├── qunit.css ├── qunit.js ├── rx-vsdoc.js ├── rx.aggregates-vsdoc.js ├── rx.aggregates.js ├── rx.aggregates.min.js ├── rx.binding-vsdoc.js ├── rx.binding.js ├── rx.binding.min.js ├── rx.coincidence-vsdoc.js ├── rx.coincidence.js ├── rx.coincidence.min.js ├── rx.experimental-vsdoc.js ├── rx.experimental.js ├── rx.experimental.min.js ├── rx.jQuery.js ├── rx.joinpatterns-vsdoc.js ├── rx.joinpatterns.js ├── rx.joinpatterns.min.js ├── rx.joins.js ├── rx.js ├── rx.min.js ├── rx.node.js ├── rx.testing-vsdoc.js ├── rx.testing.js ├── rx.testing.min.js ├── rx.time-vsdoc.js ├── rx.time.js └── rx.time.min.js ├── readme └── rxkoans.html /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | #packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | *.py[co] 135 | 136 | # Packages 137 | *.egg 138 | *.egg-info 139 | dist 140 | build 141 | eggs 142 | parts 143 | bin 144 | var 145 | sdist 146 | develop-eggs 147 | .installed.cfg 148 | 149 | # Installer logs 150 | pip-log.txt 151 | 152 | # Unit test / coverage reports 153 | .coverage 154 | .tox 155 | 156 | #Translations 157 | *.mo 158 | 159 | #Mr Developer 160 | .mr.developer.cfg 161 | 162 | # Mac crap 163 | .DS_Store 164 | 165 | # Emacs junk 166 | *.*~ 167 | \#*\# 168 | .\#* 169 | *.log 170 | 171 | Doc/Decoding.synctex.gz 172 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": false, 18 | "strict": false, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "test": false, 23 | "equals": false, 24 | "Rx": false, 25 | "L2O": false, 26 | "Range": false 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /koans/Answers/lesson1-observablestreams.js: -------------------------------------------------------------------------------- 1 | module('Lesson 1 - Observable Streams'); 2 | 3 | /* 4 | * Step 1: find the 1st method that fails 5 | * Step 2: Fill in the blank ____ to make it pass 6 | * Step 3: run it again 7 | * Note: Do not change anything other than the blank 8 | */ 9 | 10 | test('ObjectsFirst', function () { 11 | var xs = L2O.Enumerable.fromArray([1, 2, 3]); 12 | equals(xs.first(), 1/*_______*/); 13 | 14 | var itsOk = L2O.Enumerable 15 | .returnValue(42); 16 | equals(itsOk.first(), 42 /*_______*/); 17 | }); 18 | 19 | test('SimpleSubscription', function() { 20 | Rx.Observable 21 | .returnValue(42) 22 | .subscribe(function(x) { equals(x, 42 /*_______*/); }); 23 | }); 24 | 25 | test('SimpleReturn', function() { 26 | var received = ''; 27 | Rx.Observable 28 | .returnValue('Foo') 29 | .subscribe(function(x) { received = x; }); 30 | equals(received, 'Foo'/*_______*/); 31 | }); 32 | 33 | test('TheLastEvent', function() { 34 | var received = ''; 35 | var numbers = ['Foo','Bar']; 36 | Rx.Observable 37 | .fromArray(numbers) 38 | .subscribe(function(x) { received = x; }); 39 | equals(received, 'Bar'/*_______*/); 40 | }); 41 | 42 | test('EveryThingCounts', function() { 43 | var received = 0; 44 | var numbers = [3, 4 ]; 45 | Rx.Observable 46 | .fromArray(numbers) 47 | .subscribe(function(x) { received += x; }); 48 | equals(received, 7/*_______*/); 49 | }); 50 | 51 | test('DoingInTheMiddle', function() { 52 | var status = []; 53 | var daysTillTest = Range.create(1, 4).reverse().toObservable(); 54 | daysTillTest 55 | .doAction( 56 | function(d) { status.push( 57 | d + '=' + (d === 1 ? 'Study Like Mad' : 'Party'/*_______*/)); 58 | }) 59 | .subscribe(); 60 | equals( 61 | status.toString(), 62 | '4=Party,3=Party,2=Party,1=Study Like Mad'); 63 | }); 64 | 65 | test('NothingListensUntilYouSubscribe', function() { 66 | var sum = 0; 67 | var numbers = Range.create(1,10).toObservable(); 68 | var observable = numbers 69 | .doAction(function(n) { sum += n; }); 70 | 71 | equals(0, sum); 72 | 73 | observable.subscribe/*_______*/(); 74 | 75 | equals(55, sum); 76 | }); 77 | -------------------------------------------------------------------------------- /koans/Answers/lesson2-ComposableObservations.js: -------------------------------------------------------------------------------- 1 | module('Lesson 2 - Composable Observations'); 2 | 3 | /* 4 | * Step 1: find the 1st method that fails 5 | * Step 2: Fill in the blank ____ to make it pass 6 | * Step 3: run it again 7 | * Note: Do not change anything other than the blank 8 | */ 9 | 10 | test('ComposableAddition', function() { 11 | var received = 0; 12 | var numbers = [10, 100, 1000/*_______*/]; 13 | numbers 14 | .toObservable() 15 | .sum() 16 | .subscribe(function(x) { received = x; }); 17 | equals(received, 1110); 18 | }); 19 | 20 | test('ComposeableBeforeAndAfter', function() { 21 | var names = Range.create(1, 6), 22 | a = '', 23 | b = ''; 24 | names 25 | .toObservable() 26 | .doAction(function(n) { a += n.toString(); }) 27 | .where(function(n) { return n % 2 === 0; }) 28 | .doAction(function(n) { b += n.toString(); }) 29 | .subscribe(); 30 | equals(a, '123456'/*_______*/); 31 | equals(b, '246'); 32 | }); 33 | 34 | test('WeWroteThis', function() { 35 | var received = []; 36 | var names = ['Bart', 'Wes', 'Erik', 'Matthew', 'Brian']; 37 | names 38 | .toObservable() 39 | .where(function(n) { return n.length <= 4/*_______*/; }) 40 | .subscribe(function(x) { received.push(x); }); 41 | equals(received.toString(), 'Bart,Wes,Erik'); 42 | }); 43 | 44 | test('ConvertingEvents', function() { 45 | var received = ''; 46 | var names = ['wE', 'hOpE', 'yOU', 'aRe', 'eNJoyIng', 'tHiS' ]; 47 | names 48 | .toObservable() 49 | .select(function(x) { return x.toLowerCase()/*_______*/; }) 50 | .subscribe(function(x) { received += x + ' '; }); 51 | equals(received, 'we hope you are enjoying this '); 52 | }); 53 | 54 | test('CreatingAMoreRelevantEventStream', function() { 55 | var received = '', 56 | mouseXMovements = [100, 200, 150], 57 | windowTopX = 50, 58 | relativemouse = mouseXMovements 59 | .toObservable() 60 | .select(function(x) { return x - windowTopX/*_______*/; }); 61 | 62 | relativemouse.subscribe(function(x) { received += x + ', '; }); 63 | equals(received, '50, 150, 100, '); 64 | }); 65 | 66 | test('CheckingEverything', function() { 67 | var received = null; 68 | var numbers = [ 2, 4, 6, 8 ]; 69 | numbers 70 | .toObservable() 71 | .all(function(x) { return x % 2 === 0; }) 72 | .subscribe(function(x) { received = x; }); 73 | equals(received, true/*_______*/); 74 | }); 75 | 76 | test('CompositionMeansTheSumIsGreaterThanTheParts', function() { 77 | var numbers = Rx.Observable.range(1, 10); 78 | numbers 79 | .where(function(x) { return x > 8/*_______*/; }) 80 | .sum() 81 | .subscribe(function(x) { equals(19, x); }); 82 | }); 83 | -------------------------------------------------------------------------------- /koans/Answers/lesson3-Time.js: -------------------------------------------------------------------------------- 1 | module('Lesson 3 - Time'); 2 | 3 | /* 4 | * Step 1: find the 1st method that fails 5 | * Step 2: Fill in the blank ____ to make it pass 6 | * Step 3: run it again 7 | * Note: Do not change anything other than the blank 8 | */ 9 | 10 | asyncTest('LaunchingAnActionInTheFuture', function() { 11 | var received = ''; 12 | var delay = 250/*_______*/; 13 | Rx 14 | .Scheduler 15 | .immediate 16 | .schedule(function() { received = 'Finished'; }, delay); 17 | 18 | setTimeout(function() { equals(received, 'Finished'); start(); }, 500); 19 | }); 20 | 21 | asyncTest('LaunchingAnEventInTheFuture', function() { 22 | var received = '', 23 | time = 250/*_______*/; 24 | 25 | Rx 26 | .Observable 27 | .returnValue('Godot', Rx.Scheduler.Immediate) 28 | .delay(time) 29 | .subscribe(function(x) { received = x; }); 30 | 31 | setTimeout(function() { equals(received, 'Godot'); start(); }, 500); 32 | }); 33 | 34 | asyncTest('AWatchedPot', function() { 35 | var received = '', 36 | delay = 500, 37 | timeout = 650/*_______*/, 38 | timeoutEvent = 39 | Rx .Observable 40 | .returnValue('Tepid'); 41 | 42 | Rx 43 | .Observable 44 | .returnValue('Boiling') 45 | .delay(delay) 46 | .timeout(timeout, timeoutEvent) 47 | .subscribe(function(x) { received = x; }); 48 | 49 | setTimeout(function() { equals(received, 'Boiling'); start(); }, 500); 50 | }); 51 | 52 | -------------------------------------------------------------------------------- /koans/Answers/lesson4-Events.js: -------------------------------------------------------------------------------- 1 | module('Lesson 4 - Events'); 2 | 3 | test('listening to events', function() { 4 | var received = ''; 5 | var subscription = 6 | $(document) 7 | .toObservable('foo') 8 | .subscribe(function(e) { received += e.payload; }); 9 | 10 | $(document).trigger({ type: 'foo', payload : 'M'}); 11 | $(document).trigger({ type: 'foo', payload : 'A'}); 12 | $(document).trigger({ type: 'foo', payload : 'T'}); 13 | subscription.dispose(); 14 | $(document).trigger({ type: 'foo', payload : 'T'}); 15 | 16 | equals(received, 'MAT'/*_______*/); 17 | }); 18 | 19 | test('listening to the right events', function() { 20 | var received = ''; 21 | var subscription = 22 | $(document) 23 | .toObservable('foo') 24 | .subscribe(function(e) { received += e.payload; }); 25 | 26 | $(document).trigger({ type: 'foo', payload : 'M'}); 27 | $(document).trigger({ type: 'bar', payload : 'A'}); 28 | $(document).trigger({ type: 'foo', payload : 'T'}); 29 | $(document).trigger({ type: 'foo', payload : 'T'}); 30 | subscription.dispose(); 31 | 32 | equals(received, 'MTT'/*_______*/); 33 | }); 34 | -------------------------------------------------------------------------------- /koans/Answers/lesson6-AdvancedStreams.js: -------------------------------------------------------------------------------- 1 | module('Lesson 6 - Advanced Streams'); 2 | 3 | test('Merging', function() { 4 | var easy = [], 5 | you = [1,2,3].toObservable(), 6 | me = ['A','B','C'].toObservable(); 7 | you 8 | .merge(me) 9 | .subscribe(function(a) { easy.push(a); }); 10 | 11 | // equals(easy === '1 A 2 B 3 C ' || easy === '1 2 3 A B C ', true/*_______*/); 12 | 13 | // Actually, this is not so easy! The result could be any arbitrary 14 | // riffle of the original two streams. More later on Riffles in JS. 15 | 16 | riffles([1, 2, 3].toEnumerable(), ['A', 'B', 'C'].toEnumerable()) 17 | .forEach(function(riffle) { 18 | console.log("riffle: ", riffle.toArray()); 19 | }); 20 | console.log("easy: ", easy); 21 | 22 | equals( riffles([1, 2, 3].toEnumerable(), ['A', 'B', 'C'].toEnumerable()) 23 | .select(function (riffle) {return riffle.toArray();}) 24 | .contains(easy, arrayComparer), 25 | true); 26 | 27 | 28 | }); 29 | 30 | // Given a 1-based index n, produces a function that will pluck the n-th 31 | // item from any Enumerable and return it. Pluck produces a function so 32 | // that it can be mapped over Enumerables of Enumerables, say to produce 33 | // a columnar slice from an array. WARNING: these are 1-based indices! 34 | var pluck = function (n) { 35 | return function(xs) { 36 | if (n <= 0 || n > xs.count) 37 | throw new Error('index out of range'); 38 | return xs.elementAt(n - 1); 39 | }; 40 | }; 41 | 42 | // Given a 1-based index n, produces a function that will produce an 43 | // Enumerable with the n-th item missing. WARNING: these are 1-based 44 | // indices! 45 | var coPluck = function(n) { 46 | return function(xs) { 47 | 48 | // is the following error-checking redundant? The error-handling 49 | // policy of Ix is not clear to me at this point! (4 Nov 12) 50 | 51 | // if (! (xs instanceof Ix.Enumerable) ) 52 | // throw new Error('xs must be an Ix.Enumerable'); 53 | var c = xs.count(); 54 | if (n <= 0 || n > c) 55 | throw new Error('index out of range'); 56 | var ys = []; 57 | var i = 1; 58 | xs.forEach( function (x) { 59 | if (i != n) 60 | ys.push(x); 61 | i++; 62 | }); 63 | return ys.toEnumerable(); 64 | }; 65 | }; 66 | 67 | // A function that compares arrays for equality given an optional 68 | // elementComparer. Be aware that this is not sufficiently flexible 69 | // to work on arrayw of arbitrary nesting. 70 | var arrayComparer = function (xs, ys, elementComparer) { 71 | if ( (! (xs instanceof Array)) || (! (ys instanceof Array)) ) 72 | return false; 73 | var xl = xs.length; 74 | var yl = ys.length; 75 | if (xl != yl) 76 | return false; 77 | elementComparer || (elementComparer = function(x, y) { return x === y; }); 78 | var i; 79 | for (i = 0; i < xl; i++) 80 | if (! elementComparer(xs[i], ys[i])) 81 | return false; 82 | return true; 83 | }; 84 | 85 | // Produces an Enumerable of all splits of another Enumerable, as an 86 | // Enumerable of pairs of left and right after the splits. A more 87 | // sophisticated implementation would build the nested enumerators. 88 | var splits = function(xs) { 89 | var c = xs.count(); 90 | var ys = []; 91 | for (var i = 0; i <= c; i++) 92 | // ys.push( [xs.take(i), xs.skip(i)].toEnumerable() ); 93 | ys.push( [xs.take(i).toArray().slice(0).toEnumerable(), 94 | xs.skip(i).toArray().slice(0).toEnumerable()] 95 | .toEnumerable() ); 96 | return ys.toEnumerable(); 97 | }; 98 | 99 | // Produces an Enumerable of all riffles of two other Enumerables. A more 100 | // sophisticated implementation would build the nested enumerators. 101 | var riffles = function(left, right) { 102 | if (left.count() === 0) 103 | return Ix.Enumerable.returnValue(right); 104 | if (right.count() === 0) 105 | return Ix.Enumerable.returnValue(left); 106 | var ys = []; 107 | 108 | splits(right).skip(1).take(1).forEach( function(r) 109 | { splits(left).forEach( function(l) 110 | { riffles(l.elementAt(1), r.elementAt(1)).forEach( function(f) 111 | { ys.push(l.first().concat(r.first()).concat(f)); 112 | }); }); }); 113 | 114 | return ys.toEnumerable(); 115 | }; 116 | 117 | test('Riffles', function() { 118 | var e = [1, 2, 3].toEnumerable(); 119 | 120 | equals(e.contains(2), true); 121 | equals(e.count(), 3); 122 | equals([[1, 2], [3, 4]].toEnumerable().contains([3, 4], arrayComparer), true); 123 | 124 | e.forEach(function (x) { equals( pluck(x)(e), x ); }); 125 | 126 | // Expecting exceptions to be thrown 127 | try { pluck(0)(e); equals(false, true); } 128 | catch (exception) { equals(true, true); } 129 | 130 | try { pluck(4)(e); equals(false, true); } 131 | catch (exception) { equals(true, true); } 132 | 133 | try { pluck(-2)(e); equals(false, true); } 134 | catch (exception) { equals(true, true); } 135 | 136 | equals( arrayComparer(coPluck(2)(e).toArray(), [1, 3]), true ); 137 | 138 | equals( arrayComparer(riffles(e, Ix.Enumerable.empty()).first().toArray(), 139 | e.toArray()), true); 140 | equals( arrayComparer(riffles(Ix.Enumerable.empty(), e).first().toArray(), 141 | e.toArray()), true); 142 | equals( riffles([1,2,3].toEnumerable(), [4,5,6].toEnumerable()) 143 | .select(function (riffle) {return riffle.toArray();}) 144 | .contains([1,2,4,5,3,6], arrayComparer), 145 | true); 146 | }); 147 | 148 | var floatingEquals = function (a, b, digits) { 149 | var exponent = Math.abs( digits || 12 ); 150 | var multiplier = Math.pow(10, exponent); 151 | return Math.round( multiplier * a ) === Math.round( multiplier * b); 152 | }; 153 | 154 | test('DescriptiveStatistics', function () { 155 | var e = [1, 2, 3].toEnumerable(); 156 | equals(e.standardDeviation(), 1); 157 | 158 | equals(floatingEquals( 159 | [1, 2].toEnumerable().standardDeviation(), 160 | 1 / Math.sqrt(2)), true); 161 | 162 | // Should be sqrt ( (1^2 + 2^2 + 4^2 - 7^2 / 3) / 2 ) 163 | // = sqrt( (1 + 4 + 16 - 49 / 3) / 2 ) 164 | // = sqrt( (21 - 49 / 3) / 2 ) 165 | // = sqrt( (63 - 49) / 6 ) 166 | // = sqrt( 14 / 6 ) 167 | // = sqrt( 7 / 3 ) 168 | 169 | equals(floatingEquals( 170 | [1, 2, 4] 171 | .toEnumerable() 172 | .standardDeviation(), 173 | Math.sqrt(7 / 3)), true); 174 | 175 | [1, 2, 4] 176 | .toObservable() 177 | .standardDeviation() 178 | .subscribe(function (s) { 179 | console.log(s); 180 | equals(floatingEquals(s, Math.sqrt(7 / 3)), true); }); 181 | }); 182 | 183 | 184 | test('Splitting Up', function() { 185 | var oddsAndEvens = ['','']; 186 | numbers = Rx.Observable.range(1, 9), 187 | split = numbers 188 | .groupBy(function(n) { return n % 2 /*_______*/; }); 189 | 190 | split.subscribe(function (g) { 191 | return g.subscribe( 192 | function (i) { 193 | return console.log(i, g.key); 194 | }); 195 | }); 196 | 197 | split 198 | .subscribe(function(group) { 199 | group 200 | .subscribe(function(n) { oddsAndEvens[group.key] += n; }); 201 | }); 202 | 203 | var evens = oddsAndEvens[0], 204 | odds = oddsAndEvens[1]; 205 | 206 | equals(evens, '2468'); 207 | equal(odds, '13579'); 208 | }); 209 | 210 | 211 | test('Subscribe Imediately When Splitting', function() { 212 | var averages = [0.0,0.0], 213 | numbers = [22,22,99,22,101,22].toObservable(), 214 | split = numbers 215 | .groupBy(function(n) { return n % 2; }); 216 | split 217 | .subscribe(function(g) { 218 | g 219 | .average() 220 | .subscribe/*_______*/(function(a) { averages[g.key] = a; }); 221 | }); 222 | equals(22, averages[0]); 223 | equals(100, averages[1]); 224 | }); 225 | 226 | test('Multiple Subscriptions', function() { 227 | var numbers = new Rx.Subject(), 228 | sum = 0, 229 | average = 0; 230 | 231 | numbers 232 | .sum() 233 | .subscribe(function(n) { sum = n; }); 234 | numbers.onNext(1); 235 | numbers.onNext(1); 236 | numbers.onNext(1); 237 | numbers.onNext(1); 238 | numbers.onNext(1); 239 | 240 | numbers 241 | .average() 242 | .subscribe(function(n) { 243 | average = n; 244 | // Bug, not called? 245 | }); 246 | numbers.onNext(2); 247 | numbers.onNext(2); 248 | numbers.onNext(2); 249 | numbers.onNext(2); 250 | numbers.onNext(2); 251 | 252 | numbers.onCompleted(); 253 | 254 | equals(sum, 15); 255 | equals(average, 2/*_______*/); 256 | }); 257 | 258 | 259 | -------------------------------------------------------------------------------- /koans/issue.js: -------------------------------------------------------------------------------- 1 | module('issue -- possible bug for investigation'); 2 | 3 | // Produces an Enumerable of all splits of another Enumerable, as an 4 | // Enumerable of pairs of left and right after the splits. A more 5 | // sophisticated implementation would build the nested enumerators. 6 | var splits = function(xs) { 7 | var c = xs.count(); 8 | var ys = []; 9 | for (var i = 0; i <= c; i++) 10 | // ys.push( [xs.take(i), xs.skip(i)].toEnumerable() ); 11 | ys.push( [xs.take(i).toArray().slice(0).toEnumerable(), 12 | xs.skip(i).toArray().slice(0).toEnumerable()] 13 | .toEnumerable() ); 14 | return ys.toEnumerable(); 15 | }; 16 | 17 | // Produces an Enumerable of all riffles of two other Enumerables. A more 18 | // sophisticated implementation would build the nested enumerators. 19 | var riffles = function(left, right) { 20 | if (left.count() === 0) 21 | return Ix.Enumerable.returnValue(right); 22 | if (right.count() === 0) 23 | return Ix.Enumerable.returnValue(left); 24 | var ys = []; 25 | 26 | splits(right).skip(1).take(1).forEach( function(r) 27 | { splits(left).forEach( function(l) 28 | { riffles(l.elementAt(1), r.elementAt(1)).forEach( function(f) 29 | { ys.push(l.first().concat(r.first()).concat(f)); 30 | }); }); }); 31 | 32 | return ys.toEnumerable(); 33 | }; 34 | 35 | // A function that compares arrays for equality given an optional 36 | // elementComparer. Be aware that this is not sufficiently flexible 37 | // to work on arrayw of arbitrary nesting. 38 | var arrayComparer = function (xs, ys, elementComparer) { 39 | if ( (! (xs instanceof Array)) || (! (ys instanceof Array)) ) 40 | return false; 41 | var xl = xs.length; 42 | var yl = ys.length; 43 | if (xl != yl) 44 | return false; 45 | elementComparer || (elementComparer = function(x, y) { return x === y; }); 46 | var i; 47 | for (i = 0; i < xl; i++) 48 | if (! elementComparer(xs[i], ys[i])) 49 | return false; 50 | return true; 51 | }; 52 | 53 | test('Riffles', function() { 54 | // splits([1, 2, 3].toEnumerable()).forEach( function(s) { 55 | // console.log("split: ", 56 | // s.toArray().slice(0)[0].toArray().slice(0), 57 | // s.toArray().slice(0)[1].toArray().slice(0)); 58 | // }); 59 | var e = [1, 2, 3].toEnumerable(); 60 | // riffles([].toEnumerable(), [].toEnumerable()) 61 | // .forEach(function(riffle) { 62 | // console.log("riffle: ", riffle.toArray()); 63 | // }); 64 | // riffles([1].toEnumerable(), [].toEnumerable()) 65 | // .forEach(function(riffle) { 66 | // console.log("riffle: ", riffle.toArray()); 67 | // }); 68 | // riffles([].toEnumerable(), [4].toEnumerable()) 69 | // .forEach(function(riffle) { 70 | // console.log("riffle: ", riffle.toArray()); 71 | // }); 72 | // riffles([1].toEnumerable(), [4].toEnumerable()) 73 | // .forEach(function(riffle) { 74 | // console.log("riffle: ", riffle.toArray()); 75 | // }); 76 | riffles([1, 2, 3].toEnumerable(), [4, 5, 6].toEnumerable()) 77 | .forEach(function(riffle) { 78 | console.log("riffle: ", riffle.toArray()); 79 | }); 80 | 81 | //riffles([1, 2, 3].toEnumerable(), [4].toEnumerable()); 82 | 83 | equals( arrayComparer(riffles(e, Ix.Enumerable.empty()).first().toArray(), 84 | e.toArray()), true); 85 | equals( arrayComparer(riffles(Ix.Enumerable.empty(), e).first().toArray(), 86 | e.toArray()), true); 87 | equals( riffles([1,2,3].toEnumerable(), [4,5,6].toEnumerable()) 88 | .select(function (riffle) {return riffle.toArray();}) 89 | .contains([1,2,4,5,3,6], arrayComparer), 90 | true); 91 | }); 92 | 93 | -------------------------------------------------------------------------------- /koans/lesson1-observablestreams.js: -------------------------------------------------------------------------------- 1 | module('Lesson 1 - Observable Streams'); 2 | 3 | /* 4 | * Step 1: find the 1st method that fails 5 | * Step 2: Fill in the blank ____ to make it pass 6 | * Step 3: run it again 7 | * Note: Do not change anything other than the blank 8 | */ 9 | 10 | test('ObjectsFirst', function () { 11 | var xs = L2O.Enumerable.fromArray([1, 2, 3]); 12 | equals(xs.first(), _______); 13 | 14 | var itsOk = L2O.Enumerable 15 | .returnValue(42); 16 | equals(itsOk.first(), _______); 17 | }); 18 | 19 | test('SimpleSubscription', function() { 20 | Rx.Observable 21 | .returnValue(42) 22 | .subscribe(function(x) { equals(x, _______); }); 23 | }); 24 | 25 | test('SimpleReturn', function() { 26 | var received = ''; 27 | Rx.Observable 28 | .returnValue('Foo') 29 | .subscribe(function(x) { received = x; }); 30 | equals(received, _______); 31 | }); 32 | 33 | test('TheLastEvent', function() { 34 | var received = ''; 35 | var numbers = ['Foo','Bar']; 36 | Rx.Observable 37 | .fromArray(numbers) 38 | .subscribe(function(x) { received = x; }); 39 | equals(received, _______); 40 | }); 41 | 42 | test('EveryThingCounts', function() { 43 | var received = 0; 44 | var numbers = [3, 4 ]; 45 | Rx.Observable 46 | .fromArray(numbers) 47 | .subscribe(function(x) { received += x; }); 48 | equals(received, _______); 49 | }); 50 | 51 | test('DoingInTheMiddle', function() { 52 | var status = []; 53 | var daysTillTest = Range.create(1, 4).reverse().toObservable(); 54 | daysTillTest 55 | .doAction( 56 | function(d) { status.push( 57 | d + '=' + (d === 1 ? 'Study Like Mad' : _______)); 58 | }) 59 | .subscribe(); 60 | equals( 61 | status.toString(), 62 | '4=Party,3=Party,2=Party,1=Study Like Mad'); 63 | }); 64 | 65 | test('NothingListensUntilYouSubscribe', function() { 66 | var sum = 0; 67 | var numbers = Range.create(1,10).toObservable(); 68 | var observable = numbers 69 | .doAction(function(n) { sum += n; }); 70 | 71 | equals(0, sum); 72 | 73 | observable._______(); 74 | 75 | equals(55, sum); 76 | }); 77 | -------------------------------------------------------------------------------- /koans/lesson2-ComposableObservations.js: -------------------------------------------------------------------------------- 1 | module('Lesson 2 - Composable Observations'); 2 | 3 | /* 4 | * Step 1: find the 1st method that fails 5 | * Step 2: Fill in the blank ____ to make it pass 6 | * Step 3: run it again 7 | * Note: Do not change anything other than the blank 8 | */ 9 | 10 | test('ComposableAddition', function() { 11 | var received = 0; 12 | var numbers = [10, 100, _______]; 13 | numbers 14 | .toObservable() 15 | .sum() 16 | .subscribe(function(x) { received = x; }); 17 | equals(received, 1110); 18 | }); 19 | 20 | test('ComposeableBeforeAndAfter', function() { 21 | var names = Range.create(1, 6), 22 | a = '', 23 | b = ''; 24 | names 25 | .toObservable() 26 | .doAction(function(n) { a += n.toString(); }) 27 | .where(function(n) { return n % 2 === 0; }) 28 | .doAction(function(n) { b += n.toString(); }) 29 | .subscribe(); 30 | equals(a, _______); 31 | equals(b, '246'); 32 | }); 33 | 34 | test('WeWroteThis', function() { 35 | var received = []; 36 | var names = ['Bart', 'Wes', 'Erik', 'Matthew', 'Brian']; 37 | names 38 | .toObservable() 39 | .where(function(n) { return n.length <= _______; }) 40 | .subscribe(function(x) { received.push(x); }); 41 | equals(received.toString(), 'Bart,Wes,Erik'); 42 | }); 43 | 44 | test('ConvertingEvents', function() { 45 | var received = ''; 46 | var names = ['wE', 'hOpE', 'yOU', 'aRe', 'eNJoyIng', 'tHiS' ]; 47 | names 48 | .toObservable() 49 | .select(function(x) { return x._______; }) 50 | .subscribe(function(x) { received += x + ' '; }); 51 | equals(received, 'we hope you are enjoying this '); 52 | }); 53 | 54 | test('CreatingAMoreRelevantEventStream', function() { 55 | var received = '', 56 | mouseXMovements = [100, 200, 150], 57 | windowTopX = 50, 58 | relativemouse = mouseXMovements 59 | .toObservable() 60 | .select(function(x) { return x - _______; }); 61 | 62 | relativemouse.subscribe(function(x) { received += x + ', '; }); 63 | equals(received, '50, 150, 100, '); 64 | }); 65 | 66 | test('CheckingEverything', function() { 67 | var received = null; 68 | var numbers = [ 2, 4, 6, 8 ]; 69 | numbers 70 | .toObservable() 71 | .all(function(x) { return x % 2 === 0; }) 72 | .subscribe(function(x) { received = x; }); 73 | equals(received, _______); 74 | }); 75 | 76 | test('CompositionMeansTheSumIsGreaterThanTheParts', function() { 77 | var numbers = Rx.Observable.range(1, 10); 78 | numbers 79 | .where(function(x) { return x > _______; }) 80 | .sum() 81 | .subscribe(function(x) { equals(19, x); }); 82 | }); 83 | -------------------------------------------------------------------------------- /koans/lesson3-Time.js: -------------------------------------------------------------------------------- 1 | module('Lesson 3 - Time'); 2 | 3 | /* 4 | * Step 1: find the 1st method that fails 5 | * Step 2: Fill in the blank ____ to make it pass 6 | * Step 3: run it again 7 | * Note: Do not change anything other than the blank 8 | */ 9 | 10 | asyncTest('LaunchingAnActionInTheFuture', function() { 11 | var received = ''; 12 | var delay = _______; 13 | Rx 14 | .Scheduler 15 | .immediate 16 | .schedule(function() { received = 'Finished'; }, delay); 17 | 18 | setTimeout(function() { equals(received, 'Finished'); start(); }, 500); 19 | }); 20 | 21 | asyncTest('LaunchingAnEventInTheFuture', function() { 22 | var received = '', 23 | time = _______; 24 | 25 | Rx 26 | .Observable 27 | .returnValue('Godot', Rx.Scheduler.Immediate) 28 | .delay(time) 29 | .subscribe(function(x) { received = x; }); 30 | 31 | setTimeout(function() { equals(received, 'Godot'); start(); }, 500); 32 | }); 33 | 34 | asyncTest('AWatchedPot', function() { 35 | var received = '', 36 | delay = 500, 37 | timeout = _______, 38 | timeoutEvent = 39 | Rx .Observable 40 | .returnValue('Tepid'); 41 | Rx 42 | .Observable 43 | .returnValue('Boiling') 44 | .delay(delay) 45 | .timeout(timeout, timeoutEvent) 46 | .subscribe(function(x) { received = x; }); 47 | 48 | setTimeout(function() { equals(received, 'Boiling'); start(); }, 500); 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /koans/lesson4-Events.js: -------------------------------------------------------------------------------- 1 | module('Lesson 4 - Events'); 2 | 3 | test('listening to events', function() { 4 | var received = ''; 5 | var subscription = 6 | $(document) 7 | .toObservable('foo') 8 | .subscribe(function(e) { received += e.payload; }); 9 | 10 | $(document).trigger({ type: 'foo', payload : 'M'}); 11 | $(document).trigger({ type: 'foo', payload : 'A'}); 12 | $(document).trigger({ type: 'foo', payload : 'T'}); 13 | subscription.dispose(); 14 | $(document).trigger({ type: 'foo', payload : 'T'}); 15 | 16 | equals(received, 'MAT'/*_______*/); 17 | }); 18 | 19 | test('listening to the right events', function() { 20 | var received = ''; 21 | var subscription = 22 | $(document) 23 | .toObservable('foo') 24 | .subscribe(function(e) { received += e.payload; }); 25 | 26 | $(document).trigger({ type: 'foo', payload : 'M'}); 27 | $(document).trigger({ type: 'bar', payload : 'A'}); 28 | $(document).trigger({ type: 'foo', payload : 'T'}); 29 | $(document).trigger({ type: 'foo', payload : 'T'}); 30 | subscription.dispose(); 31 | 32 | equals(received, 'MTT'/*_______*/); 33 | }); 34 | -------------------------------------------------------------------------------- /koans/lesson6-AdvancedStreams.js: -------------------------------------------------------------------------------- 1 | module('Lesson 6 - Advanced Streams'); 2 | 3 | test('Merging', function() { 4 | var easy = [], 5 | you = [1,2,3].toObservable(), 6 | me = ['A','B','C'].toObservable(); 7 | you 8 | .merge(me) 9 | .subscribe(function(a) { easy.push(a); }); 10 | 11 | // equals(easy === '1 A 2 B 3 C ' || easy === '1 2 3 A B C ', _______); 12 | 13 | // Actually, this is not so easy! The result could be any arbitrary 14 | // riffle of the original two streams. 15 | 16 | riffles([1, 2, 3].toEnumerable(), ['A', 'B', 'C'].toEnumerable()) 17 | .forEach(function(riffle) { 18 | console.log("riffle: ", riffle.toArray()); 19 | }); 20 | 21 | console.log("easy: ", easy); 22 | 23 | equals( 24 | riffles([1, 2, 3].toEnumerable(), ['A', 'B', 'C'].toEnumerable()) 25 | .select(function (riffle) {return riffle.toArray();}) 26 | ._______(easy, arrayComparer), 27 | true); 28 | }); 29 | 30 | // A function that compares arrays for equality given an optional 31 | // elementComparer. Be aware that this is not sufficiently flexible 32 | // to work on arrays of arbitrary nesting. 33 | var arrayComparer = function (xs, ys, elementComparer) { 34 | if ( (! (xs instanceof Array)) || (! (ys instanceof Array)) ) 35 | return false; 36 | var xl = xs.length; 37 | var yl = ys.length; 38 | if (xl != yl) 39 | return false; 40 | elementComparer || (elementComparer = function(x, y) { return x === y; }); 41 | var i; 42 | for (i = 0; i < xl; i++) 43 | if (! elementComparer(xs[i], ys[i])) 44 | return false; 45 | return true; 46 | }; 47 | 48 | // Produces an Enumerable of all splits of another Enumerable, as an 49 | // Enumerable of pairs of left and right after the splits. A more 50 | // sophisticated implementation would build the nested enumerators. 51 | var splits = function(xs) { 52 | var c = xs.count(); 53 | var ys = []; 54 | for (var i = 0; i <= c; i++) 55 | ys.push( [xs.take(i), xs.skip(i)].toEnumerable() ); 56 | return ys.toEnumerable(); 57 | }; 58 | 59 | // Produces an Enumerable of all riffles of two other Enumerables. A more 60 | // sophisticated implementation would build the nested enumerators. 61 | var riffles = function(left, right) { 62 | if (left.count() === 0) 63 | return Ix.Enumerable.returnValue(right); 64 | if (right.count() === 0) 65 | return Ix.Enumerable.returnValue(left); 66 | var ys = []; 67 | 68 | splits(right).skip(1).take(1).forEach( function(r) 69 | { splits(left).forEach( function(l) 70 | { riffles(l.elementAt(1), r.elementAt(1)).forEach( function(f) 71 | { ys.push(l.first().concat(r.first()).concat(f)); 72 | }); }); }); 73 | 74 | return ys.toEnumerable(); 75 | }; 76 | 77 | var floatingEquals = function (a, b, digits) { 78 | var exponent = Math.abs( digits || 12 ); 79 | var multiplier = Math.pow(10, exponent); 80 | return Math.round( multiplier * a ) === Math.round( multiplier * b); 81 | }; 82 | 83 | test('DescriptiveStatistics', function () { 84 | var e = [1, 2, 3].toEnumerable(); 85 | equals(e.standardDeviation(), _______); 86 | 87 | equals(floatingEquals( 88 | [1, 2].toEnumerable().standardDeviation(), 89 | 1 / Math.sqrt(2)), true); 90 | 91 | // Should be sqrt ( (1^2 + 2^2 + 4^2 - 7^2 / 3) / 2 ) 92 | // = sqrt( (1 + 4 + 16 - 49 / 3) / 2 ) 93 | // = sqrt( (21 - 49 / 3) / 2 ) 94 | // = sqrt( (63 - 49) / 6 ) 95 | // = sqrt( 14 / 6 ) 96 | // = sqrt( 7 / 3 ) 97 | 98 | equals(floatingEquals( 99 | [1, 2, 4] 100 | .toEnumerable() 101 | .standardDeviation(), 102 | Math.sqrt(7 / 3)), true); 103 | 104 | [1, 2, 4] 105 | .toObservable() 106 | .standardDeviation() 107 | .subscribe(function (s) { 108 | console.log(s); 109 | equals(floatingEquals(s, Math.sqrt(7 / 3)), true); }); 110 | }); 111 | 112 | 113 | test('Splitting Up', function() { 114 | var oddsAndEvens = ['','']; 115 | numbers = Rx.Observable.range(1, 9), 116 | split = numbers 117 | .groupBy(function(n) { return n % _______; }); 118 | 119 | split.subscribe(function (g) { 120 | return g.subscribe( 121 | function (i) { 122 | return console.log(i, g.key, g); 123 | }); 124 | }); 125 | 126 | split 127 | .subscribe(function(group) { 128 | group 129 | .subscribe(function(n) { oddsAndEvens[group.key] += n; }); 130 | }); 131 | 132 | var evens = oddsAndEvens[0], 133 | odds = oddsAndEvens[1]; 134 | 135 | equals(evens, '2468'); 136 | equal(odds, '13579'); 137 | }); 138 | 139 | 140 | test('Subscribe Imediately When Splitting', function() { 141 | var averages = [0.0,0.0], 142 | numbers = [22,22,99,22,101,22].toObservable(), 143 | split = numbers 144 | .groupBy(function(n) { return n % 2; }); 145 | split 146 | .subscribe(function(g) { 147 | g 148 | .average() 149 | ._______(function(a) { averages[g.key] = a; }); 150 | }); 151 | equals(22, averages[0]); 152 | equals(100, averages[1]); 153 | }); 154 | 155 | test('Multiple Subscriptions', function() { 156 | var numbers = new Rx.Subject(), 157 | sum = 0, 158 | average = 0; 159 | 160 | numbers 161 | .sum() 162 | .subscribe(function(n) { sum = n; }); 163 | numbers.onNext(1); 164 | numbers.onNext(1); 165 | numbers.onNext(1); 166 | numbers.onNext(1); 167 | numbers.onNext(1); 168 | 169 | numbers 170 | .average() 171 | .subscribe(function(n) { 172 | average = n; 173 | // Bug, not called? 174 | }); 175 | numbers.onNext(2); 176 | numbers.onNext(2); 177 | numbers.onNext(2); 178 | numbers.onNext(2); 179 | numbers.onNext(2); 180 | 181 | numbers.onCompleted(); 182 | 183 | equals(sum, 15); 184 | equals(average, _______); 185 | }); 186 | 187 | test('Tally', function() 188 | { 189 | var alice = [ 190 | "Alice", "was", "beginning", "to", "get", "very", "tired", "of", "sitting", "by", "her", "sister", "on", "the", 191 | "bank", "and", "of", "having", "nothing", "to", "do", "once", "or", "twice", "she", "had", "peeped", "into", "the", 192 | "book", "her", "sister", "was", "reading", "but", "it", "had", "no", "pictures", "or", "conversations", "in", 193 | "it", "and", "what", "is", "the", "use", "of", "a", "book", "thought", "Alice", "without", "pictures", "or", 194 | "conversation", 195 | "So", "she", "was", "considering", "in", "her", "own", "mind", "as", "well", "as", "she", "could", "for", "the", 196 | "hot", "day", "made", "her", "feel", "very", "sleepy", "and", "stupid", "whether", "the", "pleasure", 197 | "of", "making", "a", "daisy", "chain", "would", "be", "worth", "the", "trouble", "of", "getting", "up", "and", 198 | "picking", "the", "daisies", "when", "suddenly", "a", "White", "Rabbit", "with", "pink", "eyes", "ran", 199 | "close", "by", "her", 200 | 201 | "There", "was", "nothing", "so", "VERY", "remarkable", "in", "that", "nor", "did", "Alice", "think", "it", "so", 202 | "VERY", "much", "out", "of", "the", "way", "to", "hear", "the", "Rabbit", "say", "to", "itself", "Oh", "dear", 203 | "Oh", "dear", "I", "shall", "be", "late", "when", "she", "thought", "it", "over", "afterwards", "it", 204 | "occurred", "to", "her", "that", "she", "ought", "to", "have", "wondered", "at", "this", "but", "at", "the", "time", 205 | "it", "all", "seemed", "quite", "natural", "but", "when", "the", "Rabbit", "actually", "TOOK", "A", "WATCH", 206 | "OUT", "OF", "ITS", "WAISTCOAT", "POCKET", "and", "looked", "at", "it", "and", "then", "hurried", "on", 207 | "Alice", "started", "to", "her", "feet", "for", "it", "flashed", "across", "her", "mind", "that", "she", "had", 208 | "never", "before", "seen", "a", "rabbit", "with", "either", "a", "waistcoat", "pocket", "or", "a", "watch", 209 | "to", "take", "out", "of", "it", "and", "burning", "with", "curiosity", "she", "ran", "across", "the", "field", 210 | "after", "it", "and", "fortunately", "was", "just", "in", "time", "to", "see", "it", "pop", "down", "a", "large", 211 | "rabbit", "hole", "under", "the", "hedge", 212 | 213 | "In", "another", "moment", "down", "went", "Alice", "after", "it", "never", "once", "considering", "how", 214 | "in", "the", "world", "she", "was", "to", "get", "out", "again", 215 | 216 | "The", "rabbit", "hole", "went", "straight", "on", "like", "a", "tunnel", "for", "some", "way", "and", "then", 217 | "dipped", "suddenly", "down", "so", "suddenly", "that", "Alice", "had", "not", "a", "moment", "to", "think", 218 | "about", "stopping", "herself", "before", "she", "found", "herself", "falling", "down", "a", "very", "deep", 219 | "well", 220 | 221 | "Either", "the", "well", "was", "very", "deep", "or", "she", "fell", "very", "slowly", "for", "she", "had", 222 | "plenty", "of", "time", "as", "she", "went", "down", "to", "look", "about", "her", "and", "to", "wonder", "what", "was", 223 | "going", "to", "happen", "next", "First", "she", "tried", "to", "look", "down", "and", "make", "out", "what", 224 | "she", "was", "coming", "to", "but", "it", "was", "too", "dark", "to", "see", "anything", "then", "she", 225 | "looked", "at", "the", "sides", "of", "the", "well", "and", "noticed", "that", "they", "were", "filled", "with", 226 | "cupboards", "and", "book", "shelves", "here", "and", "there", "she", "saw", "maps", "and", "pictures", 227 | "hung", "upon", "pegs", "She", "took", "down", "a", "jar", "from", "one", "of", "the", "shelves", "as", 228 | "she", "passed", "it", "was", "labelled", "ORANGE", "MARMALADE", "but", "to", "her", "great", 229 | "disappointment", "it", "was", "empty", "she", "did", "not", "like", "to", "drop", "the", "jar", "for", "fear", 230 | "of", "killing", "somebody", "so", "managed", "to", "put", "it", "into", "one", "of", "the", "cupboards", "as", 231 | "she", "fell", "past", "it" 232 | ]; 233 | 234 | alice 235 | .toObservable() 236 | .select(function(s) {return s.toLowerCase();}) 237 | .tally() 238 | .subscribe(function (s) {console.log(s);}) 239 | ; 240 | }); 241 | 242 | 243 | -------------------------------------------------------------------------------- /lib/IxJs/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /lib/IxJs/.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | #packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | *.py[co] 135 | 136 | # Packages 137 | *.egg 138 | *.egg-info 139 | dist 140 | build 141 | eggs 142 | parts 143 | bin 144 | var 145 | sdist 146 | develop-eggs 147 | .installed.cfg 148 | 149 | # Installer logs 150 | pip-log.txt 151 | 152 | # Unit test / coverage reports 153 | .coverage 154 | .tox 155 | 156 | #Translations 157 | *.mo 158 | 159 | #Mr Developer 160 | .mr.developer.cfg 161 | 162 | # Mac crap 163 | .DS_Store 164 | -------------------------------------------------------------------------------- /lib/IxJs/tests/L2O.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Enumerable Extensions for JavaScript Tests 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /lib/IxJs/tests/aggregates.js: -------------------------------------------------------------------------------- 1 | (function (window) { 2 | 3 | function identity (x) { return x; } 4 | 5 | var root = window.L2O, 6 | Enumerable = root.Enumerable; 7 | 8 | QUnit.module('Aggregate Tests'); 9 | 10 | test('IsEmpty_Empty', function () { 11 | ok(Enumerable.empty().isEmpty()); 12 | }); 13 | 14 | test('IsEmpty_Empty', function () { 15 | ok(!Enumerable.returnValue(1).isEmpty()); 16 | }); 17 | 18 | test('Min', function () { 19 | equal(3, Enumerable.fromArray([5,3,7]).minBy(identity, function (x, y) { return x % 3 - y % 3; }).first()); 20 | }); 21 | 22 | test('MinBy', function () { 23 | var res = Enumerable.fromArray([2, 5, 0, 7, 4, 3, 6, 2, 1]).minBy(function (x) { return x % 3; }); 24 | ok(res.sequenceEqual(Enumerable.fromArray([ 0, 3, 6]))); 25 | }); 26 | 27 | test('MinBy_Empty', function () { 28 | raises(function () { 29 | Enumerable.empty().minBy(identity); 30 | }); 31 | }); 32 | 33 | test('Max', function () { 34 | equal(5, Enumerable.fromArray([2, 5, 3, 7]).maxBy(identity, function (x, y) { return x % 7 - y % 7; }).first()); 35 | }); 36 | 37 | test('MaxBy', function () { 38 | var res = Enumerable.fromArray([2, 5, 0, 7, 4, 3, 6, 2, 1]).maxBy(function (x) { return x % 3; }); 39 | ok(res.sequenceEqual(Enumerable.fromArray([2, 5, 2 ]))); 40 | }); 41 | 42 | test('MinBy_Empty', function () { 43 | raises(function () { 44 | Enumerable.empty().maxBy(identity); 45 | }); 46 | }); 47 | 48 | 49 | }(this)); 50 | -------------------------------------------------------------------------------- /lib/IxJs/tests/creation.js: -------------------------------------------------------------------------------- 1 | (function (window) { 2 | 3 | function identity (x) { return x; } 4 | 5 | function noNext (e) { 6 | ok(!e.moveNext()); 7 | } 8 | 9 | function hasNext (e, value) { 10 | ok(e.moveNext()); 11 | equal(value, e.getCurrent()); 12 | } 13 | 14 | var root = window.L2O, 15 | Enumerator = root.Enumerator, 16 | Enumerable = root.Enumerable; 17 | 18 | function MyEnumerator() { 19 | var values = [1, 2], length = values.length, index = 0, current; 20 | return Enumerator.create(function () { 21 | if (index === length) { 22 | return false; 23 | } 24 | current = values[index++]; 25 | return true; 26 | }, function () { 27 | return current; 28 | }); 29 | } 30 | 31 | QUnit.module('Creation Tests'); 32 | 33 | test('Create1', function () { 34 | var hot = false, 35 | res = Enumerable.create(function () { 36 | hot = true; 37 | return new MyEnumerator(); 38 | }); 39 | 40 | ok(!hot); 41 | 42 | var e = res.getEnumerator(); 43 | ok(hot); 44 | 45 | hasNext(e, 1); 46 | hasNext(e, 2); 47 | noNext(e); 48 | 49 | hot = false; 50 | var f = res.getEnumerator(); 51 | ok(hot); 52 | }); 53 | 54 | test('Return', function () { 55 | equal(42, Enumerable.returnValue(42).single()); 56 | }); 57 | 58 | test('Throw', function () { 59 | var ex = new Error('Woops'); 60 | var xs = Enumerable.throwException(ex); 61 | 62 | var e = xs.getEnumerator(); 63 | raises(function () { e.moveNext(); }); 64 | }); 65 | 66 | test('Defer1', function () { 67 | var i = 0; 68 | var n = 5; 69 | var xs = Enumerable.defer(function () { 70 | i++; 71 | return Enumerable.range(0, n); 72 | }); 73 | 74 | equal(0, i); 75 | 76 | ok(xs.sequenceEqual(Enumerable.range(0, n))); 77 | equal(1, i); 78 | 79 | n = 3; 80 | ok(xs.sequenceEqual(Enumerable.range(0, n))); 81 | equal(2, i); 82 | }); 83 | 84 | test('Defer2', function () { 85 | var xs = Enumerable.defer(function () { 86 | throw new Error('woops'); 87 | }); 88 | 89 | raises(function () { xs.getEnumerator().moveNext(); }); 90 | }); 91 | 92 | test('Generate', function () { 93 | var res = Enumerable.generate( 94 | 0, 95 | function (x) { return x < 5; }, 96 | function (x) { return x + 1; }, 97 | function (x) { return x * x; }); 98 | ok(res.sequenceEqual(Enumerable.fromArray([0, 1, 4, 9, 16]))); 99 | }); 100 | 101 | function MyDisposable () { 102 | this.done = false; 103 | } 104 | MyDisposable.prototype.dispose = function () { 105 | this.done = true; 106 | }; 107 | 108 | test('Using1', function () { 109 | var d; 110 | 111 | var xs = Enumerable.using(function () { 112 | return d = new MyDisposable(); 113 | }, function (d_) { 114 | return Enumerable.returnValue(1); 115 | }); 116 | ok(!d); 117 | 118 | var d1; 119 | xs.forEach(function () { d1 = d; ok(d1); ok(!d1.done); }); 120 | ok(d1.done); 121 | 122 | var d2; 123 | xs.forEach(function () { d2 = d; ok(d2); ok(!d2.done); }); 124 | ok(d2.done); 125 | 126 | notEqual(d1, d2); 127 | }); 128 | 129 | test('Using2', function () { 130 | var d; 131 | 132 | var xs = Enumerable.using(function () { 133 | return d = new MyDisposable(); 134 | }, function (d_) { 135 | return Enumerable.throwException(new Error('Woops')); 136 | }); 137 | ok(!d); 138 | 139 | raises(function () { xs.forEach(function () { }); }); 140 | ok(d.done); 141 | }); 142 | 143 | test('Using3', function () { 144 | var d; 145 | 146 | var xs = Enumerable.using(function () { 147 | return d = new MyDisposable(); 148 | }, function (d_) { 149 | throw new Error('Woops'); 150 | }); 151 | ok(!d); 152 | 153 | raises(function () { xs.forEach(function () { }); }); 154 | ok(d.done); 155 | }); 156 | 157 | test('RepeatElementInfinite', function () { 158 | var xs = Enumerable.repeat(42).take(1000); 159 | ok(xs.all(function (x) { return x === 42; })); 160 | ok(xs.count() == 1000); 161 | }); 162 | 163 | test('RepeatSequence1', function () { 164 | var i = 0; 165 | var xs = Enumerable.fromArray([1,2]).doAction(function () { i++; }).repeat(); 166 | 167 | var res = xs.take(10).toArray(); 168 | equal(10, res.length); 169 | ok(Enumerable.fromArray(res).bufferWithCount(2).select(function (b) { return b.sum(); }).all(function (x) { return x === 3; })); 170 | equal(10, i); 171 | }); 172 | 173 | test('RepeatSequence2', function () { 174 | var i = 0; 175 | var xs = Enumerable.fromArray([1,2]).doAction(function () { i++; }).repeat(5); 176 | 177 | var res = xs.toArray(); 178 | equal(10, res.length); 179 | ok(Enumerable.fromArray(res).bufferWithCount(2).select(function (b) { return b.sum(); }).all(function (x) { return x === 3; })); 180 | equal(10, i); 181 | }); 182 | 183 | }(this)); -------------------------------------------------------------------------------- /lib/IxJs/tests/vendor/qunit-1.9.0.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.9.0 - A JavaScript Unit Testing Framework 3 | * 4 | * http://docs.jquery.com/QUnit 5 | * 6 | * Copyright (c) 2012 John Resig, Jörn Zaefferer 7 | * Dual licensed under the MIT (MIT-LICENSE.txt) 8 | * or GPL (GPL-LICENSE.txt) licenses. 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 5px 5px 0 0; 42 | -moz-border-radius: 5px 5px 0 0; 43 | -webkit-border-top-right-radius: 5px; 44 | -webkit-border-top-left-radius: 5px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-testrunner-toolbar label { 58 | display: inline-block; 59 | padding: 0 .5em 0 .1em; 60 | } 61 | 62 | #qunit-banner { 63 | height: 5px; 64 | } 65 | 66 | #qunit-testrunner-toolbar { 67 | padding: 0.5em 0 0.5em 2em; 68 | color: #5E740B; 69 | background-color: #eee; 70 | } 71 | 72 | #qunit-userAgent { 73 | padding: 0.5em 0 0.5em 2.5em; 74 | background-color: #2b81af; 75 | color: #fff; 76 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 77 | } 78 | 79 | 80 | /** Tests: Pass/Fail */ 81 | 82 | #qunit-tests { 83 | list-style-position: inside; 84 | } 85 | 86 | #qunit-tests li { 87 | padding: 0.4em 0.5em 0.4em 2.5em; 88 | border-bottom: 1px solid #fff; 89 | list-style-position: inside; 90 | } 91 | 92 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 93 | display: none; 94 | } 95 | 96 | #qunit-tests li strong { 97 | cursor: pointer; 98 | } 99 | 100 | #qunit-tests li a { 101 | padding: 0.5em; 102 | color: #c2ccd1; 103 | text-decoration: none; 104 | } 105 | #qunit-tests li a:hover, 106 | #qunit-tests li a:focus { 107 | color: #000; 108 | } 109 | 110 | #qunit-tests ol { 111 | margin-top: 0.5em; 112 | padding: 0.5em; 113 | 114 | background-color: #fff; 115 | 116 | border-radius: 5px; 117 | -moz-border-radius: 5px; 118 | -webkit-border-radius: 5px; 119 | } 120 | 121 | #qunit-tests table { 122 | border-collapse: collapse; 123 | margin-top: .2em; 124 | } 125 | 126 | #qunit-tests th { 127 | text-align: right; 128 | vertical-align: top; 129 | padding: 0 .5em 0 0; 130 | } 131 | 132 | #qunit-tests td { 133 | vertical-align: top; 134 | } 135 | 136 | #qunit-tests pre { 137 | margin: 0; 138 | white-space: pre-wrap; 139 | word-wrap: break-word; 140 | } 141 | 142 | #qunit-tests del { 143 | background-color: #e0f2be; 144 | color: #374e0c; 145 | text-decoration: none; 146 | } 147 | 148 | #qunit-tests ins { 149 | background-color: #ffcaca; 150 | color: #500; 151 | text-decoration: none; 152 | } 153 | 154 | /*** Test Counts */ 155 | 156 | #qunit-tests b.counts { color: black; } 157 | #qunit-tests b.passed { color: #5E740B; } 158 | #qunit-tests b.failed { color: #710909; } 159 | 160 | #qunit-tests li li { 161 | padding: 5px; 162 | background-color: #fff; 163 | border-bottom: none; 164 | list-style-position: inside; 165 | } 166 | 167 | /*** Passing Styles */ 168 | 169 | #qunit-tests li li.pass { 170 | color: #3c510c; 171 | background-color: #fff; 172 | border-left: 10px solid #C6E746; 173 | } 174 | 175 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 176 | #qunit-tests .pass .test-name { color: #366097; } 177 | 178 | #qunit-tests .pass .test-actual, 179 | #qunit-tests .pass .test-expected { color: #999999; } 180 | 181 | #qunit-banner.qunit-pass { background-color: #C6E746; } 182 | 183 | /*** Failing Styles */ 184 | 185 | #qunit-tests li li.fail { 186 | color: #710909; 187 | background-color: #fff; 188 | border-left: 10px solid #EE5757; 189 | white-space: pre; 190 | } 191 | 192 | #qunit-tests > li:last-child { 193 | border-radius: 0 0 5px 5px; 194 | -moz-border-radius: 0 0 5px 5px; 195 | -webkit-border-bottom-right-radius: 5px; 196 | -webkit-border-bottom-left-radius: 5px; 197 | } 198 | 199 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 200 | #qunit-tests .fail .test-name, 201 | #qunit-tests .fail .module-name { color: #000000; } 202 | 203 | #qunit-tests .fail .test-actual { color: #EE5757; } 204 | #qunit-tests .fail .test-expected { color: green; } 205 | 206 | #qunit-banner.qunit-fail { background-color: #EE5757; } 207 | 208 | 209 | /** Result */ 210 | 211 | #qunit-testresult { 212 | padding: 0.5em 0.5em 0.5em 2.5em; 213 | 214 | color: #2b81af; 215 | background-color: #D2E0E6; 216 | 217 | border-bottom: 1px solid white; 218 | } 219 | #qunit-testresult .module-name { 220 | font-weight: bold; 221 | } 222 | 223 | /** Fixture */ 224 | 225 | #qunit-fixture { 226 | position: absolute; 227 | top: -10000px; 228 | left: -10000px; 229 | width: 1000px; 230 | height: 1000px; 231 | } 232 | -------------------------------------------------------------------------------- /lib/IxJs/tests/vendor/qunit-clib.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * QUnit CLI Boilerplate v1.0.0 3 | * Copyright 2011-2012 John-David Dalton 4 | * Based on a gist by Jörn Zaefferer 5 | * Available under MIT license 6 | */ 7 | ;(function(global) { 8 | 'use strict'; 9 | 10 | /** Add `console.log()` support for Narwhal, Rhino, and RingoJS */ 11 | global.console || (global.console = { 'log': global.print }); 12 | 13 | /** Reduce global.QUnit.QUnit -> global.QUnit */ 14 | global.QUnit && (QUnit = QUnit.QUnit || QUnit); 15 | 16 | /*--------------------------------------------------------------------------*/ 17 | 18 | /** Used as a horizontal rule in console output */ 19 | var hr = '----------------------------------------'; 20 | 21 | /** Shortcut used to convert array-like objects to arrays */ 22 | var slice = [].slice; 23 | 24 | /** Used to resolve a value's internal [[Class]] */ 25 | var toString = {}.toString; 26 | 27 | /** Used by timer methods */ 28 | var doneCalled, 29 | timer, 30 | counter = 0, 31 | ids = {}; 32 | 33 | /*--------------------------------------------------------------------------*/ 34 | 35 | /** 36 | * An iteration utility for arrays. 37 | * 38 | * @private 39 | * @param {Array} array The array to iterate over. 40 | * @param {Function} callback The function called per iteration. 41 | */ 42 | function each(array, callback) { 43 | var index = -1, 44 | length = array.length; 45 | 46 | while (++index < length) { 47 | callback(array[index], index, array); 48 | } 49 | } 50 | 51 | /** 52 | * Checks if the specified `value` is a function. 53 | * 54 | * @private 55 | * @param {Mixed} value The value to check. 56 | * @returns {Boolean} Returns `true` if `value` is a function, else `false`. 57 | */ 58 | function isFunction(value) { 59 | return toString.call(value) == '[object Function]'; 60 | } 61 | 62 | /*--------------------------------------------------------------------------*/ 63 | 64 | /** 65 | * Timeout fallbacks based on the work of Andrea Giammarchi and Weston C. 66 | * https://github.com/WebReflection/wru/blob/master/src/rhinoTimers.js 67 | * http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout 68 | */ 69 | 70 | /** 71 | * Clears the delay set by `setInterval` or `setTimeout`. 72 | * 73 | * @memberOf global 74 | * @param {Number} id The ID of the timeout to be cleared. 75 | */ 76 | function clearTimer(id) { 77 | if (ids[id]) { 78 | ids[id].cancel(); 79 | timer.purge(); 80 | delete ids[id]; 81 | } 82 | } 83 | 84 | /** 85 | * Schedules timer-based callbacks. 86 | * 87 | * @private 88 | * @param {Function} fn The function to call. 89 | * @oaram {Number} delay The number of milliseconds to delay the `fn` call. 90 | * @param [arg1, arg2, ...] Arguments to invoke `fn` with. 91 | * @param {Boolean} repeated A flag to specify whether `fn` is called repeatedly. 92 | * @returns {Number} The the ID of the timeout. 93 | */ 94 | function schedule(fn, delay, args, repeated) { 95 | // Rhino 1.7RC4 will error assigning `task` below 96 | // https://bugzilla.mozilla.org/show_bug.cgi?id=775566 97 | var task = ids[++counter] = new JavaAdapter(java.util.TimerTask, { 98 | 'run': function() { 99 | fn.apply(global, args); 100 | } 101 | }); 102 | // support non-functions 103 | if (!isFunction(fn)) { 104 | fn = (function(code) { 105 | code = String(code); 106 | return function() { eval(code); }; 107 | }(fn)); 108 | } 109 | // used by setInterval 110 | if (repeated) { 111 | timer.schedule(task, delay, delay); 112 | } 113 | // used by setTimeout 114 | else { 115 | timer.schedule(task, delay); 116 | } 117 | return counter; 118 | } 119 | 120 | /** 121 | * Executes a code snippet or function repeatedly, with a delay between each call. 122 | * 123 | * @memberOf global 124 | * @param {Function|String} fn The function to call or string to evaluate. 125 | * @oaram {Number} delay The number of milliseconds to delay each `fn` call. 126 | * @param [arg1, arg2, ...] Arguments to invoke `fn` with. 127 | * @returns {Number} The the ID of the timeout. 128 | */ 129 | function setInterval(fn, delay) { 130 | return schedule(fn, delay, slice.call(arguments, 2), true); 131 | } 132 | 133 | /** 134 | * Executes a code snippet or a function after specified delay. 135 | * 136 | * @memberOf global 137 | * @param {Function|String} fn The function to call or string to evaluate. 138 | * @oaram {Number} delay The number of milliseconds to delay the `fn` call. 139 | * @param [arg1, arg2, ...] Arguments to invoke `fn` with. 140 | * @returns {Number} The the ID of the timeout. 141 | */ 142 | function setTimeout(fn, delay) { 143 | return schedule(fn, delay, slice.call(arguments, 2)); 144 | } 145 | 146 | /*--------------------------------------------------------------------------*/ 147 | 148 | /** 149 | * A logging callback triggered when all testing is completed. 150 | * 151 | * @memberOf QUnit 152 | * @param {Object} details An object with properties `failed`, `passed`, 153 | * `runtime`, and `total`. 154 | */ 155 | function done(details) { 156 | // stop `asyncTest()` from erroneously calling `done()` twice in 157 | // environments w/o timeouts 158 | if (doneCalled) { 159 | return; 160 | } 161 | doneCalled = true; 162 | console.log(hr); 163 | console.log(' PASS: ' + details.passed + ' FAIL: ' + details.failed + ' TOTAL: ' + details.total); 164 | console.log(' Finished in ' + details.runtime + ' milliseconds.'); 165 | console.log(hr); 166 | 167 | // exit out of Rhino 168 | try { 169 | quit(); 170 | } catch(e) { } 171 | 172 | // exit out of Node.js 173 | try { 174 | process.exit(); 175 | } catch(e) { } 176 | } 177 | 178 | /** 179 | * A logging callback triggered after every assertion. 180 | * 181 | * @memberOf QUnit 182 | * @param {Object} details An object with properties `actual`, `expected`, 183 | * `message`, and `result`. 184 | */ 185 | function log(details) { 186 | var expected = details.expected, 187 | result = details.result, 188 | type = typeof expected != 'undefined' ? 'EQ' : 'OK'; 189 | 190 | var assertion = [ 191 | result ? 'PASS' : 'FAIL', 192 | type, 193 | details.message || 'ok' 194 | ]; 195 | 196 | if (!result && type == 'EQ') { 197 | assertion.push('Expected: ' + expected + ', Actual: ' + details.actual); 198 | } 199 | QUnit.config.testStats.assertions.push(assertion.join(' | ')); 200 | } 201 | 202 | /** 203 | * A logging callback triggered at the start of every test module. 204 | * 205 | * @memberOf QUnit 206 | * @param {Object} details An object with property `name`. 207 | */ 208 | function moduleStart(details) { 209 | console.log(hr); 210 | console.log(details.name); 211 | console.log(hr); 212 | } 213 | 214 | /** 215 | * Converts an object into a string representation. 216 | * 217 | * @memberOf QUnit 218 | * @type Function 219 | * @param {Object} object The object to stringify. 220 | * @returns {String} The result string. 221 | */ 222 | var parseObject = (function() { 223 | var func = QUnit.jsDump.parsers.object; 224 | return function(object) { 225 | // fork to support Rhino's error objects 226 | if (typeof object.rhinoException == 'object') { 227 | return object.name + 228 | ' { message: "' + object.message + 229 | '", fileName: "' + object.fileName + 230 | '", lineNumber: ' + object.lineNumber + ' }'; 231 | } 232 | return func(object); 233 | }; 234 | }()); 235 | 236 | /** 237 | * A logging callback triggered after a test is completed. 238 | * 239 | * @memberOf QUnit 240 | * @param {Object} details An object with properties `failed`, `name`, 241 | * `passed`, and `total`. 242 | */ 243 | function testDone(details) { 244 | var assertions = QUnit.config.testStats.assertions, 245 | testName = details.name; 246 | 247 | if (details.failed > 0) { 248 | console.log(' FAIL - '+ testName); 249 | each(assertions, function(value) { 250 | console.log(' ' + value); 251 | }); 252 | } 253 | else { 254 | console.log(' PASS - ' + testName); 255 | } 256 | assertions.length = 0; 257 | } 258 | 259 | /*--------------------------------------------------------------------------*/ 260 | 261 | /** 262 | * An object used to hold information about the current running test. 263 | * 264 | * @memberOf QUnit.config 265 | * @type Object 266 | */ 267 | QUnit.config.testStats = { 268 | 269 | /** 270 | * An array of test summaries (pipe separated). 271 | * 272 | * @memberOf QUnit.config.testStats 273 | * @type Array 274 | */ 275 | 'assertions': [] 276 | }; 277 | 278 | // add shortcuts to the global 279 | // exclude `module` because some environments have it as a built-in object 280 | each(['asyncTest', 'deepEqual', 'equal', 'equals', 'expect', 'notDeepEqual', 281 | 'notEqual', 'notStrictEqual', 'ok', 'raises', 'same', 'start', 'stop', 282 | 'strictEqual', 'test', 'throws'], function(funcName) { 283 | var func = QUnit[funcName]; 284 | if (func) { 285 | global[funcName] = func; 286 | } 287 | }); 288 | 289 | // expose timer methods to global 290 | try { 291 | timer = new java.util.Timer; 292 | if (!isFunction(global.clearInterval)) { 293 | global.clearInterval = clearTimer; 294 | } 295 | if (!isFunction(global.clearTimeout)) { 296 | global.clearTimeout = clearTimer; 297 | } 298 | if (!isFunction(global.setInterval)) { 299 | global.setInterval = setInterval; 300 | } 301 | if (!isFunction(global.setTimeout)) { 302 | global.setTimeout = setTimeout; 303 | } 304 | } catch(e) { } 305 | 306 | // add callbacks 307 | QUnit.done(done); 308 | QUnit.log(log); 309 | QUnit.moduleStart(moduleStart); 310 | QUnit.testDone(testDone); 311 | 312 | // add wrapped function 313 | QUnit.jsDump.parsers.object = parseObject; 314 | 315 | // must call `QUnit.start()` in the test file if using QUnit < 1.3.0 with 316 | // Node.js or any version of QUnit with Narwhal, Rhino, or RingoJS 317 | QUnit.init(); 318 | 319 | }(typeof global == 'object' && global || this)); 320 | -------------------------------------------------------------------------------- /lib/PermutationsAndShuffles.cs.linq: -------------------------------------------------------------------------------- 1 | void Main() 2 | { 3 | (new int [] {}).Permutations().Dump("Permutations of empty collection"); 4 | (new [] {1}).Permutations().Dump("Permutations of singleton collection"); 5 | (new [] {1,2}).Permutations().Dump("Permutations of doubleton collection"); 6 | (new [] {1,2,3}).Permutations().Dump("Permutations of tripleton 7 | collection"); 8 | 9 | Pluck(2)(new [] {1,2,3}).Dump("Pluck second element from a 10 | collection"); 11 | CoPluck(2)(new [] {1,2,3}).Dump("A new collection with all 12 | but the second element"); 13 | 14 | (new [] {new [] {1,2,3}, new [] {4,5,6}}) 15 | .Select(Pluck(2)) 16 | .Dump("Map Pluck(2)"); 17 | 18 | (new [] {1,2,3}).PluckPair(2).Dump("The plucked element and its residual"); 19 | (new [] {1,2,3}).PluckPairs().Dump("All pluck pairs of a collection"); 20 | 21 | (new int [] {}).Permutations2().Dump("Permutations of empty collection"); 22 | (new [] {1}).Permutations2().Dump("Permutations of singleton collection"); 23 | (new [] {1,2}).Permutations2().Dump("Permutations of doubleton collection"); 24 | (new [] {1,2,3}).Permutations2().Dump("Permutations of tripleton 25 | collection"); 26 | 27 | (new int [] {}).Riffles(new int [] {}).Dump("All riffles of two 28 | empty collections"); 29 | (new [] {1}).Riffles(new int [] {}).Dump("All riffles of a 30 | singleton and an empty"); 31 | (new int [] {}).Riffles(new [] {4}).Dump("All riffles of an empty 32 | and a singleton"); 33 | (new [] {1}).Riffles(new [] {4}).Dump("All riffles of two singletons"); 34 | (new [] {1,2}).Riffles(new [] {4}).Dump("All riffles of a 35 | doubleton and a singletons"); 36 | (new [] {1}).Riffles(new [] {4,5}).Dump("All riffles of a 37 | singleton and a doubleton"); 38 | (new [] {1,2}).Riffles(new [] {4,5}).Dump("All riffles of two doubletons"); 39 | (new [] {1,2,3}).Riffles(new [] {4,5}).Dump("All riffles of a 40 | tripleton and a doubleton"); 41 | (new [] {1,2}).Riffles(new [] {4,5,6}).Dump("All riffles of a 42 | doubleton and a tripleton"); 43 | (new [] {1,2,3}).Riffles(new [] {4,5,6}).Dump("All riffles of two 44 | tripletons"); 45 | } 46 | 47 | // Pluck returns a function so it can be mapped over other collections. 48 | public static Func, T> Pluck(int n) 49 | { return xs => 50 | { if (n <= 0 || n > xs.Count()) 51 | throw new ArgumentOutOfRangeException("n", 52 | String.Format("argument must have value between 1 and 53 | {0} inclusive", 54 | xs.Count())); 55 | return xs.ElementAt(n - 1); 56 | }; 57 | } 58 | 59 | // CoPluck returns a function so it can be mapped over other collections 60 | public static Func, IEnumerable> CoPluck(int n) 61 | { return xs => 62 | { var c = xs.Count(); 63 | if (n <= 0 || n > c) 64 | throw new ArgumentOutOfRangeException("n", 65 | String.Format("argument must have value between 1 and 66 | {0} inclusive", c)); 67 | var ys = new List(); 68 | var i = 1; 69 | foreach (var x in xs) 70 | { if (i != n) 71 | ys.Add(x); 72 | i++; 73 | } 74 | return ys; 75 | }; 76 | } 77 | public static class Extensions 78 | { 79 | public static IEnumerable> Riffles(this 80 | IEnumerable left, IEnumerable right) 81 | { if (left.Count() == 0) 82 | { yield return right; 83 | yield break; 84 | } 85 | if (right.Count() == 0) 86 | { yield return left; 87 | yield break; 88 | } 89 | foreach (var r in right.Splits().Skip(1).Take(1)) 90 | { foreach (var l in left.Splits()) 91 | { foreach (var f in Riffles(l.Second(), r.Second())) 92 | { yield return l.First().Concat(r.First()).Concat(f); 93 | } 94 | } 95 | } 96 | } 97 | public static T Second (this IEnumerable these) 98 | { return these.Skip(1).First(); 99 | } 100 | public static IEnumerable>> 101 | Splits(this IEnumerable these) 102 | { var c = these.Count(); 103 | for (var i = 0; i <= c; i++) 104 | yield return new IEnumerable [2] {these.Take(i), these.Skip(i)}; 105 | } 106 | 107 | public static IEnumerable Append(this IEnumerable these, T that) 108 | { foreach (var t in these) 109 | yield return t; 110 | yield return that; 111 | } 112 | public static IEnumerable Prepend(this IEnumerable these, T that) 113 | { yield return that; 114 | foreach (var t in these) 115 | yield return t; 116 | } 117 | public static IEnumerable>> 118 | PluckPairs(this IEnumerable xs) 119 | { var c = xs.Count(); 120 | var result = new List>>(); 121 | for (var n = 1; n <= c; n++) 122 | { var ys = new List(); 123 | var y = default(T); 124 | var i = 1; 125 | foreach (var x in xs) 126 | { if (i != n) 127 | ys.Add(x); 128 | else 129 | y = x; 130 | i++; 131 | } 132 | result.Add(Tuple.Create(y, (IEnumerable)ys)); 133 | } 134 | return result; 135 | } 136 | public static Tuple> PluckPair(this 137 | IEnumerable xs, int n) 138 | { var c = xs.Count(); 139 | if (n <= 0 || n > c) 140 | throw new ArgumentOutOfRangeException("n", 141 | String.Format("argument must have value between 1 and 142 | {0} inclusive", c)); 143 | var ys = new List(); 144 | var y = default(T); 145 | var i = 1; 146 | foreach (var x in xs) 147 | { if (i != n) 148 | ys.Add(x); 149 | else 150 | y = x; 151 | i++; 152 | } 153 | return Tuple.Create(y, (IEnumerable)ys); 154 | } 155 | 156 | // Slow but understandable "reference implementation" of Permutations 157 | public static IEnumerable> Permutations2(this 158 | IEnumerable these) 159 | { Debug.Assert(these != null); 160 | // Inherently combinatorial-time algorithm! 161 | var len = these.Count(); 162 | if (len == 1) 163 | { yield return these; 164 | yield break; 165 | } 166 | foreach (var pluckPair in these.PluckPairs()) 167 | { foreach (var permutation in pluckPair.Item2.Permutations2()) 168 | { yield return permutation.Prepend(pluckPair.Item1); 169 | } 170 | } 171 | } 172 | // Faster but more difficult index-based implementation of Permutations 173 | public static IEnumerable> Permutations(this 174 | IEnumerable these) 175 | { Debug.Assert(these != null); 176 | // Inherently combinatorial-time algorithm! 177 | var len = these.Count(); 178 | if (len == 1) 179 | { yield return these; 180 | yield break; 181 | } 182 | var source = these.ToArray(); 183 | var result = new T [len]; 184 | for (var i = 0; i < len; i++) 185 | { result[0] = source[i]; 186 | var rs = new T [len - 1]; 187 | for (var j = 0; j < len - 1; j++) 188 | { if (j < i) 189 | { rs[j] = source[j]; 190 | } 191 | else if (j >= i) 192 | { rs[j] = source[j + 1]; 193 | } 194 | } 195 | var perms = rs.Permutations(); 196 | foreach (var perm in perms) 197 | { var ps = perm.ToArray(); 198 | for(var j = 0; j < len - 1; j++) 199 | result[j + 1] = ps[j]; 200 | yield return result; 201 | } 202 | } 203 | } 204 | } -------------------------------------------------------------------------------- /lib/PermutationsAndShuffles.js: -------------------------------------------------------------------------------- 1 | /* void Main() 2 | { 3 | (new int [] {}).Permutations().Dump("Permutations of empty collection"); 4 | (new [] {1}).Permutations().Dump("Permutations of singleton collection"); 5 | (new [] {1,2}).Permutations().Dump("Permutations of doubleton collection"); 6 | (new [] {1,2,3}).Permutations().Dump("Permutations of tripleton 7 | collection"); 8 | 9 | Pluck(2)(new [] {1,2,3}).Dump("Pluck second element from a 10 | collection"); 11 | CoPluck(2)(new [] {1,2,3}).Dump("A new collection with all 12 | but the second element"); 13 | 14 | (new [] {new [] {1,2,3}, new [] {4,5,6}}) 15 | .Select(Pluck(2)) 16 | .Dump("Map Pluck(2)"); 17 | 18 | (new [] {1,2,3}).PluckPair(2).Dump("The plucked element and its residual"); 19 | (new [] {1,2,3}).PluckPairs().Dump("All pluck pairs of a collection"); 20 | 21 | (new int [] {}).Permutations2().Dump("Permutations of empty collection"); 22 | (new [] {1}).Permutations2().Dump("Permutations of singleton collection"); 23 | (new [] {1,2}).Permutations2().Dump("Permutations of doubleton collection"); 24 | (new [] {1,2,3}).Permutations2().Dump("Permutations of tripleton 25 | collection"); 26 | 27 | (new int [] {}).Riffles(new int [] {}).Dump("All riffles of two 28 | empty collections"); 29 | (new [] {1}).Riffles(new int [] {}).Dump("All riffles of a 30 | singleton and an empty"); 31 | (new int [] {}).Riffles(new [] {4}).Dump("All riffles of an empty 32 | and a singleton"); 33 | (new [] {1}).Riffles(new [] {4}).Dump("All riffles of two singletons"); 34 | (new [] {1,2}).Riffles(new [] {4}).Dump("All riffles of a 35 | doubleton and a singletons"); 36 | (new [] {1}).Riffles(new [] {4,5}).Dump("All riffles of a 37 | singleton and a doubleton"); 38 | (new [] {1,2}).Riffles(new [] {4,5}).Dump("All riffles of two doubletons"); 39 | (new [] {1,2,3}).Riffles(new [] {4,5}).Dump("All riffles of a 40 | tripleton and a doubleton"); 41 | (new [] {1,2}).Riffles(new [] {4,5,6}).Dump("All riffles of a 42 | doubleton and a tripleton"); 43 | (new [] {1,2,3}).Riffles(new [] {4,5,6}).Dump("All riffles of two 44 | tripletons"); 45 | } 46 | 47 | // Pluck returns a function so it can be mapped over other collections. 48 | public static Func, T> Pluck(int n) 49 | { return xs => 50 | { if (n <= 0 || n > xs.Count()) 51 | throw new ArgumentOutOfRangeException("n", 52 | String.Format("argument must have value between 1 and 53 | {0} inclusive", 54 | xs.Count())); 55 | return xs.ElementAt(n - 1); 56 | }; 57 | } 58 | 59 | // CoPluck returns a function so it can be mapped over other collections 60 | public static Func, IEnumerable> CoPluck(int n) 61 | { return xs => 62 | { var c = xs.Count(); 63 | if (n <= 0 || n > c) 64 | throw new ArgumentOutOfRangeException("n", 65 | String.Format("argument must have value between 1 and 66 | {0} inclusive", c)); 67 | var ys = new List(); 68 | var i = 1; 69 | foreach (var x in xs) 70 | { if (i != n) 71 | ys.Add(x); 72 | i++; 73 | } 74 | return ys; 75 | }; 76 | } 77 | public static class Extensions 78 | { 79 | public static IEnumerable> Riffles(this 80 | IEnumerable left, IEnumerable right) 81 | { if (left.Count() == 0) 82 | { yield return right; 83 | yield break; 84 | } 85 | if (right.Count() == 0) 86 | { yield return left; 87 | yield break; 88 | } 89 | foreach (var r in right.Splits().Skip(1).Take(1)) 90 | { foreach (var l in left.Splits()) 91 | { foreach (var f in Riffles(l.Second(), r.Second())) 92 | { yield return l.First().Concat(r.First()).Concat(f); 93 | } 94 | } 95 | } 96 | } 97 | public static T Second (this IEnumerable these) 98 | { return these.Skip(1).First(); 99 | } 100 | public static IEnumerable>> 101 | Splits(this IEnumerable these) 102 | { var c = these.Count(); 103 | for (var i = 0; i <= c; i++) 104 | yield return new IEnumerable [2] {these.Take(i), these.Skip(i)}; 105 | } 106 | 107 | public static IEnumerable Append(this IEnumerable these, T that) 108 | { foreach (var t in these) 109 | yield return t; 110 | yield return that; 111 | } 112 | public static IEnumerable Prepend(this IEnumerable these, T that) 113 | { yield return that; 114 | foreach (var t in these) 115 | yield return t; 116 | } 117 | public static IEnumerable>> 118 | PluckPairs(this IEnumerable xs) 119 | { var c = xs.Count(); 120 | var result = new List>>(); 121 | for (var n = 1; n <= c; n++) 122 | { var ys = new List(); 123 | var y = default(T); 124 | var i = 1; 125 | foreach (var x in xs) 126 | { if (i != n) 127 | ys.Add(x); 128 | else 129 | y = x; 130 | i++; 131 | } 132 | result.Add(Tuple.Create(y, (IEnumerable)ys)); 133 | } 134 | return result; 135 | } 136 | public static Tuple> PluckPair(this 137 | IEnumerable xs, int n) 138 | { var c = xs.Count(); 139 | if (n <= 0 || n > c) 140 | throw new ArgumentOutOfRangeException("n", 141 | String.Format("argument must have value between 1 and 142 | {0} inclusive", c)); 143 | var ys = new List(); 144 | var y = default(T); 145 | var i = 1; 146 | foreach (var x in xs) 147 | { if (i != n) 148 | ys.Add(x); 149 | else 150 | y = x; 151 | i++; 152 | } 153 | return Tuple.Create(y, (IEnumerable)ys); 154 | } 155 | 156 | // Slow but understandable "reference implementation" of Permutations 157 | public static IEnumerable> Permutations2(this 158 | IEnumerable these) 159 | { Debug.Assert(these != null); 160 | // Inherently combinatorial-time algorithm! 161 | var len = these.Count(); 162 | if (len == 1) 163 | { yield return these; 164 | yield break; 165 | } 166 | foreach (var pluckPair in these.PluckPairs()) 167 | { foreach (var permutation in pluckPair.Item2.Permutations2()) 168 | { yield return permutation.Prepend(pluckPair.Item1); 169 | } 170 | } 171 | } 172 | // Faster but more difficult index-based implementation of Permutations 173 | public static IEnumerable> Permutations(this 174 | IEnumerable these) 175 | { Debug.Assert(these != null); 176 | // Inherently combinatorial-time algorithm! 177 | var len = these.Count(); 178 | if (len == 1) 179 | { yield return these; 180 | yield break; 181 | } 182 | var source = these.ToArray(); 183 | var result = new T [len]; 184 | for (var i = 0; i < len; i++) 185 | { result[0] = source[i]; 186 | var rs = new T [len - 1]; 187 | for (var j = 0; j < len - 1; j++) 188 | { if (j < i) 189 | { rs[j] = source[j]; 190 | } 191 | else if (j >= i) 192 | { rs[j] = source[j + 1]; 193 | } 194 | } 195 | var perms = rs.Permutations(); 196 | foreach (var perm in perms) 197 | { var ps = perm.ToArray(); 198 | for(var j = 0; j < len - 1; j++) 199 | result[j + 1] = ps[j]; 200 | yield return result; 201 | } 202 | } 203 | } 204 | } 205 | */ 206 | -------------------------------------------------------------------------------- /lib/koanutils.js: -------------------------------------------------------------------------------- 1 | var Range = { 2 | create : function (start, count) { 3 | var values = []; 4 | for(var i = 0; i < count; i++) { 5 | values.push(i + start); 6 | } 7 | 8 | return values; 9 | } 10 | }; 11 | 12 | Array.prototype.toObservable = function() { 13 | return Rx.Observable.fromArray(this); 14 | }; 15 | 16 | var Ix = L2O; 17 | 18 | Array.prototype.toEnumerable = function() { 19 | return Ix.Enumerable.fromArray(this); 20 | }; 21 | 22 | Rx.Subject.prototype.onNextAll = function() { 23 | var subject = this; 24 | for(var arg in arguments) { 25 | subject.onNext(arguments[arg]); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /lib/qunit.css: -------------------------------------------------------------------------------- 1 | /** Font Family and Sizes */ 2 | 3 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 4 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 5 | } 6 | 7 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 8 | #qunit-tests { font-size: smaller; } 9 | 10 | 11 | /** Resets */ 12 | 13 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | 19 | /** Header */ 20 | 21 | #qunit-header { 22 | padding: 0.5em 0 0.5em 1em; 23 | 24 | color: #8699a4; 25 | background-color: #0d3349; 26 | 27 | font-size: 1.5em; 28 | line-height: 1em; 29 | font-weight: normal; 30 | 31 | border-radius: 15px 15px 0 0; 32 | -moz-border-radius: 15px 15px 0 0; 33 | -webkit-border-top-right-radius: 15px; 34 | -webkit-border-top-left-radius: 15px; 35 | } 36 | 37 | #qunit-header a { 38 | text-decoration: none; 39 | color: #c2ccd1; 40 | } 41 | 42 | #qunit-header a:hover, 43 | #qunit-header a:focus { 44 | color: #fff; 45 | } 46 | 47 | #qunit-banner { 48 | height: 5px; 49 | } 50 | 51 | #qunit-testrunner-toolbar { 52 | padding: 0.5em 0 0.5em 2em; 53 | color: #5E740B; 54 | background-color: #eee; 55 | } 56 | 57 | #qunit-userAgent { 58 | padding: 0.5em 0 0.5em 2.5em; 59 | background-color: #2b81af; 60 | color: #fff; 61 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 62 | } 63 | 64 | 65 | /** Tests: Pass/Fail */ 66 | 67 | #qunit-tests { 68 | list-style-position: inside; 69 | } 70 | 71 | #qunit-tests li { 72 | padding: 0.4em 0.5em 0.4em 2.5em; 73 | border-bottom: 1px solid #fff; 74 | list-style-position: inside; 75 | } 76 | 77 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 78 | display: none; 79 | } 80 | 81 | #qunit-tests li strong { 82 | cursor: pointer; 83 | } 84 | 85 | #qunit-tests ol { 86 | margin-top: 0.5em; 87 | padding: 0.5em; 88 | 89 | background-color: #fff; 90 | 91 | border-radius: 15px; 92 | -moz-border-radius: 15px; 93 | -webkit-border-radius: 15px; 94 | 95 | box-shadow: inset 0px 2px 13px #999; 96 | -moz-box-shadow: inset 0px 2px 13px #999; 97 | -webkit-box-shadow: inset 0px 2px 13px #999; 98 | } 99 | 100 | #qunit-tests table { 101 | border-collapse: collapse; 102 | margin-top: .2em; 103 | } 104 | 105 | #qunit-tests th { 106 | text-align: right; 107 | vertical-align: top; 108 | padding: 0 .5em 0 0; 109 | } 110 | 111 | #qunit-tests td { 112 | vertical-align: top; 113 | } 114 | 115 | #qunit-tests pre { 116 | margin: 0; 117 | white-space: pre-wrap; 118 | word-wrap: break-word; 119 | } 120 | 121 | #qunit-tests del { 122 | background-color: #e0f2be; 123 | color: #374e0c; 124 | text-decoration: none; 125 | } 126 | 127 | #qunit-tests ins { 128 | background-color: #ffcaca; 129 | color: #500; 130 | text-decoration: none; 131 | } 132 | 133 | /*** Test Counts */ 134 | 135 | #qunit-tests b.counts { color: black; } 136 | #qunit-tests b.passed { color: #5E740B; } 137 | #qunit-tests b.failed { color: #710909; } 138 | 139 | #qunit-tests li li { 140 | margin: 0.5em; 141 | padding: 0.4em 0.5em 0.4em 0.5em; 142 | background-color: #fff; 143 | border-bottom: none; 144 | list-style-position: inside; 145 | } 146 | 147 | /*** Passing Styles */ 148 | 149 | #qunit-tests li li.pass { 150 | color: #5E740B; 151 | background-color: #fff; 152 | border-left: 26px solid #C6E746; 153 | } 154 | 155 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 156 | #qunit-tests .pass .test-name { color: #366097; } 157 | 158 | #qunit-tests .pass .test-actual, 159 | #qunit-tests .pass .test-expected { color: #999999; } 160 | 161 | #qunit-banner.qunit-pass { background-color: #C6E746; } 162 | 163 | /*** Failing Styles */ 164 | 165 | #qunit-tests li li.fail { 166 | color: #710909; 167 | background-color: #fff; 168 | border-left: 26px solid #EE5757; 169 | } 170 | 171 | #qunit-tests > li:last-child { 172 | border-radius: 0 0 15px 15px; 173 | -moz-border-radius: 0 0 15px 15px; 174 | -webkit-border-bottom-right-radius: 15px; 175 | -webkit-border-bottom-left-radius: 15px; 176 | } 177 | 178 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 179 | #qunit-tests .fail .test-name, 180 | #qunit-tests .fail .module-name { color: #000000; } 181 | 182 | #qunit-tests .fail .test-actual { color: #EE5757; } 183 | #qunit-tests .fail .test-expected { color: green; } 184 | 185 | #qunit-banner.qunit-fail { background-color: #EE5757; } 186 | 187 | 188 | /** Result */ 189 | 190 | #qunit-testresult { 191 | padding: 0.5em 0.5em 0.5em 2.5em; 192 | 193 | color: #2b81af; 194 | background-color: #D2E0E6; 195 | 196 | border-bottom: 1px solid white; 197 | } 198 | 199 | /** Fixture */ 200 | 201 | #qunit-fixture { 202 | position: absolute; 203 | top: -10000px; 204 | left: -10000px; 205 | } 206 | -------------------------------------------------------------------------------- /lib/rx.aggregates.min.js: -------------------------------------------------------------------------------- 1 | /*** @preserve Copyright (c) Microsoft Corporation. All rights reserved. 2 | * This code is licensed by Microsoft Corporation under the terms 3 | * of the MICROSOFT REACTIVE EXTENSIONS FOR JAVASCRIPT AND .NET LIBRARIES License. 4 | * See http://go.microsoft.com/fwlink/?LinkID=220762. 5 | */ 6 | (function(e,t){var n=typeof exports=="object"&&exports&&(typeof e=="object"&&e&&e==e.global&&(window=e),exports);typeof define=="function"&&define.amd?define(["rx","exports"],function(n,r){return e.Rx=t(e,r,n),e.Rx}):typeof module=="object"&&module&&module.exports==n?module.exports=t(e,module.exports,require("./rx")):e.Rx=t(e,{},e.Rx)})(this,function(e,t,n,r){function l(e,t){return e===t}function c(e){return e}function h(e,t){return e>t?1:e===t?0:-1}function p(e,t,n){return new u(function(r){var i=!1,s=null,o=[];return e.subscribe(function(e){var u,a;try{a=t(e)}catch(f){r.onError(f);return}u=0;if(!i)i=!0,s=a;else try{u=n(a,s)}catch(l){r.onError(l);return}u>0&&(s=a,o=[]),u>=0&&o.push(e)},r.onError.bind(r),function(){r.onNext(o),r.onCompleted()})})}function d(e){if(e.length==0)throw new Error(f);return e[0]}function v(e,t,n){return new u(function(r){var i=0,s=t.length;return e.subscribe(function(e){var o=!1;try{i0){i=a.shift();try{n=t(i,e)}catch(o){r.onError(o);return}n||(r.onNext(!1),r.onCompleted())}else s?(r.onNext(!1),r.onCompleted()):u.push(e)},r.onError.bind(r),function(){i=!0,u.length===0&&(a.length>0?(r.onNext(!1),r.onCompleted()):s&&(r.onNext(!0),r.onCompleted()))}),l=e.subscribe(function(e){var n,s;if(u.length>0){s=u.shift();try{n=t(s,e)}catch(o){r.onError(o);return}n||(r.onNext(!1),r.onCompleted())}else i?(r.onNext(!1),r.onCompleted()):a.push(e)},r.onError.bind(r),function(){s=!0,a.length===0&&(u.length>0?(r.onNext(!1),r.onCompleted()):i&&(r.onNext(!0),r.onCompleted()))});return new o(f,l)})},s.elementAt=function(e){return m(this,e,!1)},s.elementAtOrDefault=function(e,t){return m(this,e,!0,t)},s.single=function(e){return e?this.where(e).single():g(this,!1)},s.singleOrDefault=function(e,t){return e?this.where(e).singleOrDefault(null,t):g(this,!0,t)},s.first=function(e){return e?this.where(e).first():y(this,!1)},s.firstOrDefault=function(e,t){return e?this.where(e).firstOrDefault(null,t):y(this,!0,t)},s.last=function(e){return e?this.where(e).last():b(this,!1)},s.lastOrDefault=function(e,t){return e?this.where(e).lastOrDefault(null,t):b(this,!0,t)},n}); -------------------------------------------------------------------------------- /lib/rx.binding.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve Copyright (c) Microsoft Corporation. All rights reserved. 3 | * This code is licensed by Microsoft Corporation under the terms 4 | * of the Microsoft Reference Source License (MS-RSL). 5 | * http://referencesource.microsoft.com/referencesourcelicense.aspx. 6 | */ 7 | 8 | (function (root, factory) { 9 | var freeExports = typeof exports == 'object' && exports && 10 | (typeof root == 'object' && root && root == root.global && (window = root), exports); 11 | 12 | // Because of build optimizers 13 | if (typeof define === 'function' && define.amd) { 14 | define(['rx', 'exports'], function (Rx, exports) { 15 | root.Rx = factory(root, exports, Rx); 16 | return root.Rx; 17 | }); 18 | } else if (typeof module == 'object' && module && module.exports == freeExports) { 19 | module.exports = factory(root, module.exports, require('./rx')); 20 | } else { 21 | root.Rx = factory(root, {}, root.Rx); 22 | } 23 | }(this, function (global, exp, root, undefined) { 24 | 25 | var Observable = root.Observable, 26 | observableProto = Observable.prototype, 27 | AnonymousObservable = root.Internals.AnonymousObservable, 28 | Subject = root.Subject, 29 | AsyncSubject = root.AsyncSubject, 30 | Observer = root.Observer, 31 | ScheduledObserver = root.Internals.ScheduledObserver, 32 | disposableCreate = root.Disposable.create, 33 | disposableEmpty = root.Disposable.empty, 34 | CompositeDisposable = root.CompositeDisposable, 35 | currentThreadScheduler = root.Scheduler.currentThread, 36 | inherits = root.Internals.inherits, 37 | addProperties = root.Internals.addProperties; 38 | 39 | // Utilities 40 | var objectDisposed = 'Object has been disposed'; 41 | function checkDisposed() { 42 | if (this.isDisposed) { 43 | throw new Error(objectDisposed); 44 | } 45 | } 46 | 47 | observableProto.multicast = function (subjectOrSubjectSelector, selector) { 48 | var source = this; 49 | return typeof subjectOrSubjectSelector === 'function' ? 50 | new AnonymousObservable(function (observer) { 51 | var connectable = source.multicast(subjectOrSubjectSelector()); 52 | return new CompositeDisposable(selector(connectable).subscribe(observer), connectable.connect()); 53 | }) : 54 | new ConnectableObservable(source, subjectOrSubjectSelector); 55 | }; 56 | 57 | observableProto.publish = function (selector) { 58 | return !selector ? 59 | this.multicast(new Subject()) : 60 | this.multicast(function () { 61 | return new Subject(); 62 | }, selector); 63 | }; 64 | 65 | observableProto.publishLast = function (selector) { 66 | return !selector ? 67 | this.multicast(new AsyncSubject()) : 68 | this.multicast(function () { 69 | return new AsyncSubject(); 70 | }, selector); 71 | }; 72 | 73 | observableProto.publishValue = function (initialValueOrSelector, initialValue) { 74 | return arguments.length === 2 ? 75 | this.multicast(function () { 76 | return new BehaviorSubject(initialValue); 77 | }, initialValueOrSelector) : 78 | this.multicast(new BehaviorSubject(initialValueOrSelector)); 79 | }; 80 | 81 | observableProto.replay = function (selector, bufferSize, window, scheduler) { 82 | return !selector ? 83 | this.multicast(new ReplaySubject(bufferSize, window, scheduler)) : 84 | this.multicast(function () { 85 | return new ReplaySubject(bufferSize, window, scheduler); 86 | }, selector); 87 | }; 88 | 89 | var InnerSubscription = function (subject, observer) { 90 | this.subject = subject; 91 | this.observer = observer; 92 | }; 93 | InnerSubscription.prototype.dispose = function () { 94 | if (!this.subject.isDisposed && this.observer !== null) { 95 | var idx = this.subject.observers.indexOf(this.observer); 96 | this.subject.observers.splice(idx, 1); 97 | this.observer = null; 98 | } 99 | }; 100 | 101 | var BehaviorSubject = root.BehaviorSubject = (function () { 102 | function subscribe(observer) { 103 | var ex; 104 | checkDisposed.call(this); 105 | if (!this.isStopped) { 106 | this.observers.push(observer); 107 | observer.onNext(this.value); 108 | return new InnerSubscription(this, observer); 109 | } 110 | ex = this.exception; 111 | if (ex) { 112 | observer.onError(ex); 113 | } else { 114 | observer.onCompleted(); 115 | } 116 | return disposableEmpty; 117 | } 118 | 119 | inherits(BehaviorSubject, Observable); 120 | function BehaviorSubject(value) { 121 | BehaviorSubject.super_.constructor.call(this, subscribe); 122 | 123 | this.value = value, 124 | this.observers = [], 125 | this.isDisposed = false, 126 | this.isStopped = false, 127 | this.exception = null; 128 | } 129 | 130 | addProperties(BehaviorSubject.prototype, Observer, { 131 | onCompleted: function () { 132 | checkDisposed.call(this); 133 | if (!this.isStopped) { 134 | var os = this.observers.slice(0); 135 | this.isStopped = true; 136 | for (var i = 0, len = os.length; i < len; i++) { 137 | os[i].onCompleted(); 138 | } 139 | 140 | this.observers = []; 141 | } 142 | }, 143 | onError: function (error) { 144 | checkDisposed.call(this); 145 | if (!this.isStopped) { 146 | var os = this.observers.slice(0); 147 | this.isStopped = true; 148 | this.exception = error; 149 | 150 | for (var i = 0, len = os.length; i < len; i++) { 151 | os[i].onError(error); 152 | } 153 | 154 | this.observers = []; 155 | } 156 | }, 157 | onNext: function (value) { 158 | checkDisposed.call(this); 159 | if (!this.isStopped) { 160 | this.value = value; 161 | var os = this.observers.slice(0); 162 | for (var i = 0, len = os.length; i < len; i++) { 163 | os[i].onNext(value); 164 | } 165 | } 166 | }, 167 | dispose: function () { 168 | this.isDisposed = true; 169 | this.observers = null; 170 | this.value = null; 171 | this.exception = null; 172 | } 173 | }); 174 | 175 | return BehaviorSubject; 176 | }()); 177 | 178 | // Replay Subject 179 | var ReplaySubject = root.ReplaySubject = (function (base) { 180 | var RemovableDisposable = function (subject, observer) { 181 | this.subject = subject; 182 | this.observer = observer; 183 | }; 184 | 185 | RemovableDisposable.prototype.dispose = function () { 186 | this.observer.dispose(); 187 | if (!this.subject.isDisposed) { 188 | var idx = this.subject.observers.indexOf(this.observer); 189 | this.subject.observers.splice(idx, 1); 190 | } 191 | }; 192 | 193 | function subscribe(observer) { 194 | var so = new ScheduledObserver(this.scheduler, observer), 195 | subscription = new RemovableDisposable(this, so); 196 | checkDisposed.call(this); 197 | this._trim(this.scheduler.now()); 198 | this.observers.push(so); 199 | 200 | var n = this.q.length; 201 | 202 | for (var i = 0, len = this.q.length; i < len; i++) { 203 | so.onNext(this.q[i].value); 204 | } 205 | 206 | if (this.hasError) { 207 | n++; 208 | so.onError(this.error); 209 | } else if (this.isStopped) { 210 | n++; 211 | so.onCompleted(); 212 | } 213 | 214 | so.ensureActive(n); 215 | return subscription; 216 | } 217 | 218 | inherits(ReplaySubject, Observable); 219 | 220 | function ReplaySubject(bufferSize, window, scheduler) { 221 | this.bufferSize = bufferSize == null ? Number.MAX_VALUE : bufferSize; 222 | this.window = window == null ? Number.MAX_VALUE : window; 223 | this.scheduler = scheduler || currentThreadScheduler; 224 | this.q = []; 225 | this.observers = []; 226 | this.isStopped = false; 227 | this.isDisposed = false; 228 | this.hasError = false; 229 | this.error = null; 230 | ReplaySubject.super_.constructor.call(this, subscribe); 231 | } 232 | 233 | addProperties(ReplaySubject.prototype, Observer, { 234 | _trim: function (now) { 235 | while (this.q.length > this.bufferSize) { 236 | this.q.shift(); 237 | } 238 | while (this.q.length > 0 && (now - this.q[0].interval) > this.window) { 239 | this.q.shift(); 240 | } 241 | }, 242 | onNext: function (value) { 243 | var observer; 244 | checkDisposed.call(this); 245 | if (!this.isStopped) { 246 | var now = this.scheduler.now(); 247 | this.q.push({ interval: now, value: value }); 248 | this._trim(now); 249 | 250 | var o = this.observers.slice(0); 251 | for (var i = 0, len = o.length; i < len; i++) { 252 | observer = o[i]; 253 | observer.onNext(value); 254 | observer.ensureActive(); 255 | } 256 | } 257 | }, 258 | onError: function (error) { 259 | var observer; 260 | checkDisposed.call(this); 261 | if (!this.isStopped) { 262 | this.isStopped = true; 263 | this.error = error; 264 | this.hasError = true; 265 | var now = this.scheduler.now(); 266 | this._trim(now); 267 | var o = this.observers.slice(0); 268 | for (var i = 0, len = o.length; i < len; i++) { 269 | observer = o[i]; 270 | observer.onError(error); 271 | observer.ensureActive(); 272 | } 273 | this.observers = []; 274 | } 275 | }, 276 | onCompleted: function () { 277 | var observer; 278 | checkDisposed.call(this); 279 | if (!this.isStopped) { 280 | this.isStopped = true; 281 | var now = this.scheduler.now(); 282 | this._trim(now); 283 | var o = this.observers.slice(0); 284 | for (var i = 0, len = o.length; i < len; i++) { 285 | observer = o[i]; 286 | observer.onCompleted(); 287 | observer.ensureActive(); 288 | } 289 | this.observers = []; 290 | } 291 | }, 292 | dispose: function () { 293 | this.isDisposed = true; 294 | this.observers = null; 295 | } 296 | }); 297 | 298 | return ReplaySubject; 299 | }()); 300 | 301 | var ConnectableObservable = (function () { 302 | inherits(ConnectableObservable, Observable); 303 | function ConnectableObservable(source, subject) { 304 | var state = { 305 | subject: subject, 306 | source: source.asObservable(), 307 | hasSubscription: false, 308 | subscription: null 309 | }; 310 | 311 | this.connect = function () { 312 | if (!state.hasSubscription) { 313 | state.hasSubscription = true; 314 | state.subscription = new CompositeDisposable(state.source.subscribe(state.subject), disposableCreate(function () { 315 | state.hasSubscription = false; 316 | })); 317 | } 318 | return state.subscription; 319 | }; 320 | 321 | var subscribe = function (observer) { 322 | return state.subject.subscribe(observer); 323 | }; 324 | ConnectableObservable.super_.constructor.call(this, subscribe); 325 | } 326 | 327 | ConnectableObservable.prototype.connect = function () { return this.connect(); }; 328 | ConnectableObservable.prototype.refCount = function () { 329 | var connectableSubscription = null, 330 | count = 0, 331 | source = this; 332 | return new AnonymousObservable(function (observer) { 333 | var shouldConnect, subscription; 334 | count++; 335 | shouldConnect = count === 1; 336 | subscription = source.subscribe(observer); 337 | if (shouldConnect) { 338 | connectableSubscription = source.connect(); 339 | } 340 | return disposableCreate(function () { 341 | subscription.dispose(); 342 | count--; 343 | if (count === 0) { 344 | connectableSubscription.dispose(); 345 | } 346 | }); 347 | }); 348 | }; 349 | 350 | return ConnectableObservable; 351 | }()); 352 | 353 | return root; 354 | })); -------------------------------------------------------------------------------- /lib/rx.binding.min.js: -------------------------------------------------------------------------------- 1 | /*** @preserve Copyright (c) Microsoft Corporation. All rights reserved. 2 | * This code is licensed by Microsoft Corporation under the terms 3 | * of the MICROSOFT REACTIVE EXTENSIONS FOR JAVASCRIPT AND .NET LIBRARIES License. 4 | * See http://go.microsoft.com/fwlink/?LinkID=220762. 5 | */ 6 | (function(e,t){var n=typeof exports=="object"&&exports&&(typeof e=="object"&&e&&e==e.global&&(window=e),exports);typeof define=="function"&&define.amd?define(["rx","exports"],function(n,r){return e.Rx=t(e,r,n),e.Rx}):typeof module=="object"&&module&&module.exports==n?module.exports=t(e,module.exports,require("./rx")):e.Rx=t(e,{},e.Rx)})(this,function(e,t,n,r){function y(){if(this.isDisposed)throw new Error(g)}var i=n.Observable,s=i.prototype,o=n.Internals.AnonymousObservable,u=n.Subject,a=n.AsyncSubject,f=n.Observer,l=n.Internals.ScheduledObserver,c=n.Disposable.create,h=n.Disposable.empty,p=n.CompositeDisposable,d=n.Scheduler.currentThread,v=n.Internals.inherits,m=n.Internals.addProperties,g="Object has been disposed";s.multicast=function(e,t){var n=this;return typeof e=="function"?new o(function(r){var i=n.multicast(e());return new p(t(i).subscribe(r),i.connect())}):new S(n,e)},s.publish=function(e){return e?this.multicast(function(){return new u},e):this.multicast(new u)},s.publishLast=function(e){return e?this.multicast(function(){return new a},e):this.multicast(new a)},s.publishValue=function(e,t){return arguments.length===2?this.multicast(function(){return new w(t)},e):this.multicast(new w(e))},s.replay=function(e,t,n,r){return e?this.multicast(function(){return new E(t,n,r)},e):this.multicast(new E(t,n,r))};var b=function(e,t){this.subject=e,this.observer=t};b.prototype.dispose=function(){if(!this.subject.isDisposed&&this.observer!==null){var e=this.subject.observers.indexOf(this.observer);this.subject.observers.splice(e,1),this.observer=null}};var w=n.BehaviorSubject=function(){function e(e){var t;return y.call(this),this.isStopped?(t=this.exception,t?e.onError(t):e.onCompleted(),h):(this.observers.push(e),e.onNext(this.value),new b(this,e))}function t(n){t.super_.constructor.call(this,e),this.value=n,this.observers=[],this.isDisposed=!1,this.isStopped=!1,this.exception=null}return v(t,i),m(t.prototype,f,{onCompleted:function(){y.call(this);if(!this.isStopped){var e=this.observers.slice(0);this.isStopped=!0;for(var t=0,n=e.length;tthis.bufferSize)this.q.shift();while(this.q.length>0&&e-this.q[0].interval>this.window)this.q.shift()},onNext:function(e){var t;y.call(this);if(!this.isStopped){var n=this.scheduler.now();this.q.push({interval:n,value:e}),this._trim(n);var r=this.observers.slice(0);for(var i=0,s=r.length;i=e)return n}r=e|1;while(r=0;o=this.entries[o].next)if(this.entries[o].hashCode===i&&this.comparer(this.entries[o].key,e)){if(n)throw b;this.entries[o].value=t;return}if(this.freeCount>0){var u=this.freeList;this.freeList=this.entries[u].next,--this.freeCount}else this.size===this.entries.length&&(this._resize(),s=i%this.buckets.length),u=this.size,++this.size;this.entries[u].hashCode=i,this.entries[u].next=this.buckets[s],this.entries[u].key=e,this.entries[u].value=t,this.buckets[s]=u},T.prototype._resize=function(){var e=E(this.size*2),t=new Array(e);for(r=0;r=0;s=this.entries[s].next){if(this.entries[s].hashCode===t&&this.comparer(this.entries[s].key,e))return i<0?this.buckets[n]=this.entries[s].next:this.entries[i].next=this.entries[s].next,this.entries[s].hashCode=-1,this.entries[s].next=this.freeList,this.entries[s].key=null,this.entries[s].value=null,this.freeList=s,++this.freeCount,!0;i=s}}return!1},T.prototype.clear=function(){var e,t;if(this.size<=0)return;for(e=0,t=this.buckets.length;e=0;n=this.entries[n].next)if(this.entries[n].hashCode===t&&this.comparer(this.entries[n].key,e))return n}return-1},T.prototype.count=function(){return this.size-this.freeCount},T.prototype.tryGetEntry=function(e){var t=this._findEntry(e);return t>=0?{key:this.entries[t].key,value:this.entries[t].value}:r},T.prototype.getValues=function(){var e=0,t=[];if(this.entries!==r)for(var n=0;n=0&&(t[e++]=this.entries[n].value);return t},T.prototype.get=function(e){var t=this._findEntry(e);if(t>=0)return this.entries[t].value;throw new Error(y)},T.prototype.set=function(e,t){this._insert(e,t,!1)},T.prototype.containskey=function(e){return this._findEntry(e)>=0},l.join=function(e,t,n,r){var i=this;return new h(function(o){var a=new s,f=!1,l=0,c=new T,h=!1,p=0,d=new T;return a.add(i.subscribe(function(e){var n,i,s=l++,h=new u,p,m;c.add(s,e),a.add(h),i=function(){return c.remove(s)&&c.count()===0&&f&&o.onCompleted(),a.remove(h)};try{n=t(e)}catch(g){o.onError(g);return}h.disposable(n.take(1).subscribe(v,o.onError.bind(o),function(){i()})),m=d.getValues();for(var y=0;y 0) { 115 | isOwner = !isAcquired; 116 | isAcquired = true; 117 | } 118 | if (isOwner) { 119 | m.setDisposable(scheduler.scheduleRecursive(function (self) { 120 | var work; 121 | if (q.length > 0) { 122 | work = q.shift(); 123 | } else { 124 | isAcquired = false; 125 | return; 126 | } 127 | var m1 = new SingleAssignmentDisposable(); 128 | d.add(m1); 129 | m1.setDisposable(work.subscribe(function (x) { 130 | observer.onNext(x); 131 | var result = null; 132 | try { 133 | result = selector(x); 134 | } catch (e) { 135 | observer.onError(e); 136 | } 137 | q.push(result); 138 | activeCount++; 139 | ensureActive(); 140 | }, observer.onError.bind(observer), function () { 141 | d.remove(m1); 142 | activeCount--; 143 | if (activeCount === 0) { 144 | observer.onCompleted(); 145 | } 146 | })); 147 | self(); 148 | })); 149 | } 150 | }; 151 | 152 | q.push(source); 153 | activeCount++; 154 | ensureActive(); 155 | return d; 156 | }); 157 | }; 158 | 159 | Observable.forkJoin = function () { 160 | var allSources = argsOrArray(arguments, 0); 161 | return observableCreateWithDisposable(function (subscriber) { 162 | var count = allSources.length; 163 | if (count === 0) { 164 | subscriber.onCompleted(); 165 | return disposableEmpty; 166 | } 167 | var group = new CompositeDisposable(), 168 | finished = false, 169 | hasResults = new Array(count), 170 | hasCompleted = new Array(count), 171 | results = new Array(count); 172 | 173 | for (var idx = 0; idx < count; idx++) { 174 | (function (i) { 175 | var source = allSources[i]; 176 | group.add(source.subscribe(function (value) { 177 | if (!finished) { 178 | hasResults[i] = true; 179 | results[i] = value; 180 | } 181 | }, function (e) { 182 | finished = true; 183 | subscriber.onError(e); 184 | group.dispose(); 185 | }, function () { 186 | if (!finished) { 187 | if (!hasResults[i]) { 188 | subscriber.onCompleted(); 189 | return; 190 | } 191 | hasCompleted[i] = true; 192 | for (var ix = 0; ix < count; ix++) { 193 | if (!hasCompleted[ix]) { 194 | return; 195 | } 196 | } 197 | finished = true; 198 | subscriber.onNext(results); 199 | subscriber.onCompleted(); 200 | } 201 | })); 202 | })(idx); 203 | } 204 | 205 | return group; 206 | }); 207 | }; 208 | 209 | observableProto.forkJoin = function (second, resultSelector) { 210 | var first = this; 211 | 212 | return observableCreateWithDisposable(function (observer) { 213 | var leftStopped = false, rightStopped = false, 214 | hasLeft = false, hasRight = false, 215 | lastLeft, lastRight, 216 | leftSubscription = new SingleAssignmentDisposable(), rightSubscription = new SingleAssignmentDisposable(); 217 | 218 | leftSubscription.setDisposable( 219 | first.subscribe(function (left) { 220 | hasLeft = true; 221 | lastLeft = left; 222 | }, function (err) { 223 | rightSubscription.dispose(); 224 | observer.onError(err); 225 | }, function () { 226 | leftStopped = true; 227 | if (rightStopped) { 228 | if (!hasLeft) { 229 | observer.onCompleted(); 230 | } else if (!hasRight) { 231 | observer.onCompleted(); 232 | } else { 233 | var result; 234 | try { 235 | result = resultSelector(lastLeft, lastRight); 236 | } catch (e) { 237 | observer.onError(e); 238 | return; 239 | } 240 | observer.onNext(result); 241 | observer.onCompleted(); 242 | } 243 | } 244 | }) 245 | ); 246 | 247 | rightSubscription.setDisposable( 248 | second.subscribe(function (right) { 249 | hasRight = true; 250 | lastRight = right; 251 | }, function (err) { 252 | leftSubscription.dispose(); 253 | observer.onError(err); 254 | }, function () { 255 | rightStopped = true; 256 | if (leftStopped) { 257 | if (!hasLeft) { 258 | observer.onCompleted(); 259 | } else if (!hasRight) { 260 | observer.onCompleted(); 261 | } else { 262 | var result; 263 | try { 264 | result = resultSelector(lastLeft, lastRight); 265 | } catch (e) { 266 | observer.onError(e); 267 | return; 268 | } 269 | observer.onNext(result); 270 | observer.onCompleted(); 271 | } 272 | } 273 | }) 274 | ); 275 | 276 | return new CompositeDisposable(leftSubscription, rightSubscription); 277 | }); 278 | }; 279 | 280 | return root; 281 | })); -------------------------------------------------------------------------------- /lib/rx.experimental.min.js: -------------------------------------------------------------------------------- 1 | /*** @preserve Copyright (c) Microsoft Corporation. All rights reserved. 2 | * This code is licensed by Microsoft Corporation under the terms 3 | * of the MICROSOFT REACTIVE EXTENSIONS FOR JAVASCRIPT AND .NET LIBRARIES License. 4 | * See http://go.microsoft.com/fwlink/?LinkID=220762. 5 | */ 6 | (function(e,t){var n=typeof exports=="object"&&exports&&(typeof e=="object"&&e&&e==e.global&&(window=e),exports);typeof define=="function"&&define.amd?define(["rx","exports"],function(n,r){return e.Rx=t(e,r,n),e.Rx}):typeof module=="object"&&module&&module.exports==n?module.exports=t(e,module.exports,require("./rx")):e.Rx=t(e,{},e.Rx)})(this,function(e,t,n,r){function w(e,t){return e.length===1&&Array.isArray(e[t])?e[t]:b.call(e)}function E(e,t){return new m(function(){var n;return v(function(){return e()?(n=t,!0):!1},function(){return n})})}var i=n.Observable,s=i.prototype,o=i.createWithDisposable,u=i.concat,a=i.defer,f=i.empty,l=n.Disposable.empty,c=n.Internals.BinaryObserver,h=n.CompositeDisposable,p=n.SerialDisposable,d=n.SingleAssignmentDisposable,v=n.Internals.Enumerator.create,m=n.Internals.Enumerable,g=m.forEach,y=n.Scheduler.immediate,b=Array.prototype.slice;s.letBind=function(e){return e(this)},i.ifThen=function(e,t,n){return a(function(){n||(n=f());if(n.now){var r=n;n=f(r)}return e()?t:n})},i.forIn=function(e,t){return g(e,t).concat()};var S=i.whileDo=function(e,t){return E(e,t).concat()};return s.doWhile=function(e){return u([this,S(e,this)])},i.switchCase=function(e,t,n){return a(function(){n||(n=f());if(n.now){var i=n;n=f(i)}var s=t[e()];return s!==r?s:n})},s.expand=function(e,t){t||(t=y);var n=this;return o(function(r){var i=[],s=new p,o=new h(s),u=0,a=!1,f=function(){var n=!1;i.length>0&&(n=!a,a=!0),n&&s.setDisposable(t.scheduleRecursive(function(t){var n;if(!(i.length>0)){a=!1;return}n=i.shift();var s=new d;o.add(s),s.setDisposable(n.subscribe(function(t){r.onNext(t);var n=null;try{n=e(t)}catch(s){r.onError(s)}i.push(n),u++,f()},r.onError.bind(r),function(){o.remove(s),u--,u===0&&r.onCompleted()})),t()}))};return i.push(n),u++,f(),o})},i.forkJoin=function(){var e=w(arguments,0);return o(function(t){var n=e.length;if(n===0)return t.onCompleted(),l;var r=new h,i=!1,s=new Array(n),o=new Array(n),u=new Array(n);for(var a=0;a 93 | /// Creates a pattern that matches the current plan matches and when the specified observable sequences has an available value. 94 | /// 95 | /// Observable sequence to match in addition to the current pattern. 96 | /// Pattern object that matches when all observable sequences in the pattern have an available value. 97 | var patterns = this.patterns.slice(0); 98 | patterns.push(other); 99 | return new Pattern(patterns); 100 | }; 101 | Pattern.prototype.then = function (selector) { 102 | /// 103 | /// Matches when all observable sequences in the pattern (specified using a chain of and operators) have an available value and projects the values. 104 | /// 105 | /// Selector that will be invoked with available values from the source sequences, in the same order of the sequences in the pattern. 106 | /// Plan that produces the projected values, to be fed (with other plans) to the when operator. 107 | return new Plan(this, selector); 108 | }; 109 | 110 | // Plan 111 | function Plan(expression, selector) { 112 | this.expression = expression; 113 | this.selector = selector; 114 | } 115 | Plan.prototype.activate = function (externalSubscriptions, observer, deactivate) { 116 | var self = this; 117 | var joinObservers = []; 118 | for (var i = 0, len = this.expression.patterns.length; i < len; i++) { 119 | joinObservers.push(planCreateObserver(externalSubscriptions, this.expression.patterns[i], observer.onError.bind(observer))); 120 | } 121 | var activePlan = new ActivePlan(joinObservers, function () { 122 | var result; 123 | try { 124 | result = self.selector.apply(self, arguments); 125 | } catch (exception) { 126 | observer.onError(exception); 127 | return; 128 | } 129 | observer.onNext(result); 130 | }, function () { 131 | for (var j = 0, jlen = joinObservers.length; j < jlen; j++) { 132 | joinObservers[j].removeActivePlan(activePlan); 133 | } 134 | deactivate(activePlan); 135 | }); 136 | for (i = 0, len = joinObservers.length; i < len; i++) { 137 | joinObservers[i].addActivePlan(activePlan); 138 | } 139 | return activePlan; 140 | }; 141 | 142 | function planCreateObserver(externalSubscriptions, observable, onError) { 143 | var entry = externalSubscriptions.get(observable); 144 | if (!entry) { 145 | var observer = new JoinObserver(observable, onError); 146 | externalSubscriptions.set(observable, observer); 147 | return observer; 148 | } 149 | return entry; 150 | } 151 | 152 | // Active Plan 153 | function ActivePlan(joinObserverArray, onNext, onCompleted) { 154 | var i, joinObserver; 155 | this.joinObserverArray = joinObserverArray; 156 | this.onNext = onNext; 157 | this.onCompleted = onCompleted; 158 | this.joinObservers = new Map(); 159 | for (i = 0; i < this.joinObserverArray.length; i++) { 160 | joinObserver = this.joinObserverArray[i]; 161 | this.joinObservers.set(joinObserver, joinObserver); 162 | } 163 | } 164 | 165 | ActivePlan.prototype.dequeue = function () { 166 | var values = this.joinObservers.getValues(); 167 | for (var i = 0, len = values.length; i < len; i++) { 168 | values[i].queue.shift(); 169 | } 170 | }; 171 | ActivePlan.prototype.match = function () { 172 | var firstValues, i, len, isCompleted, values, hasValues = true; 173 | for (i = 0, len = this.joinObserverArray.length; i < len; i++) { 174 | if (this.joinObserverArray[i].queue.length === 0) { 175 | hasValues = false; 176 | break; 177 | } 178 | } 179 | if (hasValues) { 180 | firstValues = []; 181 | isCompleted = false; 182 | for (i = 0, len = this.joinObserverArray.length; i < len; i++) { 183 | firstValues.push(this.joinObserverArray[i].queue[0]); 184 | if (this.joinObserverArray[i].queue[0].kind === 'C') { 185 | isCompleted = true; 186 | } 187 | } 188 | if (isCompleted) { 189 | this.onCompleted(); 190 | } else { 191 | this.dequeue(); 192 | values = []; 193 | for (i = 0; i < firstValues.length; i++) { 194 | values.push(firstValues[i].value); 195 | } 196 | this.onNext.apply(this, values); 197 | } 198 | } 199 | }; 200 | 201 | // Join Observer 202 | var JoinObserver = (function () { 203 | 204 | inherits(JoinObserver, AbstractObserver); 205 | 206 | function JoinObserver(source, onError) { 207 | JoinObserver.super_.constructor.call(this); 208 | this.source = source; 209 | this.onError = onError; 210 | this.queue = []; 211 | this.activePlans = []; 212 | this.subscription = new SingleAssignmentDisposable(); 213 | this.isDisposed = false; 214 | } 215 | 216 | JoinObserver.prototype.next = function (notification) { 217 | if (!this.isDisposed) { 218 | if (notification.kind === 'E') { 219 | this.onError(notification.exception); 220 | return; 221 | } 222 | this.queue.push(notification); 223 | var activePlans = this.activePlans.slice(0); 224 | for (var i = 0, len = activePlans.length; i < len; i++) { 225 | activePlans[i].match(); 226 | } 227 | } 228 | }; 229 | JoinObserver.prototype.error = noop; 230 | JoinObserver.prototype.completed = noop; 231 | 232 | JoinObserver.prototype.addActivePlan = function (activePlan) { 233 | this.activePlans.push(activePlan); 234 | }; 235 | JoinObserver.prototype.subscribe = function () { 236 | this.subscription.disposable(this.source.materialize().subscribe(this)); 237 | }; 238 | JoinObserver.prototype.removeActivePlan = function (activePlan) { 239 | var idx = this.activePlans.indexOf(activePlan); 240 | this.activePlans.splice(idx, 1); 241 | if (this.activePlans.length === 0) { 242 | this.dispose(); 243 | } 244 | }; 245 | JoinObserver.prototype.dispose = function () { 246 | JoinObserver.super_.dispose.call(this); 247 | if (!this.isDisposed) { 248 | this.isDisposed = true; 249 | this.subscription.dispose(); 250 | } 251 | }; 252 | return JoinObserver; 253 | } ()); 254 | 255 | // Observable extensions 256 | observableProto.and = function (right) { 257 | /// 258 | /// Creates a pattern that matches when both observable sequences have an available value. 259 | /// 260 | /// Observable sequence to match with the current sequence. 261 | /// Pattern object that matches when both observable sequences have an available value. 262 | return new Pattern([this, right]); 263 | }; 264 | observableProto.then = function (selector) { 265 | /// 266 | /// Matches when the observable sequence has an available value and projects the value. 267 | /// 268 | /// Selector that will be invoked for values in the source sequence. 269 | /// Plan that produces the projected values, to be fed (with other plans) to the when operator. 270 | return new Pattern([this]).then(selector); 271 | }; 272 | Observable.when = function () { 273 | /// 274 | /// Joins together the results from several patterns. 275 | /// 276 | /// A series of plans (specified as an Array of as a series of arguments) created by use of the Then operator on patterns. 277 | /// Observable sequence with the results form matching several patterns. 278 | var plans = argsOrArray(arguments, 0); 279 | return new AnonymousObservable(function (observer) { 280 | var activePlans = [], 281 | externalSubscriptions = new Map(), 282 | group, 283 | i, len, 284 | joinObserver, 285 | joinValues, 286 | outObserver; 287 | outObserver = observerCreate(observer.onNext.bind(observer), function (exception) { 288 | var values = externalSubscriptions.getValues(); 289 | for (var j = 0, jlen = values.length; j < jlen; j++) { 290 | values[j].onError(exception); 291 | } 292 | observer.onError(exception); 293 | }, observer.onCompleted.bind(observer)); 294 | try { 295 | for (i = 0, len = plans.length; i < len; i++) { 296 | activePlans.push(plans[i].activate(externalSubscriptions, outObserver, function (activePlan) { 297 | var idx = activePlans.indexOf(activePlan); 298 | activePlans.splice(idx, 1); 299 | if (activePlans.length === 0) { 300 | outObserver.onCompleted(); 301 | } 302 | })); 303 | } 304 | } catch (e) { 305 | observableThrow(e).subscribe(observer); 306 | } 307 | group = new CompositeDisposable(); 308 | joinValues = externalSubscriptions.getValues(); 309 | for (i = 0, len = joinValues.length; i < len; i++) { 310 | joinObserver = joinValues[i]; 311 | joinObserver.subscribe(); 312 | group.add(joinObserver); 313 | } 314 | return group; 315 | }); 316 | }; 317 | 318 | 319 | return root; 320 | })); -------------------------------------------------------------------------------- /lib/rx.joinpatterns.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve Copyright (c) Microsoft Corporation. All rights reserved. 3 | * This code is licensed by Microsoft Corporation under the terms 4 | * of the Microsoft Reference Source License (MS-RSL). 5 | * http://referencesource.microsoft.com/referencesourcelicense.aspx. 6 | */ 7 | 8 | (function (root, factory) { 9 | var freeExports = typeof exports == 'object' && exports && 10 | (typeof root == 'object' && root && root == root.global && (window = root), exports); 11 | 12 | // Because of build optimizers 13 | if (typeof define === 'function' && define.amd) { 14 | define(['rx', 'exports'], function (Rx, exports) { 15 | root.Rx = factory(root, exports, Rx); 16 | return root.Rx; 17 | }); 18 | } else if (typeof module == 'object' && module && module.exports == freeExports) { 19 | module.exports = factory(root, module.exports, require('./rx')); 20 | } else { 21 | root.Rx = factory(root, {}, root.Rx); 22 | } 23 | }(this, function (global, exp, root, undefined) { 24 | 25 | // Aliases 26 | var Observable = root.Observable, 27 | observableProto = Observable.prototype, 28 | AnonymousObservable = root.Internals.AnonymousObservable, 29 | observableThrow = Observable.throwException, 30 | observerCreate = root.Observer.create, 31 | SingleAssignmentDisposable = root.SingleAssignmentDisposable, 32 | CompositeDisposable = root.CompositeDisposable, 33 | AbstractObserver = root.Internals.AbstractObserver; 34 | 35 | // Defaults 36 | function defaultComparer(x, y) { return x === y; } 37 | function noop() { } 38 | 39 | // Utilities 40 | var inherits = root.Internals.inherits; 41 | var slice = Array.prototype.slice; 42 | function argsOrArray(args, idx) { 43 | return args.length === 1 && Array.isArray(args[idx]) ? 44 | args[idx] : 45 | slice.call(args); 46 | } 47 | 48 | // TODO: Replace with a real Map once finalized 49 | var Map = (function () { 50 | function Map() { 51 | this.keys = []; 52 | this.values = []; 53 | } 54 | 55 | Map.prototype['delete'] = function (key) { 56 | var i = this.keys.indexOf(key); 57 | if (i !== -1) { 58 | this.keys.splice(i, 1); 59 | this.values.splice(i, 1); 60 | } 61 | return i !== -1; 62 | }; 63 | 64 | Map.prototype.get = function (key, fallback) { 65 | var i = this.keys.indexOf(key); 66 | return i !== -1 ? this.values[i] : fallback; 67 | }; 68 | 69 | Map.prototype.set = function (key, value) { 70 | var i = this.keys.indexOf(key); 71 | if (i !== -1) { 72 | this.values[i] = value; 73 | } 74 | this.values[this.keys.push(key) - 1] = value; 75 | }; 76 | 77 | Map.prototype.size = function () { return this.keys.length; }; 78 | Map.prototype.has = function (key) { 79 | return this.keys.indexOf(key) !== -1; 80 | }; 81 | Map.prototype.getKeys = function () { return this.keys.slice(0); }; 82 | Map.prototype.getValues = function () { return this.values.slice(0); }; 83 | 84 | return Map; 85 | }()); 86 | 87 | // Pattern 88 | function Pattern(patterns) { 89 | this.patterns = patterns; 90 | } 91 | Pattern.prototype.and = function (other) { 92 | var patterns = this.patterns.slice(0); 93 | patterns.push(other); 94 | return new Pattern(patterns); 95 | }; 96 | Pattern.prototype.then = function (selector) { 97 | return new Plan(this, selector); 98 | }; 99 | 100 | // Plan 101 | function Plan(expression, selector) { 102 | this.expression = expression; 103 | this.selector = selector; 104 | } 105 | Plan.prototype.activate = function (externalSubscriptions, observer, deactivate) { 106 | var self = this; 107 | var joinObservers = []; 108 | for (var i = 0, len = this.expression.patterns.length; i < len; i++) { 109 | joinObservers.push(planCreateObserver(externalSubscriptions, this.expression.patterns[i], observer.onError.bind(observer))); 110 | } 111 | var activePlan = new ActivePlan(joinObservers, function () { 112 | var result; 113 | try { 114 | result = self.selector.apply(self, arguments); 115 | } catch (exception) { 116 | observer.onError(exception); 117 | return; 118 | } 119 | observer.onNext(result); 120 | }, function () { 121 | for (var j = 0, jlen = joinObservers.length; j < jlen; j++) { 122 | joinObservers[j].removeActivePlan(activePlan); 123 | } 124 | deactivate(activePlan); 125 | }); 126 | for (i = 0, len = joinObservers.length; i < len; i++) { 127 | joinObservers[i].addActivePlan(activePlan); 128 | } 129 | return activePlan; 130 | }; 131 | 132 | function planCreateObserver(externalSubscriptions, observable, onError) { 133 | var entry = externalSubscriptions.get(observable); 134 | if (!entry) { 135 | var observer = new JoinObserver(observable, onError); 136 | externalSubscriptions.set(observable, observer); 137 | return observer; 138 | } 139 | return entry; 140 | } 141 | 142 | // Active Plan 143 | function ActivePlan(joinObserverArray, onNext, onCompleted) { 144 | var i, joinObserver; 145 | this.joinObserverArray = joinObserverArray; 146 | this.onNext = onNext; 147 | this.onCompleted = onCompleted; 148 | this.joinObservers = new Map(); 149 | for (i = 0; i < this.joinObserverArray.length; i++) { 150 | joinObserver = this.joinObserverArray[i]; 151 | this.joinObservers.set(joinObserver, joinObserver); 152 | } 153 | } 154 | 155 | ActivePlan.prototype.dequeue = function () { 156 | var values = this.joinObservers.getValues(); 157 | for (var i = 0, len = values.length; i < len; i++) { 158 | values[i].queue.shift(); 159 | } 160 | }; 161 | ActivePlan.prototype.match = function () { 162 | var firstValues, i, len, isCompleted, values, hasValues = true; 163 | for (i = 0, len = this.joinObserverArray.length; i < len; i++) { 164 | if (this.joinObserverArray[i].queue.length === 0) { 165 | hasValues = false; 166 | break; 167 | } 168 | } 169 | if (hasValues) { 170 | firstValues = []; 171 | isCompleted = false; 172 | for (i = 0, len = this.joinObserverArray.length; i < len; i++) { 173 | firstValues.push(this.joinObserverArray[i].queue[0]); 174 | if (this.joinObserverArray[i].queue[0].kind === 'C') { 175 | isCompleted = true; 176 | } 177 | } 178 | if (isCompleted) { 179 | this.onCompleted(); 180 | } else { 181 | this.dequeue(); 182 | values = []; 183 | for (i = 0; i < firstValues.length; i++) { 184 | values.push(firstValues[i].value); 185 | } 186 | this.onNext.apply(this, values); 187 | } 188 | } 189 | }; 190 | 191 | // Join Observer 192 | var JoinObserver = (function () { 193 | 194 | inherits(JoinObserver, AbstractObserver); 195 | 196 | function JoinObserver(source, onError) { 197 | JoinObserver.super_.constructor.call(this); 198 | this.source = source; 199 | this.onError = onError; 200 | this.queue = []; 201 | this.activePlans = []; 202 | this.subscription = new SingleAssignmentDisposable(); 203 | this.isDisposed = false; 204 | } 205 | 206 | JoinObserver.prototype.next = function (notification) { 207 | if (!this.isDisposed) { 208 | if (notification.kind === 'E') { 209 | this.onError(notification.exception); 210 | return; 211 | } 212 | this.queue.push(notification); 213 | var activePlans = this.activePlans.slice(0); 214 | for (var i = 0, len = activePlans.length; i < len; i++) { 215 | activePlans[i].match(); 216 | } 217 | } 218 | }; 219 | JoinObserver.prototype.error = noop; 220 | JoinObserver.prototype.completed = noop; 221 | 222 | JoinObserver.prototype.addActivePlan = function (activePlan) { 223 | this.activePlans.push(activePlan); 224 | }; 225 | JoinObserver.prototype.subscribe = function () { 226 | this.subscription.disposable(this.source.materialize().subscribe(this)); 227 | }; 228 | JoinObserver.prototype.removeActivePlan = function (activePlan) { 229 | var idx = this.activePlans.indexOf(activePlan); 230 | this.activePlans.splice(idx, 1); 231 | if (this.activePlans.length === 0) { 232 | this.dispose(); 233 | } 234 | }; 235 | JoinObserver.prototype.dispose = function () { 236 | JoinObserver.super_.dispose.call(this); 237 | if (!this.isDisposed) { 238 | this.isDisposed = true; 239 | this.subscription.dispose(); 240 | } 241 | }; 242 | return JoinObserver; 243 | } ()); 244 | 245 | // Observable extensions 246 | observableProto.and = function (right) { 247 | return new Pattern([this, right]); 248 | }; 249 | observableProto.then = function (selector) { 250 | return new Pattern([this]).then(selector); 251 | }; 252 | Observable.when = function () { 253 | var plans = argsOrArray(arguments, 0); 254 | return new AnonymousObservable(function (observer) { 255 | var activePlans = [], 256 | externalSubscriptions = new Map(), 257 | group, 258 | i, len, 259 | joinObserver, 260 | joinValues, 261 | outObserver; 262 | outObserver = observerCreate(observer.onNext.bind(observer), function (exception) { 263 | var values = externalSubscriptions.getValues(); 264 | for (var j = 0, jlen = values.length; j < jlen; j++) { 265 | values[j].onError(exception); 266 | } 267 | observer.onError(exception); 268 | }, observer.onCompleted.bind(observer)); 269 | try { 270 | for (i = 0, len = plans.length; i < len; i++) { 271 | activePlans.push(plans[i].activate(externalSubscriptions, outObserver, function (activePlan) { 272 | var idx = activePlans.indexOf(activePlan); 273 | activePlans.splice(idx, 1); 274 | if (activePlans.length === 0) { 275 | outObserver.onCompleted(); 276 | } 277 | })); 278 | } 279 | } catch (e) { 280 | observableThrow(e).subscribe(observer); 281 | } 282 | group = new CompositeDisposable(); 283 | joinValues = externalSubscriptions.getValues(); 284 | for (i = 0, len = joinValues.length; i < len; i++) { 285 | joinObserver = joinValues[i]; 286 | joinObserver.subscribe(); 287 | group.add(joinObserver); 288 | } 289 | return group; 290 | }); 291 | }; 292 | 293 | return root; 294 | })); -------------------------------------------------------------------------------- /lib/rx.joinpatterns.min.js: -------------------------------------------------------------------------------- 1 | /*** @preserve Copyright (c) Microsoft Corporation. All rights reserved. 2 | * This code is licensed by Microsoft Corporation under the terms 3 | * of the MICROSOFT REACTIVE EXTENSIONS FOR JAVASCRIPT AND .NET LIBRARIES License. 4 | * See http://go.microsoft.com/fwlink/?LinkID=220762. 5 | */ 6 | (function(e,t){var n=typeof exports=="object"&&exports&&(typeof e=="object"&&e&&e==e.global&&(window=e),exports);typeof define=="function"&&define.amd?define(["rx","exports"],function(n,r){return e.Rx=t(e,r,n),e.Rx}):typeof module=="object"&&module&&module.exports==n?module.exports=t(e,module.exports,require("./rx")):e.Rx=t(e,{},e.Rx)})(this,function(e,t,n,r){function h(e,t){return e===t}function p(){}function m(e,t){return e.length===1&&Array.isArray(e[t])?e[t]:v.call(e)}function y(e){this.patterns=e}function b(e,t){this.expression=e,this.selector=t}function w(e,t,n){var r=e.get(t);if(!r){var i=new S(t,n);return e.set(t,i),i}return r}function E(e,t,n){var r,i;this.joinObserverArray=e,this.onNext=t,this.onCompleted=n,this.joinObservers=new g;for(r=0;r0&&u/v<2;};var k=function(u){for(var v=0;v0&&(o=n.now(),s+=r,s<=o&&(s=o+r)),t.onNext(i++),e(s)})})}function S(e,t){var n=b(e);return new o(function(e){return t.scheduleWithRelative(n,function(){e.onNext(0),e.onCompleted()})})}function x(e,t,n){return e===t?new o(function(e){return n.schedulePeriodicWithState(0,t,function(t){return e.onNext(t),t+1})}):u(function(){return E(n.now()+e,t,n)})}function C(e,t){var n=this;return new o(function(r){var i=!1,s=new p,o=null,u=[],a=!1,f;return f=n.materialize().timestamp(t).subscribe(function(n){var f,l;n.value.kind==="E"?(u=[],u.push(n),o=n.value.exception,l=!a):(u.push({value:n.value,timestamp:n.timestamp+e}),l=!i,i=!0),l&&(o!==null?r.onError(o):(f=new h,s.disposable(f),f.disposable(t.scheduleRecursiveWithRelative(e,function(e){var n,s,f,l;if(o!==null)return;a=!0;do f=null,u.length>0&&u[0].timestamp-t.now()<=0&&(f=u.shift().value),f!==null&&f.accept(r);while(f!==null);l=!1,s=0,u.length>0?(l=!0,s=Math.max(0,u[0].timestamp-t.now())):i=!1,n=o,a=!1,n!==null?r.onError(n):l&&e(s)}))))}),new d(f,s)})}function k(e,t){var n=this;return u(function(){var r=e-t.now();return C.call(n,r,t)})}function L(e,t){return new o(function(n){function o(){s&&(s=!1,n.onNext(i)),r&&n.onCompleted()}var r,i,s;return new d(e.subscribe(function(e){s=!0,i=e},n.onError.bind(n),function(){r=!0}),t.subscribe(o,n.onError.bind(n),o))})}var i=n.Observable,s=i.prototype,o=n.Internals.AnonymousObservable,u=i.defer,a=i.empty,f=i.throwException,l=i.fromArray,c=n.Scheduler.timeout,h=n.SingleAssignmentDisposable,p=n.SerialDisposable,d=n.CompositeDisposable,v=n.RefCountDisposable,m=n.Subject,g=n.Internals.BinaryObserver,y=n.Internals.addRef,b=n.Scheduler.normalize,T=i.interval=function(e,t){return t||(t=c),x(e,e,t)},N=i.timer=function(e,t,n){var i;return n||(n=c),t!==r&&typeof t=="number"?i=t:t!==r&&typeof t=="object"&&(n=t),e instanceof Date&&i===r?w(e.getTime(),n):e instanceof Date&&i!==r?(i=t,E(e.getTime(),i,n)):i===r?S(e,n):x(e,i,n)};return s.delay=function(e,t){return t||(t=c),e instanceof Date?k.call(this,e.getTime(),t):C.call(this,e,t)},s.throttle=function(e,t){t||(t=c);var n=this;return new o(function(r){var i=new p,s=!1,o=0,u,a=null;return u=n.subscribe(function(n){var u,f;s=!0,a=n,o++,u=o,f=new h,i.disposable(f),f.disposable(t.scheduleWithRelative(e,function(){s&&o===u&&r.onNext(a),s=!1}))},function(e){i.dispose(),r.onError(e),s=!1,o++},function(){i.dispose(),s&&r.onNext(a),r.onCompleted(),s=!1,o++}),new d(u,i)})},s.windowWithTime=function(e,t,n){var i=this,s;return t===r&&(s=e),n===r&&(n=c),typeof t=="number"?s=t:typeof t=="object"&&(s=e,n=t),new o(function(t){var r,o,u=s,a=e,f=[],l,c=new p,g=0;return o=new d(c),l=new v(o),r=function(){var e,i,o,p,d;o=new h,c.disposable(o),i=!1,e=!1,a===u?(i=!0,e=!0):a0&&s-i[0].interval>=e)r.onNext(i.shift().value)},r.onError.bind(r),function(){var n=t.now();while(i.length>0&&n-i[0].interval>=e)r.onNext(i.shift().value);r.onCompleted()})})},s.takeLastWithTime=function(e,t,n){return this.takeLastBufferWithTime(e,t).selectMany(function(e){return l(e,n)})},s.takeLastBufferWithTime=function(e,t){var n=this;return t||(t=c),new o(function(r){var i=[];return n.subscribe(function(n){var r=t.now();i.push({interval:r,value:n});while(i.length>0&&r-i[0].interval>=e)i.shift()},r.onError.bind(r),function(){var n=t.now(),s=[];while(i.length>0){var o=i.shift();n-o.interval<=e&&s.push(o.value)}r.onNext(s),r.onCompleted()})})},s.takeWithTime=function(e,t){var n=this;return t||(t=c),new o(function(r){var i=t.scheduleWithRelative(e,function(){r.onCompleted()});return new d(i,n.subscribe(r))})},s.skipWithTime=function(e,t){var n=this;return t||(t=c),new o(function(r){var i=!1,s=t.scheduleWithRelative(e,function(){i=!0}),o=n.subscribe(function(e){i&&r.onNext(e)},r.onError.bind(r),r.onCompleted.bind(r));return new d(s,o)})},s.skipUntilWithTime=function(e,t){t||(t=c);var n=this;return new o(function(r){var i=!1,s=t.scheduleWithAbsolute(e,function(){i=!0}),o=n.subscribe(function(e){i&&r.onNext(e)},r.onError.bind(r),r.onCompleted.bind(r));return new d(s,o)})},s.takeUntilWithTime=function(e,t){t||(t=c);var n=this;return new o(function(r){return new d(t.scheduleWithAbsolute(e,function(){r.onCompleted()}),n.subscribe(r))})},n}); -------------------------------------------------------------------------------- /readme: -------------------------------------------------------------------------------- 1 | !! 2 | !! WARNING: This repository is for teaching purposes ONLY. 3 | !! The software in this repository is COMPLETELY UNSUPPORTED. 4 | !! 5 | 6 | Learn Reactive Extensions for JavaScript 7 | 8 | The Reactive Extensions for JavaScript Koans walk you down the path in order to learn the Reactive Extensions for JavaScript. 9 | 10 | Based on the RxKoans: http://rxkoans.codeplex.com/ -------------------------------------------------------------------------------- /rxkoans.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Rx Koans 38 | 39 | 40 |

Reactive Extensions for JavaScript: Koans (RxJSKoans)

41 |

42 |
43 |

44 |
    45 |
    test markup, will be hidden
    46 | 47 | 48 | --------------------------------------------------------------------------------