├── .gitattributes ├── concurrent ├── index.html ├── index.js ├── state_1.js ├── state_2.js ├── styles.css ├── subject.js ├── asyncSubject_1.js └── asyncSubject_2.js ├── react-demos └── react-recompoes-demo │ ├── .nvmrc │ ├── public │ ├── favicon.ico │ ├── manifest.json │ └── index.html │ ├── src │ ├── index.js │ ├── App.test.js │ ├── index.css │ └── App.js │ ├── config │ ├── jest │ │ ├── fileTransform.js │ │ └── cssTransform.js │ ├── polyfills.js │ ├── paths.js │ ├── env.js │ ├── webpackDevServer.config.js │ └── webpack.config.dev.js │ ├── .gitignore │ ├── scripts │ ├── test.js │ ├── start.js │ └── build.js │ └── package.json ├── drag-drop ├── logo.png ├── custom.css ├── index.html └── dragdrop.js ├── mario ├── img │ ├── jump-left.gif │ ├── jump-right.gif │ ├── stand-left.gif │ ├── stand-right.gif │ ├── walk-left.gif │ └── walk-right.gif ├── custom.css ├── polyfills.js ├── index.html └── custom.js ├── crop ├── images │ └── leaf-twirl.jpg ├── custom.css ├── index.html ├── requestanimationframescheduler.js └── crop.js ├── docs ├── logo │ ├── RxJSplayground.jpg │ ├── RxJSplayground-logomark.png │ ├── RxJSplayground-logotype.png │ └── License ├── OBSERVABLE_PIPELINE.md ├── rxjs_ng_europe.js ├── SUBJECT.md └── OBSERVABLE.md ├── image-color-selector ├── image.png ├── style.css ├── index.html └── script.js ├── virtual-list ├── images │ ├── 0 │ │ ├── 822761.jpg │ │ ├── 829299.jpg │ │ ├── 829508.jpg │ │ ├── 830288.jpg │ │ ├── 840951.jpg │ │ ├── 841112.jpg │ │ ├── 841246.jpg │ │ ├── 841257.jpg │ │ ├── 841270.jpg │ │ ├── 841271.jpg │ │ ├── 841337.jpg │ │ ├── 850831.jpg │ │ └── 851778.jpg │ ├── 1 │ │ ├── 868813.jpg │ │ ├── 868989.jpg │ │ ├── 868992.jpg │ │ ├── 868994.jpg │ │ ├── 869023.jpg │ │ ├── 869096.jpg │ │ ├── 869203.jpg │ │ ├── 869237.jpg │ │ ├── 869380.jpg │ │ ├── 869382.jpg │ │ ├── 869386.jpg │ │ ├── 869476.jpg │ │ └── 869508.jpg │ ├── 2 │ │ ├── 869510.jpg │ │ ├── 869516.jpg │ │ ├── 869533.jpg │ │ ├── 869541.jpg │ │ ├── 869548.jpg │ │ ├── 872532.jpg │ │ ├── 925010.jpg │ │ ├── 925077.jpg │ │ ├── 928203.jpg │ │ ├── 929016.jpg │ │ ├── 930241.jpg │ │ ├── 1585264.jpg │ │ ├── 1585360.jpg │ │ └── 1641134.jpg │ └── 3 │ │ ├── 1712289.jpg │ │ ├── 1829772.jpg │ │ ├── 2036766.jpg │ │ ├── 2086307.jpg │ │ ├── 2091847.jpg │ │ ├── 2171006.jpg │ │ ├── 2171910.jpg │ │ ├── 2187391.jpg │ │ ├── 2316690.jpg │ │ ├── 2324402.jpg │ │ ├── 2325775.jpg │ │ ├── 2365903.jpg │ │ ├── 2371520.jpg │ │ └── 2372627.jpg ├── styles.css ├── index.html └── index.js ├── letter-count ├── custom.css ├── custom.js └── index.html ├── canvas-paint ├── custom.css ├── index.html └── custom.js ├── .editorconfig ├── animation ├── custom.css ├── index.html └── custom.js ├── infinite-scroll ├── styles.css ├── index.html └── index.js ├── .gitignore ├── real-time ├── custom.css ├── index.html └── custom.js ├── mouse-tracking ├── index.html └── script.js ├── follow-the-mouse ├── custom.css ├── index.html └── custom.js ├── CONTRIBUTING.md ├── LICENSE ├── smart-counter ├── index.html └── index.js ├── debt-calculator ├── index.html ├── style.css └── index.js ├── typehead ├── index.html └── typehead.js ├── dynamic-render ├── index.html └── script.js ├── data-binding ├── index.html └── custom.js ├── github-fetch ├── index.html └── index.js ├── README.md ├── CODE_OF_CONDUCT.md └── common ├── js └── RxJS │ └── rx.selectors.js └── css └── bootstrap-responsive.min.css /.gitattributes: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /concurrent/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /concurrent/index.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /concurrent/state_1.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /concurrent/state_2.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /concurrent/styles.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /concurrent/subject.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /concurrent/asyncSubject_1.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /concurrent/asyncSubject_2.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/.nvmrc: -------------------------------------------------------------------------------- 1 | v8 2 | -------------------------------------------------------------------------------- /drag-drop/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/drag-drop/logo.png -------------------------------------------------------------------------------- /mario/img/jump-left.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/mario/img/jump-left.gif -------------------------------------------------------------------------------- /mario/img/jump-right.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/mario/img/jump-right.gif -------------------------------------------------------------------------------- /mario/img/stand-left.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/mario/img/stand-left.gif -------------------------------------------------------------------------------- /mario/img/stand-right.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/mario/img/stand-right.gif -------------------------------------------------------------------------------- /mario/img/walk-left.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/mario/img/walk-left.gif -------------------------------------------------------------------------------- /mario/img/walk-right.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/mario/img/walk-right.gif -------------------------------------------------------------------------------- /crop/images/leaf-twirl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/crop/images/leaf-twirl.jpg -------------------------------------------------------------------------------- /docs/logo/RxJSplayground.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/docs/logo/RxJSplayground.jpg -------------------------------------------------------------------------------- /image-color-selector/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/image-color-selector/image.png -------------------------------------------------------------------------------- /virtual-list/images/0/822761.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/822761.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/829299.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/829299.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/829508.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/829508.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/830288.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/830288.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/840951.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/840951.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/841112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/841112.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/841246.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/841246.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/841257.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/841257.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/841270.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/841270.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/841271.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/841271.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/841337.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/841337.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/850831.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/850831.jpg -------------------------------------------------------------------------------- /virtual-list/images/0/851778.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/0/851778.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/868813.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/868813.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/868989.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/868989.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/868992.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/868992.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/868994.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/868994.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/869023.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/869023.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/869096.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/869096.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/869203.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/869203.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/869237.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/869237.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/869380.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/869380.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/869382.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/869382.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/869386.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/869386.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/869476.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/869476.jpg -------------------------------------------------------------------------------- /virtual-list/images/1/869508.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/1/869508.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/869510.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/869510.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/869516.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/869516.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/869533.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/869533.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/869541.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/869541.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/869548.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/869548.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/872532.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/872532.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/925010.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/925010.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/925077.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/925077.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/928203.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/928203.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/929016.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/929016.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/930241.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/930241.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/1585264.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/1585264.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/1585360.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/1585360.jpg -------------------------------------------------------------------------------- /virtual-list/images/2/1641134.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/2/1641134.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/1712289.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/1712289.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/1829772.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/1829772.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2036766.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2036766.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2086307.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2086307.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2091847.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2091847.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2171006.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2171006.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2171910.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2171910.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2187391.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2187391.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2316690.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2316690.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2324402.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2324402.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2325775.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2325775.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2365903.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2365903.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2371520.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2371520.jpg -------------------------------------------------------------------------------- /virtual-list/images/3/2372627.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/virtual-list/images/3/2372627.jpg -------------------------------------------------------------------------------- /docs/logo/RxJSplayground-logomark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/docs/logo/RxJSplayground-logomark.png -------------------------------------------------------------------------------- /docs/logo/RxJSplayground-logotype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/docs/logo/RxJSplayground-logotype.png -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JayKan/RxJS-Playground/HEAD/react-demos/react-recompoes-demo/public/favicon.ico -------------------------------------------------------------------------------- /letter-count/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 10px; 3 | font-family: sans-serif; 4 | text-align: center; 5 | background-color: #CCFFCC; 6 | color: #006600;} 7 | } 8 | 9 | 10 | -------------------------------------------------------------------------------- /canvas-paint/custom.css: -------------------------------------------------------------------------------- 1 | canvas { 2 | background-color: #cccccb; 3 | } 4 | 5 | html { 6 | -moz-user-select: none; 7 | -khtml-user-select: none; 8 | -webkit-user-select: none; 9 | user-select: none; 10 | } 11 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | insert_final_newline = false 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /animation/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 20px; 3 | font-family: 'Helvetica Neue', arial, sans-serif; 4 | color: #333; 5 | } 6 | #drawing { 7 | width: 100%; 8 | height: 400px; 9 | border: #ddd 1px solid; 10 | } 11 | #displayqueue { 12 | text-align: center; 13 | font-family: 'Inconsolata', 'Menlo', 'Courier', monospace; 14 | color: #666; 15 | margin: 20px; 16 | } 17 | -------------------------------------------------------------------------------- /image-color-selector/style.css: -------------------------------------------------------------------------------- 1 | .actual-color{ 2 | height:50px; 3 | width:50px; 4 | } 5 | 6 | #sample{ 7 | cursor: crosshair; 8 | } 9 | 10 | .selected-colors{ 11 | list-style-type: none; 12 | width:50%; 13 | } 14 | 15 | .selected-colors li{ 16 | float: left; 17 | } 18 | 19 | .selected-colors div{ 20 | width: 25px; 21 | height: 25px; 22 | margin: 0 10px 10px 0; 23 | } -------------------------------------------------------------------------------- /drag-drop/custom.css: -------------------------------------------------------------------------------- 1 | #dragTarget { 2 | background-image: url(./logo.png); 3 | background-repeat: no-repeat; 4 | background-position: center; 5 | background-size: contain; 6 | height: 200px; 7 | width: 200px; 8 | background-color: #000000; 9 | border: 1px solid #666666; 10 | color: #ffffff; 11 | padding: 10px; 12 | position: absolute; 13 | cursor: move; 14 | } 15 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/en/webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/en/webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /infinite-scroll/styles.css: -------------------------------------------------------------------------------- 1 | #infinite-scroller { 2 | height: 500px; 3 | width: 700px; 4 | border: 1px solid #f5ad7c; 5 | overflow: scroll; 6 | padding: 0; 7 | } 8 | 9 | #infinite-scroller li { 10 | padding : 10px 5px; 11 | line-height: 1.5; 12 | } 13 | 14 | #infinite-scroller li:nth-child(odd) { 15 | background : #ffe8d8; 16 | } 17 | 18 | #infinite-scroller li:nth-child(even) { 19 | background : #f5ad7c; 20 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #--------------------------------- 2 | # Extensions 3 | #--------------------------------- 4 | *.gz 5 | *.log 6 | *.rar 7 | *.tar 8 | *.zip 9 | 10 | 11 | #--------------------------------- 12 | # IDE generated 13 | #--------------------------------- 14 | .idea/ 15 | .project 16 | *.iml 17 | 18 | 19 | #--------------------------------- 20 | # OS generated 21 | #--------------------------------- 22 | __MACOSX/ 23 | .DS_Store 24 | Thumbs.db 25 | -------------------------------------------------------------------------------- /infinite-scroll/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Naive Infinite Scroll in RxJS 7 | 8 | 9 | 10 |

Infinite Scroller for HackerNews titles

11 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /real-time/custom.css: -------------------------------------------------------------------------------- 1 | /* Originally from https://github.com/DanGorst/frp-with-bacon */ 2 | h1 { 3 | font-family: 'Helvetica'; 4 | font-size: 32px; 5 | } 6 | 7 | h3 { 8 | font-family: 'Helvetica'; 9 | font-size: 20px; 10 | } 11 | 12 | text { 13 | font-family: 'Helvetica'; 14 | font-size: 16px; 15 | } 16 | 17 | .axis path, .axis line { 18 | fill: none; 19 | stroke: #777; 20 | shape-rendering: crispEdges; 21 | } 22 | 23 | .axis text { 24 | font-family: 'Helvetica'; 25 | font-size: 14px; 26 | } 27 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/src/index.css: -------------------------------------------------------------------------------- 1 | *, *:before, *:after { 2 | box-sizing: inherit; 3 | } 4 | 5 | body, input, textarea, select, button { 6 | text-rendering: optimizeLegibility; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | html, body, #root { 12 | margin: 0; 13 | padding: 0; 14 | height: 100%; 15 | font-weight: 300; 16 | line-height: normal; 17 | box-sizing: content-box; 18 | font-family: 'Pompiere', cursive; 19 | } 20 | 21 | body, #root { 22 | position: relative; 23 | min-height: 100vh; 24 | } 25 | -------------------------------------------------------------------------------- /mouse-tracking/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RxJS mouse tracking example 5 | 6 | 7 | 8 | 9 | 10 |

Mouse tracking example

11 |

Hold mouse button down while moving the mouse and see coordinates

12 |

13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /follow-the-mouse/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #ffffff; 3 | padding: 10px; 4 | font-family: sans-serif; 5 | } 6 | 7 | li { 8 | padding: 5px; 9 | } 10 | 11 | .contrastBlock { 12 | background-color: #000000; 13 | border: 1px solid #666666; 14 | color: #ffffff; 15 | padding: 10px; 16 | } 17 | 18 | label { 19 | display: block; 20 | padding: 5px; 21 | } 22 | fieldset { 23 | border-style: solid; 24 | border-width: 3px; 25 | } 26 | 27 | .contrastBlock { 28 | position: absolute; 29 | } 30 | 31 | #theTail { 32 | color: #FF0000; 33 | } 34 | #theWagging { 35 | color: #FFFF00; 36 | } 37 | -------------------------------------------------------------------------------- /crop/custom.css: -------------------------------------------------------------------------------- 1 | #container { 2 | position: relative; 3 | } 4 | 5 | .canvasoverlays { 6 | position: absolute; 7 | } 8 | 9 | .handle { 10 | position: absolute; 11 | width: 20px; 12 | height: 20px; 13 | background-color: rgba(255,255,255,0.5); 14 | border: 1px solid rgba(0,0,0,0.5); 15 | user-select: none; 16 | } 17 | 18 | .handle#tr { 19 | margin-left: -20px; 20 | } 21 | 22 | .handle#t, .handle#b { 23 | margin-left: -10px; 24 | } 25 | 26 | .handle#r, .handle#l { 27 | margin-top: -10px; 28 | } 29 | 30 | .handle#br { 31 | margin-left: -20px; 32 | margin-top: -20px; 33 | } 34 | 35 | .handle#bl { 36 | margin-top: -20px; 37 | } 38 | -------------------------------------------------------------------------------- /letter-count/custom.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 3 | 'use strict'; 4 | 5 | var countElement = document.getElementById('toCount'); 6 | var resultElement = document.getElementById('result'); 7 | var result2 = document.getElementById('result2'); 8 | 9 | var source = Rx.Observable.fromEvent(countElement, 'keyup') 10 | .map(function(e){ 11 | return 'length: ' + e.target.value.length; 12 | }) 13 | .distinctUntilChanged(); 14 | 15 | function setHtml(text){ 16 | this.innerHTML = text; 17 | } 18 | 19 | source.subscribe(function(text){ 20 | resultElement.innerHTML = text; 21 | }); 22 | 23 | source.subscribe( setHtml.bind( result2 ) ); 24 | 25 | })(); -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for your interest in contributing! Feel free to put up a PR for any issue or feature request. Please follow blow steps to begin your contribution :) 4 | 5 | ## Setup 6 | 1. `Fork` the repo 7 | 2. `Clone` your fork 8 | 3. Make a branch for your bug fix or adding a new example 9 | 4. Work your magic and make your changes 10 | 5. Commit your changes 11 | 6. Push your branch to your fork 12 | 7. Create a pull request from your branch on your fork to `master` on this repo 13 | 8. Have your branch get merged in and celebrate 🎉 🎊 14 | 15 | If you experience any issues or problems at any point, please don't hesitate to file an issue or send me a message! -------------------------------------------------------------------------------- /mouse-tracking/script.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var mouseUpStream = Rx.Observable.fromEvent(document,"mouseup"); 3 | var mouseMoveStream = Rx.Observable.fromEvent(document,"mousemove") 4 | .takeUntil(mouseUpStream); 5 | 6 | 7 | var mouseDownStream = Rx.Observable.fromEvent(document,"mousedown") 8 | .flatMap(function(){ 9 | return mouseMoveStream; 10 | }) 11 | .map(function(event){ 12 | return { x: event.clientX, y: event.clientY}; 13 | }); 14 | 15 | var cntLabel = document.querySelector("h1"); 16 | 17 | mouseDownStream.subscribe(function(result){ 18 | cntLabel.textContent = "x: " + result.x + " , y: "+ result.y; 19 | }); 20 | 21 | }()); -------------------------------------------------------------------------------- /image-color-selector/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 |

Image sampler example

13 |

Hover image to check color, click on image to add to the samples

14 | 15 | 16 |

Hovered color

17 | 18 |
19 |
20 | 21 |

Selected color samples

22 |
23 |
    24 | 25 |
26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /mario/custom.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial; 3 | font-size: 14px; 4 | color: #505050; 5 | padding: 20px; 6 | margin: 0; 7 | background-color: rgba(174, 238, 238, 1); 8 | } 9 | 10 | nav ul { 11 | list-style: none; 12 | margin: 0; 13 | padding: 0; 14 | } 15 | 16 | nav ul li { 17 | margin: 4px 0; 18 | } 19 | 20 | a { 21 | text-decoration: underline; 22 | color: inherit; 23 | text-decoration-style: double; 24 | } 25 | 26 | .arrows-keys { 27 | text-decoration: underline; 28 | text-decoration-style: wavy; 29 | line-height: 1.6; 30 | color: black; 31 | font-size: 15px; 32 | } 33 | #grass { 34 | position: fixed; 35 | background-color: rgba(74, 163, 41, 1); 36 | left: 0; 37 | right: 0; 38 | bottom: 0; 39 | height: 60px; 40 | } 41 | 42 | #mario { 43 | position: fixed; 44 | } -------------------------------------------------------------------------------- /animation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | RxJS Animation Test 10 | 11 | 12 | 13 | 14 |

RxJS Animation Fun

15 |
16 |

[x,y] tuples for each square:

17 |
18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | let argv = process.argv.slice(2); 20 | 21 | // Watch unless on CI or in coverage mode 22 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 23 | argv.push('--watch'); 24 | } 25 | 26 | 27 | jest.run(argv); 28 | -------------------------------------------------------------------------------- /letter-count/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | RxJS Simple Letter Count Example 6 | 7 | 8 | 9 | 10 | 11 |
12 | 16 |
17 |

Text buffer:

18 |

Start Typing!

19 |

20 |
21 |
22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /real-time/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | d3 Real-time with RxJS Fun! 9 | 10 | 11 | 12 | 13 |

Wikipedia Updates using RxJS

14 |

15 | Originally from https://github.com/DanGorst/frp-with-bacon 16 |

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | 18 | // In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet. 19 | // We don't polyfill it in the browser--this is user's responsibility. 20 | if (process.env.NODE_ENV === 'test') { 21 | require('raf').polyfill(global); 22 | } 23 | -------------------------------------------------------------------------------- /drag-drop/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | RxJS Drag and Drop Example! 10 | 11 | 12 | 13 | 14 | 15 | 16 |
Drag Me!
17 |
18 | 22 |
23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/logo/License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Baran Pirinçal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /virtual-list/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | /*overflow: hidden;*/ 3 | } 4 | 5 | .border { 6 | border: 1px solid black; 7 | } 8 | 9 | .outer { 10 | overflow: hidden; 11 | /*overflow: none;*/ 12 | position: absolute; 13 | left: 0; 14 | right: 0; 15 | top: 50%; 16 | margin-top: -85px; 17 | height: 170px; 18 | } 19 | 20 | .inner { 21 | position: absolute; 22 | overflow: none; 23 | box-sizing: border-box; 24 | border-color: red; 25 | border-width: 2px; 26 | left: 0; 27 | right: 0; 28 | top: 20px; 29 | bottom: 15px; 30 | padding: 5px 0 5px 0; 31 | transform-style: preserve-3d; 32 | transform: translate3d(0, 0, 0); 33 | -webkit-transform: translate3d(0, 0, 0); 34 | white-space: nowrap; 35 | } 36 | 37 | .box-art { 38 | box-sizing: border-box; 39 | display: inline-block; 40 | background-repeat: no-repeat; 41 | margin: 0 5px 0 5px; 42 | width: 90px; 43 | height: 120px; 44 | } 45 | 46 | .box-art img { 47 | position: absolute; 48 | width: 90px; 49 | height: 120px; 50 | pointer-events: none; 51 | } 52 | -------------------------------------------------------------------------------- /mario/polyfills.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | 'use strict'; 3 | 4 | if (!Object.assign) { 5 | Object.defineProperty(Object, "assign", { 6 | enumerable: false, 7 | configurable: true, 8 | writable: true, 9 | value: function(target, firstSource) { 10 | "use strict"; 11 | if (target === undefined || target === null) { 12 | throw new TypeError("Cannot convert first argument to object"); 13 | } 14 | var to = Object(target); 15 | for (var i = 1; i < arguments.length; i++) { 16 | var nextSource = arguments[i]; 17 | if (nextSource === undefined || nextSource === null) continue; 18 | var keysArray = Object.keys(Object(nextSource)); 19 | for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { 20 | var nextKey = keysArray[nextIndex]; 21 | var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); 22 | if (desc !== undefined && desc.enumerable) { to[nextKey] = nextSource[nextKey]; } 23 | } 24 | } 25 | return to; 26 | } 27 | }); 28 | } 29 | }()); 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Jay Kan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /follow-the-mouse/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Rx for JavaScript Rocks! 10 | 11 | 12 | 13 | 14 | 15 | 16 |
the mouse!
17 |
its tail!
18 |
is happy!
19 | 20 |
21 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /virtual-list/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |
23 | 24 | -------------------------------------------------------------------------------- /canvas-paint/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | RxJS Canvas Paint Example Rocks! 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 |
19 | 23 |
24 | 25 |
26 |
27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /crop/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Rx for JavaScript Rocks! 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/src/App.js: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | // import { Observable } from 'rxjs'; 3 | // import config from 'recompose/rxjsObservableConfig'; 4 | // import { 5 | // setObservableConfig, 6 | // componentFromStream 7 | // } from 'recompose'; 8 | 9 | // setObservableConfig(config); 10 | 11 | // // class App extends Component { 12 | // // render() { 13 | // // return ( 14 | // //
15 | // //

16 | // // Hello World 17 | // //

18 | // //
19 | // // ); 20 | // // } 21 | // // } 22 | 23 | // const App = componentFromStream(props$ => { 24 | // return Observable.interval(1000).map(i => ( 25 | //
{i}
26 | // )); 27 | // }); 28 | 29 | // export default App; 30 | import React from "react" 31 | import { render } from "react-dom" 32 | import { Observable } from "rxjs" 33 | import config from "recompose/rxjsObservableConfig" 34 | import { 35 | setObservableConfig, 36 | componentFromStream 37 | } from "recompose" 38 | 39 | setObservableConfig(config) 40 | 41 | const App = componentFromStream(props$ => { 42 | return Observable.interval(1000).map(i => ( 43 |
{i}
44 | )) 45 | }) 46 | 47 | export default App; 48 | -------------------------------------------------------------------------------- /smart-counter/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Smart Counter in Rx5 7 | 37 | 38 | 39 | 40 |
41 |
42 |

Smoothly transitions numbers with ease!

43 | 44 | 45 |

46 |
47 |
48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /mario/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Mario in RxJS Rocks! 10 | 11 | 12 | 13 | 14 |
15 |

RxJS - Mario

16 |

Originally was developed by Daniel Fudala on GitHub

17 |

Use arrow keys (←, →, ↑, ↓) to move around your Mario!

18 |

View Source Source

19 |
20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /debt-calculator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Simple Debt Calculator 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 | -------------------------------------------------------------------------------- /smart-counter/index.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | 'use strict'; 3 | 4 | var input = document.getElementById('range'); 5 | var updateButton = document.getElementById('update'); 6 | 7 | // Utility functions 8 | function takeUntilFunc(endRange, currentNumber) { 9 | return endRange > currentNumber 10 | ? function (val) { return val <= endRange; } 11 | : function (val) { return val >= endRange; } 12 | } 13 | 14 | function positiveOrNegative(endRange, currentNumber) { 15 | return endRange > currentNumber ? 1 : -1; 16 | } 17 | 18 | function updateHTML(id) { 19 | return function (val) { 20 | return document.getElementById(id).innerHTML = val; 21 | }; 22 | } 23 | 24 | // Creating subscription 25 | var subscription = (function(currentNumber) { 26 | return Rx.Observable 27 | .fromEvent(updateButton, 'click') 28 | .map(function() { return parseInt(input.value); }) 29 | .switchMap(function(endRange) { 30 | return Rx.Observable.timer(0, 20) 31 | .mapTo(positiveOrNegative(endRange, currentNumber)) 32 | .startWith(currentNumber) 33 | .scan(function(acc, curr) { 34 | return acc + curr; 35 | }) 36 | .takeWhile(takeUntilFunc(endRange, currentNumber)); 37 | }) 38 | .do(function(v) { 39 | return currentNumber = v; 40 | }) 41 | .startWith(currentNumber) 42 | .subscribe(updateHTML('display')); 43 | })(0); 44 | })(); -------------------------------------------------------------------------------- /drag-drop/dragdrop.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 3 | 'use strict'; 4 | 5 | function main () { 6 | 7 | var targetElement = document.getElementById('dragTarget'); 8 | 9 | /* 10 | Get the three major mouseEvents we'll be observing. 11 | */ 12 | var mouseUp = Rx.Observable.fromEvent( targetElement, 'mouseup' ); 13 | var mouseMove = Rx.Observable.fromEvent( document, 'mousemove' ); 14 | var mouseDown = Rx.Observable.fromEvent( targetElement, 'mousedown' ); 15 | 16 | var mouseDrag = mouseDown.flatMap(function (md) { 17 | 18 | /* 19 | Calculates the offsets when mouse starting moving down 20 | */ 21 | var startX = md.offsetX, 22 | startY = md.offsetY; 23 | 24 | /* 25 | Calculates delta with mouseMove events 26 | until mouseUp events are done. 27 | */ 28 | return mouseMove.map(function (mm) { 29 | mm.preventDefault(); 30 | 31 | return { 32 | left: mm.clientX - startX, 33 | top: mm.clientY - startY 34 | }; 35 | }).takeUntil(mouseUp); 36 | }); 37 | 38 | function updateElementPosition(position){ 39 | this.style.top = position.top + 'px'; 40 | this.style.left = position.left + 'px'; 41 | } 42 | 43 | /* 44 | Updates drag&drop Element position. 45 | */ 46 | mouseDrag.subscribe( 47 | updateElementPosition.bind( targetElement ) 48 | ); 49 | } 50 | 51 | main(); 52 | 53 | })(); -------------------------------------------------------------------------------- /typehead/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | RxJS TypeHead Example 9 | 10 | 11 | 12 | 13 | 14 |
15 | 19 | 20 |
21 |
22 |
23 | 24 | 29 |
30 |
31 |
32 | 33 |
34 |
    35 |
    36 |
    37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /dynamic-render/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dynamic Rendering Widget Demo 7 | 12 | 13 | 14 |
    15 | 19 |
    20 |
    21 | 25 |
    26 |
    27 | 31 |
    32 |
    33 | 37 |
    38 |
    39 | 43 |
    44 |
    45 | 49 |
    50 |
    51 | 55 |
    56 |
    57 | 58 |
    59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /data-binding/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Rx for JavaScript Rocks! 10 | 11 | 12 | 13 | 14 | 15 |
    16 | 20 |
    21 |
    22 |
    23 | Simple Form Data 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
    34 |
    35 |
    36 |
    37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /follow-the-mouse/custom.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 3 | 'use strict'; 4 | 5 | function extractClientX(e) { return e.clientX; } 6 | function extractClientY(e) { return e.clientY; } 7 | function setLeft(x) { this.style.left = x + 'px'; } 8 | function setTop(y) { this.style.top = y + 'px'; } 9 | function add(x, y) { return x + y } 10 | function partialAdd(x) { return add.bind(null, x); } 11 | function randomize() { return Math.round(10 * Math.random() -5); } 12 | 13 | var delay = 300; 14 | 15 | var mouseMove = Rx.Observable.fromEvent(document, 'mousemove'); 16 | var left = mouseMove.map(extractClientX); 17 | var top = mouseMove.map(extractClientY); 18 | 19 | // Update the mouse 20 | var theMouse = document.querySelector('#theMouse'); 21 | left.subscribe(setLeft.bind(theMouse)); 22 | top.subscribe(setTop.bind(theMouse)); 23 | 24 | // Update the tail 25 | var mouseOffset = theMouse.offsetWidth; 26 | var theTail = document.querySelector('#theTail'); 27 | left 28 | .map(partialAdd(mouseOffset)) 29 | .delay(delay) 30 | .subscribe(setLeft.bind(theTail)) 31 | ; 32 | top 33 | .delay(delay) 34 | .subscribe(setTop.bind(theTail)) 35 | ; 36 | 37 | // Update the wagging 38 | var wagDelay = delay * 1.5; 39 | var wagging = document.querySelector('#theWagging'); 40 | var mouseAndTailOffset = mouseOffset + theTail.offsetWidth; 41 | left 42 | .map(partialAdd(mouseAndTailOffset)) 43 | .delay(wagDelay) 44 | .subscribe(setLeft.bind(wagging)) 45 | ; 46 | var waggingDelay = Rx.Observable 47 | .interval(100) 48 | .map(randomize) 49 | ; 50 | 51 | top 52 | .delay(wagDelay) 53 | .combineLatest(waggingDelay, add) 54 | .subscribe(setTop.bind(wagging)) 55 | ; 56 | 57 | })(); -------------------------------------------------------------------------------- /docs/OBSERVABLE_PIPELINE.md: -------------------------------------------------------------------------------- 1 | # Observable Pipeline 2 | > An Observable pipeline is a technique that combines a group of operators chained together, where each one takes an Observable as input and returns an Observable as output. 3 | 4 | ### Pipeline Example 5 | ```javascript 6 | Rx.Observable 7 | .from(1, 2, 3, 4, 5, 6, 7, 8) 8 | .filter(function(val) { return val % 2 }) 9 | .map(function(val) { return val * 10 }); 10 | ``` 11 | Pipelines are self-contained. All state flows from one operator to the next, without the need for any external variables. As a result, the operators in a pipeline should always use pure functions to avoid any side effects. 12 | 13 | Pure functions always return the same output given the same input. It's easier to design programs with high concurrency when we can guarantee that a function in the program can't modify state other functions rely on. 14 | 15 | ### Pipelines are efficient 16 | ```javascript 17 | stringObservalbe // represents an observable emitting 1,000 strings 18 | .map(function(str) { 19 | return str.toUpperCase(); 20 | }) 21 | .filter(function(str) { 22 | return /^[A-Z]+$/.test(str); 23 | }) 24 | .subscribe(function(str) { 25 | console.log(str); 26 | }); 27 | ``` 28 | 29 | Observable pipelines look extremely similar to array chains, but their similarities end here. In an Observable, nothing ever happens until we **subscribe** to it, no matter how many queries and transformations we apply to it. When we chain a transformation like *map*, we're composing a single function that will operate on every item of the array once. 30 | 31 | With Observables, we'll ONLY go through our array list once, and we'll apply the transformations only if absolutely required. This way of operating is called *lazy evaluation*, and it is very common in functional languages such as Haskell and Miranda. 32 | 33 | -------------------------------------------------------------------------------- /docs/rxjs_ng_europe.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function map(transformFn) { 4 | const inputObservable = this; 5 | const outputObservable = createObservable(function subscribe(outputObserver) { 6 | inputObservable.subscribe({ 7 | next: function(x) { 8 | const y = transformFn(x); 9 | outputObserver.next(y); 10 | }, 11 | error: function(e) { 12 | outputObserver.error(e); 13 | }, 14 | complete: function () { 15 | outputObserver.complete(); 16 | } 17 | }) 18 | }); 19 | return outputObservable; 20 | } 21 | 22 | function filter(conditionFn) { 23 | const inputObservable = this; 24 | const outputObservable = createObservable(function subscribe(outputObserver) { 25 | inputObservable.subscribe({ 26 | next: function(x) { 27 | if (conditionFn(x)) { 28 | outputObserver.next(x); 29 | } 30 | }, 31 | error: function(e) { 32 | outputObserver.error(e); 33 | }, 34 | complete: function () { 35 | outputObserver.complete(); 36 | } 37 | }) 38 | }); 39 | return outputObservable; 40 | } 41 | 42 | 43 | function createObservable(subscribe) { 44 | return { 45 | subscribe: subscribe, 46 | map: map, 47 | filter: filter 48 | }; 49 | } 50 | 51 | const arrayObservable = createObservable(function(observer) { 52 | [10, 20, 30].forEach(observer.next); 53 | observer.complete(); 54 | }); 55 | 56 | const observer = { 57 | next: function nextCallback(data) { 58 | console.log(data); 59 | }, 60 | error: function errorCallback(err) { 61 | console.error(err); 62 | }, 63 | complete: function completeCallback() { 64 | console.log('done'); 65 | } 66 | }; 67 | 68 | 69 | arrayObservable 70 | .map(function(x) { return x / 10; }) 71 | .filter(function (x) { return x !== 2; }) 72 | .subscribe(observer); 73 | 74 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | 23 | React App 24 | 25 | 26 | 29 |
    30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /image-color-selector/script.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | // find DOM elements 3 | var sampleImage = document.querySelector("#sample"); 4 | var selectedColorsList = document.querySelector(".selected-colors"); 5 | var actualColorDiv = document.querySelector(".actual-color"); 6 | var actualColorLabel = document.querySelector(".actual-color-label"); 7 | var canvas = document.createElement("canvas"); 8 | 9 | init(); 10 | 11 | var mouseMoveStream = Rx.Observable.fromEvent(sampleImage, "mousemove") 12 | .map(retrieveColorFromEvent); 13 | 14 | var clickStream = Rx.Observable.fromEvent(sampleImage, "mousedown") 15 | .map(retrieveColorFromEvent).distinctUntilChanged(); 16 | 17 | mouseMoveStream.subscribe(function (color) { 18 | addActualColor(color); 19 | }); 20 | 21 | clickStream.subscribe(function (color) { 22 | addSelectedColor(color); 23 | }); 24 | 25 | function init(){ 26 | canvas.width = sampleImage.clientWidth; 27 | canvas.height = sampleImage.clientHeight; 28 | } 29 | 30 | function retrieveColorFromEvent(event) { 31 | var point = { 32 | x: event.offsetX, 33 | y: event.offsetY 34 | }; 35 | 36 | canvas.getContext('2d').drawImage(sampleImage, 0, 0, canvas.width, canvas.height); 37 | var pixelData = canvas.getContext('2d').getImageData(point.x, point.y, 1, 1).data; 38 | 39 | return "rgba(" + pixelData[0] + "," + pixelData[1] + "," + pixelData[2] + "," + pixelData[3] / 255 + ")"; 40 | } 41 | 42 | function addActualColor(color) { 43 | actualColorDiv.style.backgroundColor = color; 44 | actualColorLabel.innerHTML = color; 45 | } 46 | 47 | function addSelectedColor(color) { 48 | selectedColorsList.innerHTML += '
  • '; 49 | } 50 | }()); 51 | -------------------------------------------------------------------------------- /infinite-scroll/index.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | 'use strict'; 3 | 4 | var currentPage = 1; 5 | var scrollElem = document.getElementById('infinite-scroller'); 6 | var scrollEvent$ = Rx.Observable.fromEvent(scrollElem, 'scroll'); 7 | 8 | function getQuotesAPI() { 9 | return 'https://node-hnapi.herokuapp.com/news?page=' + currentPage; 10 | } 11 | 12 | // Check if the user is scrolling down by previous 13 | // scroll position and current scroll position 14 | function isUserScrollingDown(positions) { 15 | return positions[0].sT < positions[1].sT 16 | } 17 | 18 | // Check if the scroll position at required percentage 19 | function isScrollExpectedPercent(position, percent) { 20 | return ((position.sT + position.cH) / position.sH) > (percent / 100); 21 | } 22 | 23 | // Render each news on the view 24 | function renderNews(news) { 25 | var li = document.createElement('li'); 26 | li.innerHTML = [news.id, news.title].join(' - '); 27 | scrollElem.appendChild(li); 28 | } 29 | 30 | // Process data returned from api 31 | function processData(res) { 32 | res.json() 33 | .then(function (news) { 34 | currentPage++; 35 | news.forEach(renderNews); 36 | }); 37 | } 38 | 39 | // User scroll down stream 40 | var userScrolledDown$ = scrollEvent$ 41 | .map(function (e) { 42 | return { 43 | sH: e.target.scrollHeight, 44 | sT: e.target.scrollTop, 45 | cH: e.target.clientHeight 46 | }; 47 | }) 48 | .pairwise() 49 | .filter(function (positions) { 50 | return isUserScrollingDown(positions) && isScrollExpectedPercent(positions[1], 70); 51 | }); 52 | 53 | var requestOnScroll$ = userScrolledDown$ 54 | .startWith([]) 55 | .exhaustMap(function () { 56 | return Rx.Observable.fromPromise(fetch(getQuotesAPI())) 57 | }); 58 | 59 | 60 | // Subscribe and apply effects 61 | requestOnScroll$.subscribe(processData); 62 | 63 | })(); -------------------------------------------------------------------------------- /canvas-paint/custom.js: -------------------------------------------------------------------------------- 1 | ;(function(window, undefined) { 2 | 'use strict'; 3 | 4 | /** 5 | * @kind function 6 | * @name getOffset 7 | * @param event 8 | * @description Calculate offset either layerX/Y or offsetX/Y 9 | */ 10 | function getOffset(event) { 11 | return { 12 | offsetX: event.offsetX === undefined ? event.layerX : event.offsetX, 13 | offsetY: event.offsetY === undefined ? event.layerY : event.offsetY 14 | }; 15 | } 16 | 17 | function bootstrap() { 18 | var canvas = document.getElementById('tutorial'), 19 | move, down, up, context; 20 | 21 | if (canvas.getContext) { 22 | context = canvas.getContext('2d'); 23 | context.beginPath(); 24 | 25 | // check for pinter events 26 | if (window.navigator.pointerEnabled) { 27 | move = 'pointermove'; 28 | down = 'pointerdown'; 29 | up = 'pointerup'; 30 | } else { 31 | move = 'mousemove'; 32 | down = 'mousedown'; 33 | up = 'mouseup'; 34 | } 35 | 36 | // Get mouse moves. 37 | var mouseMoves = Rx.Observable.fromEvent(canvas, move); 38 | 39 | // Calculate the difference between two mouse moves. 40 | var mouseDelta = mouseMoves.zip(mouseMoves.skip(1), function(fst, snd) { 41 | return { first: getOffset(fst), second: getOffset(snd) }; 42 | }); 43 | 44 | // Merge together both mouse up and mouse down events. 45 | var mouseButton = Rx.Observable.fromEvent(canvas, down) 46 | .map(function() { 47 | return true; 48 | }) 49 | .merge(Rx.Observable.fromEvent(canvas,up).map(function() { 50 | return false; 51 | })); 52 | 53 | // Paint if the mouse is down. 54 | var paint = mouseButton.flatMapLatest(function(down) { 55 | return down ? mouseDelta : mouseDelta.take(0); 56 | }); 57 | 58 | // Update the canvas. 59 | paint.subscribe(function(x) { 60 | context.moveTo(x.first.offsetX, x.first.offsetY); 61 | context.lineTo(x.second.offsetX, x.second.offsetY); 62 | context.stroke(); 63 | }); 64 | } 65 | } 66 | 67 | window.onload = function() { 68 | bootstrap(); 69 | } 70 | 71 | })(window); -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebookincubator/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(path, needsSlash) { 15 | const hasSlash = path.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return path.substr(path, path.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${path}/`; 20 | } else { 21 | return path; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /typehead/typehead.js: -------------------------------------------------------------------------------- 1 | ;(function(global, $) { 2 | 3 | 'use strict'; 4 | 5 | var $input = $('#textInput'), 6 | $results = $('#results'); 7 | 8 | /** 9 | * @kind function 10 | * @method searchWikipedia 11 | * @param term {string} 12 | * @returns {Promise} 13 | */ 14 | function searchWikipedia(term) { 15 | if (!term) throw('Search term must be present in order to fetch from Wikipedia!'); 16 | return $.ajax({ 17 | url: 'http://en.wikipedia.org/w/api.php', 18 | dataType: 'jsonp', 19 | data: { 20 | action: 'opensearch', 21 | format: 'json', 22 | search: term 23 | } 24 | }).promise(); 25 | } 26 | 27 | /** 28 | * @kind function 29 | * @method valueFunc 30 | * @param data 31 | */ 32 | function valueFunc(data) { 33 | // console.log('------- Got new Data: ', data); 34 | var response = data[1]; 35 | 36 | // clean current results list 37 | $results.empty(); 38 | 39 | $.each(response, function(_, value) { 40 | $('
  • ' + value + '
  • ').appendTo($results); 41 | }); 42 | } 43 | 44 | /** 45 | * @kind function 46 | * @method errorFunc 47 | * @param error 48 | */ 49 | function errorFunc(error) { 50 | // clean current results list 51 | $results.empty(); 52 | // handle any errors 53 | $('
  • Error: ' + error + '
  • ').appendTo($results); 54 | } 55 | 56 | function completeFunc() { 57 | console.log('onComplete gets called!'); 58 | } 59 | 60 | /** 61 | * @kind function 62 | * @method bootstrap 63 | */ 64 | function bootstrap() { 65 | // Grab all distinct key-up events from the input 66 | var keyup = Rx.Observable.fromEvent($input, 'keyup') 67 | .map(function(e) { 68 | // project the text value from the input 69 | return e.target.value; 70 | }) 71 | .filter(function(text) { 72 | // empty the search results list if its less than 2 characters. 73 | if (text.length < 2 ) { 74 | $results.empty(); 75 | } 76 | // ensure more than 2 characters so you can fetch 77 | return text.length > 2; 78 | }) 79 | // Pause for 500ms 80 | .debounce(500) 81 | // Only if the value has changed. 82 | .distinctUntilChanged() 83 | .flatMapLatest(searchWikipedia) 84 | .subscribe(valueFunc, errorFunc, completeFunc) 85 | ; 86 | } 87 | 88 | global.onload = function() { 89 | bootstrap(); 90 | }; 91 | 92 | })(window, jQuery); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
    2 | 3 | RxJS Playground 4 | 5 |

    🌟 Clean examples, explanations, and resources for learning and exploring RxJS ✨ 6 |

    7 |
    8 | 9 | [![PRs Welcome][prs-badge]][prs] 10 | [![Code of Conduct][coc-badge]][coc] 11 | [![MIT License][license-badge]][license] 12 | 13 | > Clean examples, explanations, and resources for learning and exploring RxJS in depth 👊 14 | 15 | ### Current Examples 16 | - [x] Animation Painting 17 | - [x] Canvas Painting 18 | - [x] Crop a Image 19 | - [x] Data Binding 20 | - [x] Debt Calculator 21 | - [x] Drag and Drop 22 | - [x] Dynamic Rendering 23 | - [x] Follow the Mouse 24 | - [x] Github Fetch 25 | - [x] Image Color Selector 26 | - [x] Infinite Scroll 27 | - [x] Letter Count 28 | - [x] Mario 29 | - [x] Mouse Tracking 30 | - [x] Real Time 31 | - [x] Smart Counter 32 | - [x] Typehead 33 | - [x] Netflix Virtual List 34 | 35 | ### Awesome Resources 36 | * [The introduction to Reactive Programming you've been missing](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) - Andre Staltz 37 | * [Rx Marbles - Interactive diagrams of Rx Observables](http://rxmarbles.com/) - André Staltz 38 | * [Observables Under The Hood](https://netbasal.com/javascript-observables-under-the-hood-2423f760584) - Netanel Basal 39 | * [Rx Visualizer - Animated playground for Rx Observables](https://rxviz.com) - Misha Moroshko 40 | * [Reactive.how - Animated cards to learn Reactive Programming](http://reactive.how) - Cédric Soulas 41 | * [Zen Observable](https://github.com/zenparsing/zen-observable) - An implementation of Observables for JavaScript 42 | 43 | ### Contributing 44 | If you find a bug or think about enhancement or adding new examples, feel free to contribute and submit a PR. Contributions of any kind are welcome! 45 | 46 | > Special thanks to [Baran Pirinçal](https://github.com/baranpirincal) for an outstanding new logo design 👏 👏 47 | 48 | ## License 49 | MIT © [Jay Kan](https://github.com/JayKan) 50 |
    51 | MIT @ [Baran Pirinçal](https://github.com/baranpirincal) for the awesome new logo 52 | 53 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square 54 | [prs]: https://github.com/JayKan/RxJS-Playground/pulls 55 | [license-badge]: https://img.shields.io/npm/l/express.svg?style=flat-square 56 | [license]: https://github.com/JayKan/RxJS-Playground/blob/master/LICENSE 57 | [coc-badge]: https://img.shields.io/badge/code%20of-conduct-brightgreen.svg?style=flat-square 58 | [coc]: https://github.com/JayKan/RxJS-Playground/blob/master/CODE_OF_CONDUCT.md 59 | -------------------------------------------------------------------------------- /github-fetch/index.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | 'use strict'; 3 | 4 | var refreshButton = document.querySelector('.refresh'); 5 | var closeButton1 = document.querySelector('.close1'); 6 | var closeButton2 = document.querySelector('.close2'); 7 | var closeButton3 = document.querySelector('.close3'); 8 | 9 | var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click'); 10 | var close1ClickStream = Rx.Observable.fromEvent(closeButton1, 'click'); 11 | var close2ClickStream = Rx.Observable.fromEvent(closeButton2, 'click'); 12 | var close3ClickStream = Rx.Observable.fromEvent(closeButton3, 'click'); 13 | 14 | var requestStream = refreshClickStream.startWith('startup click') 15 | .map(function() { 16 | var randomOffset = Math.floor(Math.random()*500); 17 | return 'https://api.github.com/users?since=' + randomOffset; 18 | }); 19 | 20 | var responseStream = requestStream 21 | .flatMap(function (requestUrl) { 22 | return Rx.Observable.fromPromise($.getJSON(requestUrl)); 23 | }); 24 | 25 | function createSuggestionStream(closeClickStream) { 26 | return closeClickStream.startWith('startup click') 27 | .combineLatest(responseStream, 28 | function(click, listUsers) { 29 | return listUsers[Math.floor(Math.random()*listUsers.length)]; 30 | } 31 | ) 32 | .merge( 33 | refreshClickStream.map(function(){ 34 | return null; 35 | }) 36 | ) 37 | .startWith(null); 38 | } 39 | 40 | var suggestion1Stream = createSuggestionStream(close1ClickStream); 41 | var suggestion2Stream = createSuggestionStream(close2ClickStream); 42 | var suggestion3Stream = createSuggestionStream(close3ClickStream); 43 | 44 | 45 | // Rendering --------------------------------------------------- 46 | function renderSuggestion(suggestedUser, selector) { 47 | var suggestionEl = document.querySelector(selector); 48 | if (suggestedUser === null) { 49 | suggestionEl.style.visibility = 'hidden'; 50 | } else { 51 | suggestionEl.style.visibility = 'visible'; 52 | var usernameEl = suggestionEl.querySelector('.username'); 53 | usernameEl.href = suggestedUser.html_url; 54 | usernameEl.textContent = suggestedUser.login; 55 | var imgEl = suggestionEl.querySelector('img'); 56 | imgEl.src = ""; 57 | imgEl.src = suggestedUser.avatar_url; 58 | } 59 | } 60 | 61 | suggestion1Stream.subscribe(function (suggestedUser) { 62 | renderSuggestion(suggestedUser, '.suggestion1'); 63 | }); 64 | 65 | suggestion2Stream.subscribe(function (suggestedUser) { 66 | renderSuggestion(suggestedUser, '.suggestion2'); 67 | }); 68 | 69 | suggestion3Stream.subscribe(function (suggestedUser) { 70 | renderSuggestion(suggestedUser, '.suggestion3'); 71 | }); 72 | 73 | })(); -------------------------------------------------------------------------------- /debt-calculator/style.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, u, i, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, 11 | figure, figcaption, footer, header, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video { 14 | margin: 0; 15 | padding: 0; 16 | border: 0; 17 | font: inherit; 18 | font-size: 100%; 19 | vertical-align: baseline; 20 | } 21 | 22 | html { 23 | line-height: 1; 24 | } 25 | 26 | ol, ul { 27 | list-style: none; 28 | } 29 | 30 | table { 31 | border-collapse: collapse; 32 | border-spacing: 0; 33 | } 34 | 35 | 36 | caption, th, td { 37 | text-align: left; 38 | font-weight: normal; 39 | vertical-align: middle; 40 | } 41 | 42 | 43 | q, blockquote { 44 | quotes: none; 45 | } 46 | 47 | q:before, q:after, blockquote:before, blockquote:after { 48 | content: ""; 49 | content: none; 50 | } 51 | 52 | a img { 53 | border: none; 54 | } 55 | 56 | article, aside, details, figcaption, figure, footer, header, menu, nav, section, summary { 57 | display: block; 58 | } 59 | 60 | 61 | .one #result { 62 | color: red; 63 | } 64 | 65 | 66 | .two .row { 67 | border: 1px solid black; 68 | padding: 10px; 69 | border: 1px solid gray; 70 | width: 400px; 71 | margin: 0 0 2px 0; 72 | } 73 | 74 | .two .title { 75 | font-size: 1.25em; 76 | padding: 0 0 5px 0; 77 | } 78 | 79 | .two .description { 80 | font-size: 0.75em; 81 | font-style: italic; 82 | } 83 | 84 | 85 | .three .title { 86 | font-size: 1.25em; 87 | margin: 0 0 4px 0; 88 | } 89 | 90 | .three .capital { 91 | font-size: 0.9em; 92 | margin: 2px 0; 93 | } 94 | 95 | .three .population { 96 | font-size: 0.9em; 97 | margin: 2px 0; 98 | } 99 | 100 | .three .row { 101 | border: 1px solid black; 102 | padding: 10px; 103 | border: 1px solid gray; 104 | width: 300px; 105 | margin: 0 5px 5px 0; 106 | display: inline-block; 107 | } 108 | 109 | 110 | body { 111 | font-family: "Arial"; 112 | } 113 | 114 | 115 | #app { 116 | margin: 50px; 117 | } 118 | 119 | 120 | .input-group { 121 | margin: 10px 0; 122 | } 123 | 124 | 125 | input, select { 126 | width: 200px; 127 | margin: 10px 0; 128 | padding: 5px 10px; 129 | border: 1px solid gray; 130 | background: none; 131 | } 132 | 133 | label { 134 | display: block; 135 | font-weight: bold; 136 | font-size: 0.9em; 137 | } 138 | -------------------------------------------------------------------------------- /dynamic-render/script.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | 'use strict'; 3 | 4 | var Observable = Rx.Observable; 5 | var fromEvent = Observable.fromEvent; 6 | 7 | var canv = document.getElementById('canvas'); 8 | var contx = canv.getContext('2d'); 9 | contx.clearRect(0, 0, canv.width, canv.height); 10 | 11 | var points = document.getElementById('points'); 12 | var outerRadius = document.getElementById('outer-radius'); 13 | var innerRadius = document.getElementById('inner-radius'); 14 | var angle = document.getElementById('clockwise'); 15 | var lineWidth = document.getElementById('line-width'); 16 | var strokeColor = document.getElementById('stroke-color'); 17 | var fillColor = document.getElementById('fill-color'); 18 | 19 | var points$ = fromEvent(points, 'input', function(e) { 20 | return e.target.value 21 | }).startWith(points.value); 22 | 23 | var outerRadius$ = fromEvent(outerRadius, 'input', function(e) { 24 | return e.target.value; 25 | }).startWith(outerRadius.value).distinctUntilChanged(); 26 | 27 | var innerRadius$ = fromEvent(innerRadius, 'input', function (e) { 28 | return e.target.value; 29 | }).startWith(innerRadius.value); 30 | 31 | var angle$ = fromEvent(angle, 'input', function(e) { 32 | return e.target.value; 33 | }).startWith(angle.value); 34 | 35 | var lineWidth$ = fromEvent(lineWidth, 'input', function(e) { 36 | return e.target.value; 37 | }).startWith(lineWidth.value); 38 | 39 | var strokeColor$ = fromEvent(strokeColor, 'input', function(e) { 40 | return e.target.value; 41 | }).startWith(strokeColor.value); 42 | 43 | var fillColor$ = fromEvent(fillColor, 'input', function(e) { 44 | return e.target.value; 45 | }).startWith(fillColor.value); 46 | 47 | Rx.Observable 48 | .combineLatest(points$, outerRadius$, innerRadius$, angle$, strokeColor$, fillColor$, lineWidth$) 49 | .subscribe(function(values) { 50 | draw.apply(null, values); 51 | }); 52 | 53 | function draw(points, radius1, radius2, alpha0, strokeColor, fillColor, lineWidth) { 54 | contx.clearRect(0, 0, canv.width, canv.height); 55 | contx.beginPath(); 56 | drawShape(contx, canv.width / 2, 57 | canv.height / 2, parseInt(points), parseInt(radius1), parseInt(radius2), parseInt(alpha0), 1); 58 | contx.strokeStyle = strokeColor; 59 | contx.fillStyle = fillColor; 60 | contx.lineWidth = lineWidth; 61 | contx.stroke(); 62 | contx.fill(); 63 | } 64 | 65 | function drawShape(ctx, x, y, points, radius1, radius2, alpha0, ratio) { 66 | //points: number of points (or number of sides for polygons) 67 | //radius1: "outer" radius of the star 68 | //radius2: "inner" radius of the star (if equal to radius1, a polygon is drawn) 69 | //angle0: initial angle (clockwise), by default, stars and polygons are 'pointing' up 70 | var i, angle, radius; 71 | if (radius2 !== radius1) { 72 | points = 2 * points; 73 | } 74 | for (i = 0; i <= points; i++) { 75 | angle = i * 2 * Math.PI / points - Math.PI / 2 + alpha0; 76 | radius = i % 2 === 0 ? radius1 : radius2; 77 | ctx.lineTo(x + radius * Math.cos(angle), y + radius * Math.sin(angle) * ratio); 78 | } 79 | } 80 | 81 | })(); -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-recompoes-demo", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "autoprefixer": "7.1.6", 7 | "babel-core": "6.26.0", 8 | "babel-eslint": "7.2.3", 9 | "babel-jest": "20.0.3", 10 | "babel-loader": "7.1.2", 11 | "babel-preset-react-app": "^3.1.1", 12 | "babel-runtime": "6.26.0", 13 | "case-sensitive-paths-webpack-plugin": "2.1.1", 14 | "chalk": "1.1.3", 15 | "css-loader": "0.28.7", 16 | "dotenv": "4.0.0", 17 | "dotenv-expand": "4.2.0", 18 | "eslint": "4.10.0", 19 | "eslint-config-react-app": "^2.1.0", 20 | "eslint-loader": "1.9.0", 21 | "eslint-plugin-flowtype": "2.39.1", 22 | "eslint-plugin-import": "2.8.0", 23 | "eslint-plugin-jsx-a11y": "5.1.1", 24 | "eslint-plugin-react": "7.4.0", 25 | "extract-text-webpack-plugin": "3.0.2", 26 | "file-loader": "1.1.5", 27 | "fs-extra": "3.0.1", 28 | "html-webpack-plugin": "2.29.0", 29 | "jest": "20.0.4", 30 | "object-assign": "4.1.1", 31 | "postcss-flexbugs-fixes": "3.2.0", 32 | "postcss-loader": "2.0.8", 33 | "promise": "8.0.1", 34 | "raf": "3.4.0", 35 | "react": "^16.4.1", 36 | "react-dev-utils": "^6.1.1", 37 | "react-dom": "^16.4.1", 38 | "recompose": "^0.25.1", 39 | "resolve": "1.6.0", 40 | "rxjs": "^5.4.3", 41 | "style-loader": "0.19.0", 42 | "sw-precache-webpack-plugin": "0.11.4", 43 | "url-loader": "0.6.2", 44 | "webpack": "3.8.1", 45 | "webpack-dev-server": "^3.1.10", 46 | "webpack-manifest-plugin": "1.3.2", 47 | "whatwg-fetch": "2.0.3" 48 | }, 49 | "scripts": { 50 | "start": "node scripts/start.js", 51 | "build": "node scripts/build.js", 52 | "test": "node scripts/test.js --env=jsdom" 53 | }, 54 | "devDependencies": { 55 | "babel-plugin-react-require": "^3.0.0" 56 | }, 57 | "jest": { 58 | "collectCoverageFrom": [ 59 | "src/**/*.{js,jsx,mjs}" 60 | ], 61 | "setupFiles": [ 62 | "/config/polyfills.js" 63 | ], 64 | "testMatch": [ 65 | "/src/**/__tests__/**/*.{js,jsx,mjs}", 66 | "/src/**/?(*.)(spec|test).{js,jsx,mjs}" 67 | ], 68 | "testEnvironment": "node", 69 | "testURL": "http://localhost", 70 | "transform": { 71 | "^.+\\.(js|jsx|mjs)$": "/node_modules/babel-jest", 72 | "^.+\\.css$": "/config/jest/cssTransform.js", 73 | "^(?!.*\\.(js|jsx|mjs|css|json)$)": "/config/jest/fileTransform.js" 74 | }, 75 | "transformIgnorePatterns": [ 76 | "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$" 77 | ], 78 | "moduleNameMapper": { 79 | "^react-native$": "react-native-web" 80 | }, 81 | "moduleFileExtensions": [ 82 | "web.js", 83 | "js", 84 | "json", 85 | "web.jsx", 86 | "jsx", 87 | "node", 88 | "mjs" 89 | ] 90 | }, 91 | "babel": { 92 | "presets": [ 93 | "react-app" 94 | ], 95 | "plugins": [ 96 | "react-require" 97 | ] 98 | }, 99 | "eslintConfig": { 100 | "extends": "react-app" 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /animation/custom.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 3 | 'use strict'; 4 | 5 | console.log('Animation Test loading'); 6 | 7 | var fromEvent = Rx.Observable.fromEvent; 8 | 9 | /** 10 | * Extends RxJS Observable 11 | * @method movingWindow 12 | * @param size {number} 13 | * @param selector {Function} Current box 14 | * @param onShift {Function} 15 | */ 16 | Rx.Observable.prototype.movingWindow = function(size, selector, onShift) { 17 | var fn = this; 18 | return Rx.Observable.create(function(observable) { 19 | var arr = []; 20 | return fn.subscribe( 21 | function(x) { 22 | var item = selector(x); 23 | arr.push(item); 24 | if (arr.length > size) { 25 | var i = arr.shift(); 26 | onShift(i); 27 | } 28 | }, 29 | function(e) { observable.onError(e); }, 30 | function() { observable.onCompleted(); } 31 | ); 32 | }); 33 | }; 34 | 35 | /** 36 | * @class 37 | * @constructor 38 | * @param point 39 | * @param parent 40 | * @namespace Box 41 | */ 42 | function Box(point, parent) { 43 | this.parent = parent; 44 | this.id = 'box_' + Date.now(); 45 | this.point = {}; 46 | this.point.x = point[0]; 47 | this.point.y = point[1]; 48 | this.buildBox(); 49 | } 50 | 51 | Box.prototype = { 52 | /** 53 | * @method buildBox 54 | * @returns Box instance for chaining. 55 | */ 56 | buildBox: function buildBox(){ 57 | var box = $("
    ").css({ 58 | height: 20, 59 | width: 20, 60 | position: 'absolute', 61 | top: this.point.y - 10, 62 | left: this.point.x - 10, 63 | display: 'none', 64 | backgroundColor: 'rgb(' + (random(0, 255)) + ', ' + (random(0, 255)) + ', ' + (random(0, 255)) + ')' 65 | }); 66 | this.parent.append(box); 67 | return this; 68 | }, 69 | 70 | /** 71 | * Finds the parent
    with current box.id and then 72 | * adds jQuery fadeIn animation. 73 | */ 74 | showBox: function(){ 75 | return this.parent.find('#' + this.id).fadeIn('fast'); 76 | }, 77 | /** 78 | * Find the parent
    and then remove current box. 79 | */ 80 | hideBox: function(){ 81 | return this.parent.find("#" + this.id).fadeOut('fast', function() { 82 | return $(this).remove(); 83 | }); 84 | } 85 | }; 86 | 87 | /** 88 | * @private 89 | * @function random 90 | * @param low 91 | * @param high 92 | * @returns {number} 93 | */ 94 | function random(low, high) { 95 | return Math.floor(Math.random() * (high + 1)) - low; 96 | } 97 | 98 | function load(){ 99 | var canvasElement = $('#drawing'); 100 | return Rx.Observable.fromEvent(canvasElement, 'mousemove') 101 | .movingWindow( 102 | 25, 103 | function(x) { 104 | var box = new Box([x.clientX, x.clientY], canvasElement); 105 | box.showBox(); 106 | return box; 107 | }, 108 | function(box){ 109 | box.hideBox(); 110 | } 111 | ).subscribe(); 112 | } 113 | window.onload = load; 114 | })(); 115 | -------------------------------------------------------------------------------- /data-binding/custom.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 3 | 'use strict'; 4 | 5 | /** 6 | * @private 7 | * @method setText 8 | * @param text {string} 9 | */ 10 | function setText(text){ 11 | this.textContent = text; 12 | } 13 | 14 | /** 15 | * @private 16 | * @method fromEvent 17 | * @param {Object} HTML DOM element or NodeList to attach a listener. 18 | * @param {string} eventName to attach to the observable sequence. 19 | * @returns {Observable} An observable sequence of events from the specified element 20 | * and the specified event. 21 | */ 22 | var fromEvent = Rx.Observable.fromEvent; 23 | 24 | 25 | // Make a simple binding 26 | var label1 = document.getElementById('label1'); 27 | var hello = new Rx.BehaviorSubject('Hello'); 28 | hello.subscribe( setText.bind(label1) ); 29 | 30 | /** 31 | * Rx.BehaviorSubject Class 32 | * - A Rx.BehaviorSubject Class represents a value that changes over time. 33 | * Observers can subscribe to the subject to receive the last (or initial) value and 34 | * all subsequent notifications 35 | */ 36 | // Initialize with initial value of 42 37 | var subject = new Rx.BehaviorSubject(42); 38 | 39 | var subscription = subject.subscribe( 40 | function(x){ 41 | console.log('onNext Called: ' + x.toString() ); 42 | }, 43 | function(err){ 44 | console.log('Error: ', + err ); 45 | }, 46 | function(){ 47 | console.log('onCompleted invoked!'); 48 | } 49 | ); 50 | subject.onNext(56); // => Next: 56 51 | subject.onCompleted(); // => Completed 52 | 53 | 54 | // Create simple bindings for first and last name 55 | var firstNameSubject = new Rx.BehaviorSubject(''); 56 | var lastNameSubject = new Rx.BehaviorSubject(''); 57 | 58 | // Create first and last name composite 59 | var fullNameSubject = firstNameSubject.combineLatest(lastNameSubject, function(first, last){ 60 | return first + ' ' + last; 61 | }); 62 | 63 | // Subscribe to them all 64 | var firstNameElement = document.getElementById('firstName'); 65 | firstNameSubject.subscribe( 66 | function(text){ 67 | firstNameElement.value = text; 68 | } 69 | ); 70 | 71 | var lastNameElement = document.getElementById('lastName'); 72 | lastNameSubject.subscribe( 73 | function(text){ 74 | lastNameElement.value = text; 75 | }, 76 | function(error){ 77 | console.log('onError: ', error); 78 | }, 79 | function(){ 80 | console.log('onCompleted lastNameElement!'); 81 | } 82 | ); 83 | 84 | var formDataElement = document.getElementById('formData'); 85 | fullNameSubject.subscribe(function(text){ 86 | formDataElement.value = text; 87 | }); 88 | 89 | fromEvent(firstNameElement, 'keyup') 90 | .subscribe( 91 | function(e){ 92 | firstNameSubject.onNext(e.target.value) 93 | }, 94 | function(){ 95 | console.log('Error on firstName input element'); 96 | }, 97 | function(){ 98 | console.log('FirstName input element completed subscription!'); 99 | } 100 | ); 101 | 102 | fromEvent(lastNameElement, 'keyup') 103 | .subscribe( 104 | function(e){ 105 | lastNameSubject.onNext(e.target.value); 106 | }, 107 | function(){ 108 | console.log('Error on lastName input element.'); 109 | }, 110 | function(){ 111 | console.log('LastName input element completed subscription!'); 112 | } 113 | ); 114 | 115 | 116 | })(); -------------------------------------------------------------------------------- /docs/SUBJECT.md: -------------------------------------------------------------------------------- 1 | ### RxJS's Subject Class 2 | > A Subject is a type that implements both Observer and Observables types. As an Observer, it can subscribe to Observables, and as an Observable it can produce values and have Observers subscribe to it. 3 | 4 | In some scenarios a single Subject can do the work of a **combination** of Observers and Observables. For instance, for making a proxy object between a data source and the Subject's listeners, we could use something like this: 5 | ```javascript 6 | // Create a new Subject 7 | var subject = new Rx.Subject(); 8 | 9 | // Create a `source` Observable 10 | var source = Rx.Observable 11 | .interval(1000) 12 | .map(function(v) { return 'Interval message #' + v; }) 13 | .take(5); 14 | 15 | // Subscribe the `Subject` to the `Observable` 16 | source.subscribe(subject); 17 | 18 | // Subscribe an `Observer` to the `Subject`. `Subject` now behaves as an Observable now. 19 | var subscription = subject.subscribe( 20 | function onNext(e) { console.log('onNext: ' + x); }, 21 | function onError(e) { console.log('onError: ' + e.message); }, 22 | function onCompleted() { console.log('onCompleted'); } 23 | ); 24 | 25 | // Make our Subject emits values of its own (message1 and message2) 26 | subject.onNext('Our message #1'); 27 | subject.onNext('Our message #2'); 28 | 29 | setTimeout(function() { 30 | subject.onCompleted(); 31 | }, 2000); 32 | ``` 33 | 34 | ### AsyncSubject 35 | > AsyncSubject emits the last value of a sequence only if the sequence completes. This value is then cached forever, and any Observer that subscribes after the value has been emitted will receive it right away. AsyncSubject is convenient for asynchronous operations that return a single value, such as Ajax requests. 36 | 37 | ```javascript 38 | // Create a new AsyncSubject 39 | var subject = new Rx.AsyncSubject(); 40 | 41 | // Create a delayed Observable 42 | var delayed$ = Rx.Observable.range(0, 5).delay(1000); 43 | 44 | // Subscribe our Observable to AsyncSubject. 45 | delayed$.subscribe(subject); 46 | 47 | subject.subscribe( 48 | function onNext(v) { console.log('onNext: ' + v); }, 49 | function onError(e) { console.log('onError: ' + e.message); }, 50 | function onCompleted() { console.log('onCompleted'); } 51 | ); 52 | ``` 53 | 54 | *AsyncSubject* represents the result of an asynchronous action, and you can use it as a substitute for a promise. The difference internally is that a promise will only ever process a single value, whereas **AsyncSubject** processes all values in a sequence, only ever emitting (and caching) the **last one**. 55 | 56 | ### BehaviorSubject 57 | > When an Observer subscribes to a BehaviorSubject, it receives the last emitted value and then all the subsequent values. BehaviorSubject requires that we provide a **starting value**, so that all Observers will always receive a value when they subscribe to a BehaviorSubject. 58 | 59 | ```javascript 60 | var subject = new Rx.BehaviorSubject('Waiting for content'); 61 | 62 | subject.subscribe( 63 | function(result) { 64 | document.body.textContent = result.response || result; 65 | }, 66 | function(err) { 67 | document.body.textContent = 'There was an error retrieving content'; 68 | } 69 | ); 70 | 71 | Rx.DOM.get('/remote/content').subscribe(subject); 72 | ``` 73 | 74 | ### ReplaySubject 75 | > A ReplaySubject caches its values and re-emits them to any Observer that subscribes late to it. Unlike with AsyncSubject, the sequence does not need to be completed for this to happen. 76 | 77 | *ReplaySubject* is useful to make sure that Observers get **all the values** emitted by an Observable from the start. It spares us from writing messy code that caches previous values, saving us from nasty concurrency-related bugs. 78 | 79 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | **Table of Contents** 4 | * [Our Pledge](#our-pledge) 5 | * [Our Standards](#our-standards) 6 | * [Our Responsibilities](#our-responsibilities) 7 | * [Scope](#scope) 8 | * [Enforcement](#enforcement) 9 | * [Attribution](#attribution) 10 | 11 | ## Our Pledge 12 | 13 | In the interest of fostering an open and welcoming environment, we as 14 | contributors and maintainers pledge to making participation in our project and 15 | our community a harassment-free experience for everyone, regardless of age, body 16 | size, disability, ethnicity, gender identity and expression, level of experience, 17 | nationality, personal appearance, race, religion, or sexual identity and 18 | orientation. 19 | 20 | ## Our Standards 21 | 22 | Examples of behavior that contributes to creating a positive environment 23 | include: 24 | 25 | * Using welcoming and inclusive language 26 | * Being respectful of differing viewpoints and experiences 27 | * Gracefully accepting constructive criticism 28 | * Focusing on what is best for the community 29 | * Showing empathy towards other community members 30 | 31 | Examples of unacceptable behavior by participants include: 32 | 33 | * The use of sexualized language or imagery and unwelcome sexual attention or 34 | advances 35 | * Trolling, insulting/derogatory comments, and personal or political attacks 36 | * Public or private harassment 37 | * Publishing others' private information, such as a physical or electronic 38 | address, without explicit permission 39 | * Other conduct which could reasonably be considered inappropriate in a 40 | professional setting 41 | 42 | ## Our Responsibilities 43 | 44 | Project maintainers are responsible for clarifying the standards of acceptable 45 | behavior and are expected to take appropriate and fair corrective action in 46 | response to any instances of unacceptable behavior. 47 | 48 | Project maintainers have the right and responsibility to remove, edit, or 49 | reject comments, commits, code, wiki edits, issues, and other contributions 50 | that are not aligned to this Code of Conduct, or to ban temporarily or 51 | permanently any contributor for other behaviors that they deem inappropriate, 52 | threatening, offensive, or harmful. 53 | 54 | ## Scope 55 | 56 | This Code of Conduct applies both within project spaces and in public spaces 57 | when an individual is representing the project or its community. Examples of 58 | representing a project or community include using an official project e-mail 59 | address, posting via an official social media account, or acting as an appointed 60 | representative at an online or offline event. Representation of a project may be 61 | further defined and clarified by project maintainers. 62 | 63 | ## Enforcement 64 | 65 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 66 | reported by contacting the project team at leicasper@gmail.com. All 67 | complaints will be reviewed and investigated and will result in a response that 68 | is deemed necessary and appropriate to the circumstances. The project team is 69 | obligated to maintain confidentiality with regard to the reporter of an incident. 70 | Further details of specific enforcement policies may be posted separately. 71 | 72 | Project maintainers who do not follow or enforce the Code of Conduct in good 73 | faith may face temporary or permanent repercussions as determined by other 74 | members of the project's leadership. 75 | 76 | ## Attribution 77 | 78 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 79 | available at [http://contributor-covenant.org/version/1/4][version] 80 | 81 | [homepage]: http://contributor-covenant.org 82 | [version]: http://contributor-covenant.org/version/1/4/ 83 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/config/env.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const paths = require('./paths'); 6 | 7 | // Make sure that including paths.js after env.js will read .env variables. 8 | delete require.cache[require.resolve('./paths')]; 9 | 10 | const NODE_ENV = process.env.NODE_ENV; 11 | if (!NODE_ENV) { 12 | throw new Error( 13 | 'The NODE_ENV environment variable is required but was not specified.' 14 | ); 15 | } 16 | 17 | // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use 18 | var dotenvFiles = [ 19 | `${paths.dotenv}.${NODE_ENV}.local`, 20 | `${paths.dotenv}.${NODE_ENV}`, 21 | // Don't include `.env.local` for `test` environment 22 | // since normally you expect tests to produce the same 23 | // results for everyone 24 | NODE_ENV !== 'test' && `${paths.dotenv}.local`, 25 | paths.dotenv, 26 | ].filter(Boolean); 27 | 28 | // Load environment variables from .env* files. Suppress warnings using silent 29 | // if this file is missing. dotenv will never modify any environment variables 30 | // that have already been set. Variable expansion is supported in .env files. 31 | // https://github.com/motdotla/dotenv 32 | // https://github.com/motdotla/dotenv-expand 33 | dotenvFiles.forEach(dotenvFile => { 34 | if (fs.existsSync(dotenvFile)) { 35 | require('dotenv-expand')( 36 | require('dotenv').config({ 37 | path: dotenvFile, 38 | }) 39 | ); 40 | } 41 | }); 42 | 43 | // We support resolving modules according to `NODE_PATH`. 44 | // This lets you use absolute paths in imports inside large monorepos: 45 | // https://github.com/facebookincubator/create-react-app/issues/253. 46 | // It works similar to `NODE_PATH` in Node itself: 47 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 48 | // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. 49 | // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. 50 | // https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421 51 | // We also resolve them to make sure all tools using them work consistently. 52 | const appDirectory = fs.realpathSync(process.cwd()); 53 | process.env.NODE_PATH = (process.env.NODE_PATH || '') 54 | .split(path.delimiter) 55 | .filter(folder => folder && !path.isAbsolute(folder)) 56 | .map(folder => path.resolve(appDirectory, folder)) 57 | .join(path.delimiter); 58 | 59 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 60 | // injected into the application via DefinePlugin in Webpack configuration. 61 | const REACT_APP = /^REACT_APP_/i; 62 | 63 | function getClientEnvironment(publicUrl) { 64 | const raw = Object.keys(process.env) 65 | .filter(key => REACT_APP.test(key)) 66 | .reduce( 67 | (env, key) => { 68 | env[key] = process.env[key]; 69 | return env; 70 | }, 71 | { 72 | // Useful for determining whether we’re running in production mode. 73 | // Most importantly, it switches React into the correct mode. 74 | NODE_ENV: process.env.NODE_ENV || 'development', 75 | // Useful for resolving the correct path to static assets in `public`. 76 | // For example, . 77 | // This should only be used as an escape hatch. Normally you would put 78 | // images into the `src` and `import` them in code to get their paths. 79 | PUBLIC_URL: publicUrl, 80 | } 81 | ); 82 | // Stringify all values so we can feed into Webpack DefinePlugin 83 | const stringified = { 84 | 'process.env': Object.keys(raw).reduce((env, key) => { 85 | env[key] = JSON.stringify(raw[key]); 86 | return env; 87 | }, {}), 88 | }; 89 | 90 | return { raw, stringified }; 91 | } 92 | 93 | module.exports = getClientEnvironment; 94 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/scripts/start.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'development'; 5 | process.env.NODE_ENV = 'development'; 6 | 7 | // Makes the script crash on unhandled rejections instead of silently 8 | // ignoring them. In the future, promise rejections that are not handled will 9 | // terminate the Node.js process with a non-zero exit code. 10 | process.on('unhandledRejection', err => { 11 | throw err; 12 | }); 13 | 14 | // Ensure environment variables are read. 15 | require('../config/env'); 16 | 17 | const fs = require('fs'); 18 | const chalk = require('chalk'); 19 | const webpack = require('webpack'); 20 | const WebpackDevServer = require('webpack-dev-server'); 21 | const clearConsole = require('react-dev-utils/clearConsole'); 22 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); 23 | const { 24 | choosePort, 25 | createCompiler, 26 | prepareProxy, 27 | prepareUrls, 28 | } = require('react-dev-utils/WebpackDevServerUtils'); 29 | const openBrowser = require('react-dev-utils/openBrowser'); 30 | const paths = require('../config/paths'); 31 | const config = require('../config/webpack.config.dev'); 32 | const createDevServerConfig = require('../config/webpackDevServer.config'); 33 | 34 | const useYarn = fs.existsSync(paths.yarnLockFile); 35 | const isInteractive = process.stdout.isTTY; 36 | 37 | // Warn and crash if required files are missing 38 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { 39 | process.exit(1); 40 | } 41 | 42 | // Tools like Cloud9 rely on this. 43 | const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; 44 | const HOST = process.env.HOST || '0.0.0.0'; 45 | 46 | if (process.env.HOST) { 47 | console.log( 48 | chalk.cyan( 49 | `Attempting to bind to HOST environment variable: ${chalk.yellow( 50 | chalk.bold(process.env.HOST) 51 | )}` 52 | ) 53 | ); 54 | console.log( 55 | `If this was unintentional, check that you haven't mistakenly set it in your shell.` 56 | ); 57 | console.log(`Learn more here: ${chalk.yellow('http://bit.ly/2mwWSwH')}`); 58 | console.log(); 59 | } 60 | 61 | // We attempt to use the default port but if it is busy, we offer the user to 62 | // run on a different port. `choosePort()` Promise resolves to the next free port. 63 | choosePort(HOST, DEFAULT_PORT) 64 | .then(port => { 65 | if (port == null) { 66 | // We have not found a port. 67 | return; 68 | } 69 | const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; 70 | const appName = require(paths.appPackageJson).name; 71 | const urls = prepareUrls(protocol, HOST, port); 72 | // Create a webpack compiler that is configured with custom messages. 73 | const compiler = createCompiler(webpack, config, appName, urls, useYarn); 74 | // Load proxy config 75 | const proxySetting = require(paths.appPackageJson).proxy; 76 | const proxyConfig = prepareProxy(proxySetting, paths.appPublic); 77 | // Serve webpack assets generated by the compiler over a web sever. 78 | const serverConfig = createDevServerConfig( 79 | proxyConfig, 80 | urls.lanUrlForConfig 81 | ); 82 | const devServer = new WebpackDevServer(compiler, serverConfig); 83 | // Launch WebpackDevServer. 84 | devServer.listen(port, HOST, err => { 85 | if (err) { 86 | return console.log(err); 87 | } 88 | if (isInteractive) { 89 | clearConsole(); 90 | } 91 | console.log(chalk.cyan('Starting the development server...\n')); 92 | openBrowser(urls.localUrlForBrowser); 93 | }); 94 | 95 | ['SIGINT', 'SIGTERM'].forEach(function(sig) { 96 | process.on(sig, function() { 97 | devServer.close(); 98 | process.exit(); 99 | }); 100 | }); 101 | }) 102 | .catch(err => { 103 | if (err && err.message) { 104 | console.log(err.message); 105 | } 106 | process.exit(1); 107 | }); 108 | -------------------------------------------------------------------------------- /crop/requestanimationframescheduler.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. 2 | 3 | (function (root, factory) { 4 | var freeExports = typeof exports == 'object' && exports, 5 | freeModule = typeof module == 'object' && module && module.exports == freeExports && module, 6 | freeGlobal = typeof global == 'object' && global; 7 | if (freeGlobal.global === freeGlobal) { 8 | window = freeGlobal; 9 | } 10 | 11 | // Because of build optimizers 12 | if (typeof define === 'function' && define.amd) { 13 | define(['rx', 'exports'], function (Rx, exports) { 14 | root.Rx = factory(root, exports, Rx); 15 | return root.Rx; 16 | }); 17 | } else if (typeof module === 'object' && module && module.exports === freeExports) { 18 | module.exports = factory(root, module.exports, require('./rx')); 19 | } else { 20 | root.Rx = factory(root, {}, root.Rx); 21 | } 22 | }(this, function (window, exp, Rx, undefined) { 23 | 24 | var Scheduler = Rx.Scheduler, 25 | SingleAssignmentDisposable = Rx.SingleAssignmentDisposable, 26 | CompositeDisposable = Rx.CompositeDisposable, 27 | disposableCreate = Rx.Disposable.create, 28 | defaultNow = (function () { return !!Date.now ? Date.now : function () { return +new Date; }; }());; 29 | 30 | // Get the right animation frame method 31 | var requestAnimFrame, cancelAnimFrame; 32 | if (window.requestAnimationFrame) { 33 | requestAnimFrame = window.requestAnimationFrame; 34 | cancelAnimFrame = window.cancelAnimationFrame; 35 | } else if (window.mozRequestAnimationFrame) { 36 | requestAnimFrame = window.mozRequestAnimationFrame; 37 | cancelAnimFrame = window.mozCancelAnimationFrame; 38 | } else if (window.webkitRequestAnimationFrame) { 39 | requestAnimFrame = window.webkitRequestAnimationFrame; 40 | cancelAnimFrame = window.webkitCancelAnimationFrame; 41 | } else if (window.msRequestAnimationFrame) { 42 | requestAnimFrame = window.msRequestAnimationFrame; 43 | cancelAnimFrame = window.msCancelAnimationFrame; 44 | } else if (window.oRequestAnimationFrame) { 45 | requestAnimFrame = window.oRequestAnimationFrame; 46 | cancelAnimFrame = window.oCancelAnimationFrame; 47 | } else { 48 | requestAnimFrame = function(cb) { window.setTimeout(cb, 1000 / 60); }; 49 | cancelAnimFrame = window.clearTimeout; 50 | } 51 | 52 | /** 53 | * Gets a scheduler that schedules schedules work on the requestAnimationFrame for immediate actions. 54 | */ 55 | Scheduler.requestAnimationFrame = (function () { 56 | 57 | function scheduleNow(state, action) { 58 | var scheduler = this, 59 | disposable = new SingleAssignmentDisposable(); 60 | var id = requestAnimFrame(function () { 61 | if (!disposable.isDisposed) { 62 | disposable.setDisposable(action(scheduler, state)); 63 | } 64 | }); 65 | return new CompositeDisposable(disposable, disposableCreate(function () { 66 | cancelAnimFrame(id); 67 | })); 68 | } 69 | 70 | function scheduleRelative(state, dueTime, action) { 71 | var scheduler = this, 72 | dt = Scheduler.normalize(dueTime); 73 | if (dt === 0) { 74 | return scheduler.scheduleWithState(state, action); 75 | } 76 | 77 | var disposable = new SingleAssignmentDisposable(), 78 | id; 79 | var scheduleFunc = function () { 80 | if (id) { cancelAnimFrame(id); } 81 | if (dt - scheduler.now() <= 0) { 82 | if (!disposable.isDisposed) { 83 | disposable.setDisposable(action(scheduler, state)); 84 | } 85 | } else { 86 | id = requestAnimFrame(scheduleFunc); 87 | } 88 | }; 89 | 90 | id = requestAnimFrame(scheduleFunc); 91 | 92 | return new CompositeDisposable(disposable, disposableCreate(function () { 93 | cancelAnimFrame(id); 94 | })); 95 | } 96 | 97 | function scheduleAbsolute(state, dueTime, action) { 98 | return this.scheduleWithRelativeAndState(state, dueTime - this.now(), action); 99 | } 100 | 101 | return new Scheduler(defaultNow, scheduleNow, scheduleRelative, scheduleAbsolute); 102 | 103 | }()); 104 | 105 | return Rx; 106 | })); 107 | -------------------------------------------------------------------------------- /docs/OBSERVABLE.md: -------------------------------------------------------------------------------- 1 | # Learning RxJS 2 | 3 | ### Observable 4 | 5 | ### How to create an Observer **explicitly**: 6 | ```javascript 7 | var observer = Rx.Observer.create( 8 | function onNext(x) { console.log('Next: ' + x); }, 9 | function onErorr(err) { console.log('Error: ', + err) }, 10 | function onCompleted() { console.log('Completed') } 11 | }; 12 | 13 | ``` 14 | 15 | ### How to make Ajax Calls with an Observable: 16 | ```javascript 17 | function get(url) { 18 | return Rx.Observable.create(function(observer) { 19 | // make your request 20 | var req = new XMLHttpRequest(); 21 | req.open('GET', url); 22 | 23 | req.onload = function() { 24 | if (req.status === 200) { 25 | observer.onNext(req.resposne); 26 | observer.onCompleted(); 27 | } else { 28 | observer.onError(new Error(req.statusText)); 29 | } 30 | }; 31 | 32 | req.onerror = function() { 33 | observer.onError(new Error('Unknown Error')); 34 | }; 35 | 36 | req.send(); 37 | }); 38 | } 39 | 40 | // Create your Ajax Observable 41 | var ajax$ = get('/api/contents.json'); 42 | 43 | // Subscribe an `Observer` to our `ajax` observable 44 | ajax$.subscribe( 45 | function onNext(x) { console.log('Result: ' + x); }, 46 | function onError(e) { console.log('Error: ' + e); }, 47 | function onCompleted() { console.log('Completed'); } 48 | ); 49 | 50 | ``` 51 | 52 | ### How to create Observables from Arrays 53 | ```javascript 54 | 55 | Rx.Observable 56 | .from (['A', 'B', 'C']) 57 | .subscribe( 58 | function(x) { console.log('Next: ' + x); }, 59 | function(e) { console.log('Error: ', + e); }, 60 | function() { console.log('Completed'); } 61 | ); 62 | 63 | ``` 64 | 65 | ### How to create Observables from JavaScript Events 66 | The following `allMouseMoves$` observable example demonstrates how to emit the coordinates of the mouse pointer: 67 | ```javascript 68 | var allMouseMoves$ = Rx.Observable.fromEvent(document, 'mousemove'); 69 | 70 | allMouseMoves$.subscribe(function(e) { 71 | console.log(e.clientX, e.clientY); 72 | }); 73 | 74 | ``` 75 | 76 | ### How to create new Observables based on the original Observables: 77 | ```javascript 78 | 79 | // Create `movesOnTheRight$` observable based from previously `allMouseMoves$` 80 | var movesOnTheRight$ = allMouseMoves$.filter(function(e) { 81 | return e.clientX > window.innerWidth /2; 82 | }); 83 | 84 | ``` 85 | 86 | 87 | ### How to effectively use sequences in our programs? 88 | - `merge` operator takes two different Observables and returns a new one with the merged values. 89 | - `interval` operator returns an Observable that yields incremental numbers at a given interval of time, expressed in milliseconds. 90 | 91 | 92 | ### Transforming Operators: 93 | `Map` is the sequence transformation operator. It takes an `Observable` and a function and applies that function to each of the values in the source Observable and returns a new Observable with the transformed values. 94 | ```javascript 95 | 96 | var logValue = function(val) { console.log(val); }; 97 | 98 | var src$ = Rx.Observable.range(1, 5); 99 | 100 | var upper$ = src$.map(function(name) { 101 | return name * 2; 102 | }); 103 | 104 | upper$.subscribe(logValue); 105 | 106 | ``` 107 | In the above case, `src$` observable doesn't mutate. If you would like to pass a function to `map` operator that does a asynchronous computation to transform the value, you would probably use `flatMap` operator. 108 | 109 | 110 | ### Canceling Sequences 111 | There are two main ways we can cancel an `Observable`: *implicitly* and *explicitly* 112 | ```javascript 113 | 114 | // Explicit cancellation example: 115 | var counter = Rx.Observable.interval(1000); 116 | 117 | var subscription1 = counter.subscribe(function(i) { 118 | console.log('Subscription 1: ', i); 119 | }); 120 | 121 | var subscription2 = counter.subscribe(function(i) { 122 | console.log('Subscription 2: ', i); 123 | }); 124 | 125 | setTimeout(function() { 126 | console.log('Canceling subscription2!'); 127 | subscription2.dispose(); 128 | }, 2000); 129 | ``` 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /mario/custom.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | 'use strict'; 3 | 4 | var slice = Array.prototype.slice, 5 | Observable = Rx.Observable; 6 | 7 | var dom = {}; 8 | ['resize','keyup','keydown'].forEach(function (event) { 9 | dom[event] = function (element) { 10 | return Observable.fromEvent(element, event); 11 | }; 12 | }); 13 | 14 | // compose functions 15 | function compose() { 16 | return slice.call(arguments, 0).reduce(function(f, g) { 17 | return function () { 18 | return f(g.apply(null, arguments)); 19 | }; 20 | }); 21 | } 22 | 23 | // update record 24 | function update(r, u) { 25 | return typeof u === 'function' ? 26 | update(r, u(r)) : 27 | Object.assign({}, r, u); 28 | } 29 | 30 | // Environment functions 31 | 32 | function jump(dt) { 33 | return function (m) { 34 | return dt.y > 0 && m.y === 0 ? 35 | update(m, {vy: 5}) : 36 | m; 37 | }; 38 | } 39 | 40 | function gravity(t) { 41 | return function (m) { 42 | return m.y > 0 ? 43 | update(m, {vy: m.vy - t / 4}) : 44 | m; 45 | }; 46 | } 47 | 48 | function physics(t) { 49 | return function (m) { 50 | return update(m, { 51 | x: m.x + t * m.vx, 52 | y: Math.max(0, m.y + t * m.vy) 53 | }); 54 | }; 55 | } 56 | 57 | function walk(dt) { 58 | return function (m) { 59 | var dir = m.dir; 60 | if (dt.x < 0) { 61 | dir = 'left'; 62 | } else if (dt.x > 0) { 63 | dir = 'right'; 64 | } 65 | 66 | return update(m, { 67 | vx: dt.x, 68 | dir: dir 69 | }); 70 | }; 71 | } 72 | 73 | function step(dt, keys) { 74 | return compose(jump(keys), gravity(dt), walk(keys), physics(dt)); 75 | } 76 | 77 | // Render 78 | function render(dimensions, mario, marioImage) { 79 | var verb = 'stand'; 80 | if (mario.y > 0) { 81 | verb = 'jump'; 82 | } else if (mario.vx !== 0) { 83 | verb = 'walk'; 84 | } 85 | 86 | var src = 'img/' + verb + '-' + mario.dir + '.gif'; 87 | 88 | // gif animations reset on src assignment 89 | if(marioImage.name !== src) { 90 | marioImage.src = src; 91 | marioImage.name = src; 92 | } 93 | 94 | marioImage.style.left = (mario.x + dimensions.width / 2) + 'px'; 95 | marioImage.style.top = (dimensions.height - 91 - mario.y) + 'px'; 96 | } 97 | 98 | // Elm's FPS 99 | function fps(v) { 100 | return Observable.interval(1000 / v) 101 | .timestamp() 102 | .bufferWithCount(2, 1) 103 | .map(function (w) { return w[1].timestamp - w[0].timestamp; }) 104 | .share(); 105 | } 106 | 107 | function keysBuffer(buffer, e) { 108 | var result = buffer.slice(0); 109 | if (e.type === 'keydown') { 110 | if(buffer.indexOf(e.keyCode) === -1) { 111 | result.push(e.keyCode); 112 | } 113 | } else { 114 | result = buffer.filter(function (keyCode) {return keyCode !== e.keyCode; }); 115 | } 116 | 117 | return result; 118 | } 119 | 120 | // Set up the environment 121 | 122 | var dimensions = dom.resize(window) 123 | .map(function (e) { 124 | return { 125 | width: e.target.innerWidth, 126 | height: e.target.innerHeight 127 | }; 128 | }) 129 | .shareValue({ 130 | width: window.innerWidth, 131 | height: window.innerHeight 132 | }); 133 | 134 | var keyDowns = dom.keydown(document); 135 | var keyUps = dom.keyup(document); 136 | 137 | // array of currently pressed keys 138 | var keyboard = keyDowns 139 | .merge(keyUps) 140 | .scan(keysBuffer, []) 141 | .distinctUntilChanged() 142 | .shareValue([]); 143 | 144 | // LEFT: 37 145 | // UP: 38 146 | // RIGHT: 39 147 | // UP: 40 148 | var arrowsMap = { 149 | 37: {x: -1, y: 0}, 150 | 39: {x: 1, y: 0}, 151 | 38: {x: 0, y: 1}, 152 | 40: {x: 0, y: -1} 153 | }; 154 | 155 | // Elm's Keyboard.arrows 156 | var arrows = keyboard 157 | .map(function (keys) { 158 | return keys 159 | .filter(function (key) { return key in arrowsMap; }) 160 | .reduce(function (agg, key) { 161 | return update(agg, function (prev) { 162 | return { 163 | x: prev.x + arrowsMap[key].x, 164 | y: prev.y + arrowsMap[key].y 165 | }; 166 | }); 167 | }, {x: 0, y: 0}); 168 | }); 169 | 170 | var deltas = fps(60).map(function (t) { return t / 20; }); 171 | var input = deltas 172 | .combineLatest(arrows, function (dt, keys) { return { dt: dt, keys: keys }; }) 173 | .sample(deltas); 174 | 175 | var marioImage = document.getElementById('mario'); 176 | 177 | // mario 178 | var mario = { 179 | x: 0, 180 | y: 0, 181 | vx: 0, 182 | vy: 0, 183 | dir: 'right' 184 | }; 185 | 186 | var marios = input.scan(function (m, c) { 187 | return step(c.dt, c.keys)(m); 188 | }, mario); 189 | 190 | marios 191 | .combineLatest(dimensions, function (mario, dimensions) { 192 | return { mario: mario, dimensions: dimensions }; 193 | }) 194 | .subscribe( 195 | function (c) { render(c.dimensions, c.mario, marioImage); }, 196 | function (err) { console.log(err); } 197 | ); 198 | }()); -------------------------------------------------------------------------------- /debt-calculator/index.js: -------------------------------------------------------------------------------- 1 | ;(function() { 2 | 3 | 'use strict'; 4 | 5 | var amount = document.querySelector('#amount'); 6 | var rate = document.querySelector('#rate'); 7 | var years = document.querySelector('#years'); 8 | var result = document.querySelector('#result'); 9 | 10 | function calculateDebt(amount, rate, years) { 11 | return parseInt(amount) + (amount / 100 * rate) * years; 12 | } 13 | 14 | // Helper function to get a stream of values from each input. 15 | function inputStreamGenerator(element) { 16 | return Rx.Observable.fromEvent(element, 'input') 17 | .map(function(e) { 18 | return e.target.value; 19 | }) 20 | .startWith(element.value); 21 | } 22 | 23 | function applyValue(value) { 24 | this.innerHTML = 'Total Debt: $' + value; 25 | } 26 | 27 | var amount$ = inputStreamGenerator(amount); 28 | var rate$ = inputStreamGenerator(rate); 29 | var years$ = inputStreamGenerator(years); 30 | 31 | // observer listen to observable for changes and applies new value. 32 | var observer = applyValue.bind(result); 33 | 34 | // Merge the latest values from each stream and update the output result. 35 | Rx.Observable.combineLatest( 36 | amount$, 37 | rate$, 38 | years$, 39 | calculateDebt 40 | ).subscribe(observer); 41 | 42 | 43 | function pmt(interestRate, numberOfPeriods, presentValue, futureValue, type) { 44 | /* 45 | * interestRate - interest rate per month 46 | * numberOfPeriods - number of periods (months) 47 | * presentValue - present value 48 | * futureValue - future value 49 | * type - when the payments are due: 50 | * 0: end of the period, e.g. end of month (default) 51 | * 1: beginning of period 52 | */ 53 | var payment, pvif; 54 | 55 | futureValue || (futureValue = 0); 56 | type || (type = 0); 57 | 58 | if (interestRate === 0) 59 | return -(presentValue + futureValue) / numberOfPeriods; 60 | 61 | pvif = Math.pow(1 + interestRate, numberOfPeriods); 62 | payment = - interestRate * presentValue * (pvif + futureValue) / (pvif -1); 63 | 64 | if (type === 1) 65 | payment /= (1+interestRate); 66 | 67 | return payment; 68 | } 69 | 70 | 71 | var monthly = pmt(0.035/12, 10*12, -190000); 72 | console.log('----------- Monthly --------------: ', monthly); 73 | 74 | console.clear(); 75 | 76 | var average$ = Rx.Observable.range(2, 5) 77 | .reduce(function(prev, cur) { 78 | console.log('cur: ', cur); 79 | return { 80 | sum: prev.sum + cur, 81 | count: prev.count + 1 82 | }; 83 | }, { sum: 0, count: 0 }) 84 | .map(function(o) { 85 | console.log('Count: ', o.count); 86 | console.log('Sum: ', o.sum); 87 | return o.sum / o.count; 88 | }); 89 | 90 | var subscription = average$.subscribe(function(result) { 91 | console.log('Average Result: ', result); 92 | }); 93 | 94 | console.clear(); 95 | 96 | /** 97 | * PROBLEM: Calculate the average speed while users walking. 98 | * 99 | * Even if the user has not finished walking, we need to be able to make a 100 | * calculation using the speed values we know so far. We want to log the average of an infinite sequence in real time. 101 | * The problem is that if the sequence never ends, an aggregate operator like reduce will never call its Observers’ 102 | * onNext operator* 103 | * 104 | */ 105 | var averageSpeed$ = Rx.Observable.interval(1000) 106 | .scan(function(prev, cur) { 107 | return { 108 | sum: prev.sum + cur, 109 | count: prev.count + 1 110 | } 111 | }, { sum: 0, count: 0 }) 112 | .map(function(res) { 113 | return res.sum / res.count; 114 | }); 115 | 116 | var averageSpeedSubscription = averageSpeed$.subscribe(function(data) { 117 | // console.log('Average Speed User is walking: ', data); 118 | }); 119 | 120 | // console.log(averageSpeedSubscription); 121 | setTimeout(function() { 122 | // console.log('Cancel!!'); 123 | averageSpeedSubscription.dispose(); 124 | }, 2000); 125 | 126 | console.clear(); 127 | 128 | /** 129 | * `concatAll` Implementation 130 | * @example 131 | * concatAll([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) => [0, 1, 2, 3, 4, 5, 6, 7, 8] 132 | */ 133 | function concatAll(source) { 134 | return source.reduce(function(a, b) { 135 | return a.concat(b); 136 | }); 137 | } 138 | var flatten = concatAll([[0, 1, 2], [3, 4, 5], [6, 7, 8]]); 139 | console.log('Flatten array dimension: ', flatten); 140 | console.clear(); 141 | 142 | 143 | var p = new Promise(function(resolve, reject) { 144 | window.setTimeout(resolve, 1000); 145 | }); 146 | 147 | p.then(function() { 148 | // console.log('Potential side effect!'); 149 | }); 150 | 151 | var pSub = Rx.Observable.fromPromise(p).subscribe(function(msg) { 152 | console.log('Observable resolved: ', msg); 153 | }); 154 | pSub.dispose(); 155 | 156 | 157 | /** 158 | * Errors Handling in Observable 159 | */ 160 | function getJSON(arr) { 161 | return Rx.Observable.from(arr).map(function(str) { 162 | return JSON.parse(str); 163 | }); 164 | } 165 | 166 | var caught$ = getJSON(['{"1": 1, "2": 2}', '{"1: 1}']) 167 | .catch(Rx.Observable.return( 168 | { error: 'There was an error parsing JSON' } 169 | ) 170 | ); 171 | 172 | caught$.subscribe(function(data) { 173 | console.log('Success: ', data); 174 | }, function(e) { 175 | console.log('ERROR: ', e); 176 | }) 177 | 178 | 179 | 180 | })(); -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/scripts/build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'production'; 5 | process.env.NODE_ENV = 'production'; 6 | 7 | // Makes the script crash on unhandled rejections instead of silently 8 | // ignoring them. In the future, promise rejections that are not handled will 9 | // terminate the Node.js process with a non-zero exit code. 10 | process.on('unhandledRejection', err => { 11 | throw err; 12 | }); 13 | 14 | // Ensure environment variables are read. 15 | require('../config/env'); 16 | 17 | const path = require('path'); 18 | const chalk = require('chalk'); 19 | const fs = require('fs-extra'); 20 | const webpack = require('webpack'); 21 | const config = require('../config/webpack.config.prod'); 22 | const paths = require('../config/paths'); 23 | const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); 24 | const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); 25 | const printHostingInstructions = require('react-dev-utils/printHostingInstructions'); 26 | const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); 27 | const printBuildError = require('react-dev-utils/printBuildError'); 28 | 29 | const measureFileSizesBeforeBuild = 30 | FileSizeReporter.measureFileSizesBeforeBuild; 31 | const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; 32 | const useYarn = fs.existsSync(paths.yarnLockFile); 33 | 34 | // These sizes are pretty large. We'll warn for bundles exceeding them. 35 | const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; 36 | const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; 37 | 38 | // Warn and crash if required files are missing 39 | if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { 40 | process.exit(1); 41 | } 42 | 43 | // First, read the current file sizes in build directory. 44 | // This lets us display how much they changed later. 45 | measureFileSizesBeforeBuild(paths.appBuild) 46 | .then(previousFileSizes => { 47 | // Remove all content but keep the directory so that 48 | // if you're in it, you don't end up in Trash 49 | fs.emptyDirSync(paths.appBuild); 50 | // Merge with the public folder 51 | copyPublicFolder(); 52 | // Start the webpack build 53 | return build(previousFileSizes); 54 | }) 55 | .then( 56 | ({ stats, previousFileSizes, warnings }) => { 57 | if (warnings.length) { 58 | console.log(chalk.yellow('Compiled with warnings.\n')); 59 | console.log(warnings.join('\n\n')); 60 | console.log( 61 | '\nSearch for the ' + 62 | chalk.underline(chalk.yellow('keywords')) + 63 | ' to learn more about each warning.' 64 | ); 65 | console.log( 66 | 'To ignore, add ' + 67 | chalk.cyan('// eslint-disable-next-line') + 68 | ' to the line before.\n' 69 | ); 70 | } else { 71 | console.log(chalk.green('Compiled successfully.\n')); 72 | } 73 | 74 | console.log('File sizes after gzip:\n'); 75 | printFileSizesAfterBuild( 76 | stats, 77 | previousFileSizes, 78 | paths.appBuild, 79 | WARN_AFTER_BUNDLE_GZIP_SIZE, 80 | WARN_AFTER_CHUNK_GZIP_SIZE 81 | ); 82 | console.log(); 83 | 84 | const appPackage = require(paths.appPackageJson); 85 | const publicUrl = paths.publicUrl; 86 | const publicPath = config.output.publicPath; 87 | const buildFolder = path.relative(process.cwd(), paths.appBuild); 88 | printHostingInstructions( 89 | appPackage, 90 | publicUrl, 91 | publicPath, 92 | buildFolder, 93 | useYarn 94 | ); 95 | }, 96 | err => { 97 | console.log(chalk.red('Failed to compile.\n')); 98 | printBuildError(err); 99 | process.exit(1); 100 | } 101 | ); 102 | 103 | // Create the production build and print the deployment instructions. 104 | function build(previousFileSizes) { 105 | console.log('Creating an optimized production build...'); 106 | 107 | let compiler = webpack(config); 108 | return new Promise((resolve, reject) => { 109 | compiler.run((err, stats) => { 110 | if (err) { 111 | return reject(err); 112 | } 113 | const messages = formatWebpackMessages(stats.toJson({}, true)); 114 | if (messages.errors.length) { 115 | // Only keep the first error. Others are often indicative 116 | // of the same problem, but confuse the reader with noise. 117 | if (messages.errors.length > 1) { 118 | messages.errors.length = 1; 119 | } 120 | return reject(new Error(messages.errors.join('\n\n'))); 121 | } 122 | if ( 123 | process.env.CI && 124 | (typeof process.env.CI !== 'string' || 125 | process.env.CI.toLowerCase() !== 'false') && 126 | messages.warnings.length 127 | ) { 128 | console.log( 129 | chalk.yellow( 130 | '\nTreating warnings as errors because process.env.CI = true.\n' + 131 | 'Most CI servers set it automatically.\n' 132 | ) 133 | ); 134 | return reject(new Error(messages.warnings.join('\n\n'))); 135 | } 136 | return resolve({ 137 | stats, 138 | previousFileSizes, 139 | warnings: messages.warnings, 140 | }); 141 | }); 142 | }); 143 | } 144 | 145 | function copyPublicFolder() { 146 | fs.copySync(paths.appPublic, paths.appBuild, { 147 | dereference: true, 148 | filter: file => file !== paths.appHtml, 149 | }); 150 | } 151 | -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/config/webpackDevServer.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware'); 4 | const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware'); 5 | const ignoredFiles = require('react-dev-utils/ignoredFiles'); 6 | const config = require('./webpack.config.dev'); 7 | const paths = require('./paths'); 8 | 9 | const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; 10 | const host = process.env.HOST || '0.0.0.0'; 11 | 12 | module.exports = function(proxy, allowedHost) { 13 | return { 14 | // WebpackDevServer 2.4.3 introduced a security fix that prevents remote 15 | // websites from potentially accessing local content through DNS rebinding: 16 | // https://github.com/webpack/webpack-dev-server/issues/887 17 | // https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a 18 | // However, it made several existing use cases such as development in cloud 19 | // environment or subdomains in development significantly more complicated: 20 | // https://github.com/facebookincubator/create-react-app/issues/2271 21 | // https://github.com/facebookincubator/create-react-app/issues/2233 22 | // While we're investigating better solutions, for now we will take a 23 | // compromise. Since our WDS configuration only serves files in the `public` 24 | // folder we won't consider accessing them a vulnerability. However, if you 25 | // use the `proxy` feature, it gets more dangerous because it can expose 26 | // remote code execution vulnerabilities in backends like Django and Rails. 27 | // So we will disable the host check normally, but enable it if you have 28 | // specified the `proxy` setting. Finally, we let you override it if you 29 | // really know what you're doing with a special environment variable. 30 | disableHostCheck: 31 | !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true', 32 | // Enable gzip compression of generated files. 33 | compress: true, 34 | // Silence WebpackDevServer's own logs since they're generally not useful. 35 | // It will still show compile warnings and errors with this setting. 36 | clientLogLevel: 'none', 37 | // By default WebpackDevServer serves physical files from current directory 38 | // in addition to all the virtual build products that it serves from memory. 39 | // This is confusing because those files won’t automatically be available in 40 | // production build folder unless we copy them. However, copying the whole 41 | // project directory is dangerous because we may expose sensitive files. 42 | // Instead, we establish a convention that only files in `public` directory 43 | // get served. Our build script will copy `public` into the `build` folder. 44 | // In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%: 45 | // 46 | // In JavaScript code, you can access it with `process.env.PUBLIC_URL`. 47 | // Note that we only recommend to use `public` folder as an escape hatch 48 | // for files like `favicon.ico`, `manifest.json`, and libraries that are 49 | // for some reason broken when imported through Webpack. If you just want to 50 | // use an image, put it in `src` and `import` it from JavaScript instead. 51 | contentBase: paths.appPublic, 52 | // By default files from `contentBase` will not trigger a page reload. 53 | watchContentBase: true, 54 | // Enable hot reloading server. It will provide /sockjs-node/ endpoint 55 | // for the WebpackDevServer client so it can learn when the files were 56 | // updated. The WebpackDevServer client is included as an entry point 57 | // in the Webpack development configuration. Note that only changes 58 | // to CSS are currently hot reloaded. JS changes will refresh the browser. 59 | hot: true, 60 | // It is important to tell WebpackDevServer to use the same "root" path 61 | // as we specified in the config. In development, we always serve from /. 62 | publicPath: config.output.publicPath, 63 | // WebpackDevServer is noisy by default so we emit custom message instead 64 | // by listening to the compiler events with `compiler.plugin` calls above. 65 | quiet: true, 66 | // Reportedly, this avoids CPU overload on some systems. 67 | // https://github.com/facebookincubator/create-react-app/issues/293 68 | // src/node_modules is not ignored to support absolute imports 69 | // https://github.com/facebookincubator/create-react-app/issues/1065 70 | watchOptions: { 71 | ignored: ignoredFiles(paths.appSrc), 72 | }, 73 | // Enable HTTPS if the HTTPS environment variable is set to 'true' 74 | https: protocol === 'https', 75 | host: host, 76 | overlay: false, 77 | historyApiFallback: { 78 | // Paths with dots should still use the history fallback. 79 | // See https://github.com/facebookincubator/create-react-app/issues/387. 80 | disableDotRule: true, 81 | }, 82 | public: allowedHost, 83 | proxy, 84 | before(app) { 85 | // This lets us open files from the runtime error overlay. 86 | app.use(errorOverlayMiddleware()); 87 | // This service worker file is effectively a 'no-op' that will reset any 88 | // previous service worker registered for the same host:port combination. 89 | // We do this in development to avoid hitting the production cache if 90 | // it used the same host and port. 91 | // https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432 92 | app.use(noopServiceWorkerMiddleware()); 93 | }, 94 | }; 95 | }; 96 | -------------------------------------------------------------------------------- /common/js/RxJS/rx.selectors.js: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | 3 | var dependencies = ['rx', 'underscore']; 4 | 5 | var freeExports = typeof exports == 'object' && exports, 6 | freeModule = typeof module == 'object' && module && module.exports == freeExports && module, 7 | freeGlobal = typeof global == 'object' && global; 8 | if (freeGlobal.global === freeGlobal) { 9 | window = freeGlobal; 10 | } 11 | 12 | // Because of build optimizers 13 | if (typeof define === 'function' && define.amd) { 14 | define(dependencies, function(Rx, _) { 15 | root.Rx = factory(root, Rx, _); 16 | return root.Rx; 17 | }); 18 | } else if (typeof module === 'object' && module && module.exports === freeExports) { 19 | module.exports = factory.apply(root, [root].concat(dependencies.map(require))); 20 | } else { 21 | root.Rx = factory(root, root.Rx, root._); 22 | } 23 | }(this, function(global, Rx, _, undefined) { 24 | 25 | var selectors = { 26 | i: identity, 27 | k: value, 28 | identity: identity, 29 | value: value, 30 | keyOf: keyOf, 31 | a: apply, 32 | apply: apply, 33 | args: args, 34 | tuple: tuple, 35 | merge: merge, 36 | 37 | print: print, 38 | 39 | sequence: sequence, 40 | fork: fork, 41 | not: not, 42 | pluck: pluck, 43 | 44 | first: first, 45 | last: last, 46 | 47 | and: and, 48 | or: or, 49 | 50 | eq: eq, 51 | gt: gt, 52 | gte: gte, 53 | lt: lt, 54 | lte: lte 55 | }; 56 | 57 | Rx.selectors = selectors; 58 | 59 | return Rx; 60 | 61 | function identity() { 62 | return arguments[0]; 63 | } 64 | 65 | function value(val) { 66 | return function() { 67 | return val; 68 | } 69 | } 70 | 71 | function keyOf(hash) { 72 | return function(key) { 73 | return _.has(hash, key); 74 | } 75 | } 76 | 77 | function apply(fn, context) { 78 | return function(arr) { 79 | return fn.apply(context, _.isArray(arr) ? arr : arguments); 80 | } 81 | } 82 | 83 | function args() { 84 | return _.toArray(arguments); 85 | } 86 | 87 | function tuple(a, b) { 88 | return [a, b]; 89 | } 90 | 91 | function first() { 92 | return arguments[0]; 93 | } 94 | 95 | function last() { 96 | return arguments[arguments.length - 1]; 97 | } 98 | 99 | function merge() { 100 | return _.flatten(arguments, true); 101 | } 102 | 103 | function sequence() { 104 | var fns = _.toArray(arguments); 105 | return function(value) { 106 | return _.reduce(fns, function(memo, fn) { 107 | return fn(memo); 108 | }, value); 109 | } 110 | } 111 | 112 | function fork() { 113 | var fns = _.toArray(arguments); 114 | return function() { 115 | var args = arguments; 116 | return _.map(fns, function(fn) { 117 | return fn.apply(null, args); 118 | }); 119 | } 120 | } 121 | 122 | function print() { 123 | var messages = _.toArray(arguments) || []; 124 | return function() { 125 | var values = _.toArray(arguments) || []; 126 | console.log.apply(console, messages.concat(values)); 127 | return values[0]; 128 | } 129 | } 130 | 131 | function not(fn, context) { 132 | return function() { 133 | return !fn.apply(context, arguments); 134 | } 135 | } 136 | 137 | function pluck() { 138 | 139 | var invocations = _.toArray(arguments), 140 | invocations = _.map(invocations, mapArrayInv), 141 | invocations = _.map(_.flatten(invocations), mapStringInv('.')), 142 | invocations = _.map(_.flatten(invocations), mapObjectInv), 143 | invocations = _.map(_.flatten(invocations), mapInvocationFns); 144 | 145 | return _.compose.apply(_, invocations.reverse()); 146 | } 147 | 148 | function or() { 149 | var fns = _.toArray(arguments); 150 | return function() { 151 | var args = arguments; 152 | return _.detect(fns, function(fn) { 153 | return fn.apply(null, args); 154 | }); 155 | } 156 | } 157 | 158 | function and() { 159 | var fns = _.toArray(arguments); 160 | return function() { 161 | var args = arguments; 162 | return _.every(fns, function(fn) { 163 | return fn.apply(null, args); 164 | }); 165 | } 166 | } 167 | 168 | function eq(valueToMatch) { 169 | return function(value) { 170 | return value === valueToMatch; 171 | } 172 | } 173 | 174 | function gt(smallerValue) { 175 | return function(largerValue) { 176 | return largerValue > smallerValue; 177 | } 178 | } 179 | 180 | function gte(smallerOrEqualValue) { 181 | return function(largerOrEqualValue) { 182 | return largerOrEqualValue >= smallerOrEqualValue; 183 | } 184 | } 185 | 186 | function lt(largerValue) { 187 | return function(smallerValue) { 188 | return smallerValue < largerValue; 189 | } 190 | } 191 | 192 | function lte(largerOrEqualValue) { 193 | return function(smallerOrEqualValue) { 194 | return smallerOrEqualValue <= largerOrEqualValue; 195 | } 196 | } 197 | 198 | function mapStringInv(delimiter) { 199 | return function(item) { 200 | return typeof item === 'string' ? 201 | item.split(delimiter) : 202 | item; 203 | } 204 | } 205 | 206 | function mapArrayInv(item) { 207 | 208 | if (_.isArray(item) === false) return item; 209 | 210 | var args = item.length > 3 ? 211 | _.take(item, 2) : 212 | item.length == 3 ? 213 | item[2] : []; 214 | 215 | return { 216 | isInvocation: true, 217 | context: item[0], 218 | name: item[1], 219 | args: _.isArray(args) ? args : [args] 220 | }; 221 | } 222 | 223 | function mapObjectInv(item) { 224 | 225 | if (_.isPlainObject(item) === false) return item; 226 | if (item.isInvocation === true) return item; 227 | 228 | return _.map(item, function(val, key) { 229 | return { 230 | name: key, 231 | args: _.isArray(val) ? val : [val] 232 | }; 233 | }); 234 | } 235 | 236 | function mapInvocationFns(invocation) { 237 | 238 | if (_.isString(invocation) === true) { 239 | return function(item) { 240 | var val = item[invocation]; 241 | 242 | return _.isFunction(val) ? 243 | val.call(item) : 244 | val; 245 | }; 246 | } 247 | 248 | if (_.isFunction(invocation) === true) { 249 | return invocation; 250 | } 251 | 252 | if (_.isPlainObject(invocation) === true) { 253 | return function(item) { 254 | var fn = invocation.name, 255 | args = invocation.args || [item], 256 | context = invocation.context || item, 257 | val = context[fn]; 258 | 259 | return _.isFunction(val) ? 260 | val.apply(context, args) : 261 | val; 262 | } 263 | } 264 | 265 | return function() {}; 266 | } 267 | })); -------------------------------------------------------------------------------- /crop/crop.js: -------------------------------------------------------------------------------- 1 | ;(function(window) { 2 | 3 | 'use strict'; 4 | 5 | var boundingBox, // a model for the region of the image to be cropped. 6 | handles = [],// an array of draggable elements that modify the crop region. 7 | hanleNodes, // we need to handle as a nodeList in order to add multiple event listeners at once. 8 | overlay, // `overlay` is a canvas element that allows us to darken the portion of the image that will be removed. 9 | context; // `context` will the drawing context for `overlay`. 10 | 11 | 12 | /** 13 | * @kind function 14 | * @name loadImage 15 | * @returns {Observable} 16 | */ 17 | function loadImage() { 18 | // `buffer` is a canvas element that displays the actual image to crop. 19 | var buffer = document.getElementById('buffer'); 20 | // `image` is an img element tag we use to load the image, though we never add it to the DOM. 21 | var image = document.createElement('img'); 22 | image.src = 'images/leaf-twirl.jpg'; 23 | 24 | // Returns an observable which fires when the image is loaded. 25 | return Rx.Observable.fromEvent(image, 'load').map(function() { 26 | overlay.width = image.width; 27 | overlay.height = image.height; 28 | 29 | buffer.width = image.width; 30 | buffer.height = image.height; 31 | buffer.getContext('2d').drawImage(image, 0, 0); 32 | 33 | return { 34 | width: image.width, 35 | height: image.height 36 | } 37 | }); 38 | } 39 | 40 | 41 | /** 42 | * @kind function 43 | * @name initBoundingBox 44 | * @param size 45 | * @returns {void} 46 | */ 47 | function initBoundingBox(size) { 48 | boundingBox = { 49 | x: 0, 50 | y: 0, 51 | x2: size.width, 52 | y2: size.height 53 | }; 54 | } 55 | 56 | /** 57 | * @kind function 58 | * @name createHandles 59 | */ 60 | function createHandles() { 61 | var container = document.getElementById('container'); 62 | 63 | function _createHandler(id, render, updateModel) { 64 | var handle = document.createElement('div'); 65 | handle.className += ' handle'; 66 | handle.setAttribute('id', id); 67 | container.appendChild(handle); 68 | 69 | // `render` allows us to visually update the handle after it has been dragged. 70 | handle['render'] = render; 71 | // `updateModel` allows us to modify the correct part of the crop region model. 72 | handle['updateModel'] = updateModel; 73 | 74 | handles.push(handle); 75 | } 76 | 77 | // top left drag event. 78 | _createHandler('tl', function() { 79 | this.style.top = boundingBox.y + 'px'; 80 | this.style.left = boundingBox.x + 'px'; 81 | }, function(x, y) { 82 | boundingBox.x = x; 83 | boundingBox.y = y; 84 | }); 85 | 86 | // top right drag event. 87 | _createHandler('tr', function() { 88 | this.style.top = boundingBox.y + 'px'; 89 | this.style.left = boundingBox.x2 + 'px'; 90 | }, function(x, y) { 91 | boundingBox.y = y; 92 | boundingBox.x2 = x; 93 | }); 94 | 95 | // bottom left drag event. 96 | _createHandler('bl', function() { 97 | this.style.top = boundingBox.y2 + 'px'; 98 | this.style.left = boundingBox.x + 'px'; 99 | }, function(x, y) { 100 | boundingBox.x = x; 101 | boundingBox.y2 = y; 102 | }); 103 | 104 | // bottom right drag event. 105 | _createHandler('br', function() { 106 | this.style.top = boundingBox.y2 + 'px'; 107 | this.style.left = boundingBox.x2 + 'px'; 108 | }, function(x, y) { 109 | boundingBox.x2 = x; 110 | boundingBox.y2 = y; 111 | }); 112 | 113 | // render the handles in their initial positions 114 | handles.forEach(function(element) { 115 | element['render'](); 116 | }); 117 | 118 | // grab handles in a nodeList so we can add mouseDown listeners to all of them at once. 119 | hanleNodes = document.querySelectorAll('.handle'); 120 | } 121 | 122 | /** 123 | * @kind function 124 | * @name respondToGestures 125 | */ 126 | function respondToGestures() { 127 | // check for pointer events 128 | var moves = 'mousemove', 129 | downs = 'mousedown', 130 | ups = 'mouseup'; 131 | 132 | if (window.navigator.pointerEnabled) { 133 | moves = 'pointermove'; 134 | downs = 'pointerdown'; 135 | ups = 'pointerup'; 136 | } 137 | 138 | var fromEvent = Rx.Observable.fromEvent, 139 | move = fromEvent(overlay, moves), 140 | up = fromEvent(document, ups), 141 | down = fromEvent(hanleNodes, downs); 142 | 143 | // when the mouse is down on a handle, returns the handle element. 144 | return down.flatMap(function(handle) { 145 | handle.preventDefault(); 146 | 147 | return move 148 | // we combine the handle element with the position data from the move event. 149 | .map(function(pos) { 150 | return { 151 | element: handle.target, 152 | offsetX: pos.offsetX, 153 | offsetY: pos.offsetY 154 | }; 155 | }) 156 | // However, when the mouse is up (anywhere on the document) then stop the stream. 157 | .takeUntil(up); 158 | }); 159 | } 160 | 161 | /** 162 | * @kind function 163 | * @name drawOverlay 164 | */ 165 | function drawOverlay() { 166 | var x = boundingBox.x, 167 | y = boundingBox.y, 168 | w = boundingBox.x2 - boundingBox.x, 169 | h = boundingBox.y2 - boundingBox.y; 170 | 171 | context.globalCompositeOperation = 'source-over'; 172 | 173 | context.clearRect(0, 0, overlay.width, overlay.height); 174 | context.fillStyle = 'rgba(0,0,0,0.7)'; 175 | context.fillRect(0, 0, overlay.width, overlay.height); 176 | 177 | context.globalCompositeOperation = 'destination-out'; 178 | context.fillStyle = 'rgba(0,0,0,1)'; 179 | context.fillRect(x, y, w, h); 180 | 181 | handles.forEach(function(element) { element['render'](); }); 182 | } 183 | 184 | /** 185 | * @kind function 186 | * @name main 187 | */ 188 | function main() { 189 | overlay = document.getElementById('overlay'); 190 | context = overlay.getContext('2d'); 191 | 192 | var subscription = loadImage() 193 | .flatMap(function(size) { 194 | 195 | // initialize after load 196 | initBoundingBox(size); 197 | createHandles(); 198 | 199 | // return respondToGestures(); 200 | var newPos = respondToGestures(); 201 | return respondToGestures(); 202 | }) 203 | .subscribe(function(data) { 204 | console.log('------ drag & drop position ---: ', data); 205 | // update model and redraw via an async operation 206 | data.element.updateModel(data.offsetX, data.offsetY); 207 | 208 | Rx.Scheduler.requestAnimationFrame.schedule(drawOverlay); 209 | }); 210 | } 211 | 212 | window.onload = function(){ 213 | main(); 214 | } 215 | 216 | 217 | })(window); -------------------------------------------------------------------------------- /real-time/custom.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 3 | 'use strict'; 4 | 5 | var fromEvent = Rx.Observable.fromEvent; 6 | 7 | var updatesOverTime = []; 8 | 9 | var width = 960, 10 | height = 600, 11 | margins = { top: 20, bottom: 50, left: 70, right: 20 }; 12 | 13 | var svgElement = d3.select('svg') 14 | .attr('width', width ) 15 | .attr('height', height + 200 ); 16 | 17 | var xRange = d3.time.scale() 18 | .range([margins.left, width - margins.right ]) 19 | .domain([new Date(), new Date() ]); 20 | 21 | var yRange = d3.time.scale() 22 | .range([height - margins.bottom, margins.top ]) 23 | .domain([0, 0]); 24 | 25 | var xAxis = d3.svg.axis() 26 | .scale(xRange) 27 | .tickSize(5) 28 | .tickSubdivide(true) 29 | .tickFormat(d3.time.format('%x')); 30 | 31 | var yAxis = d3.svg.axis() 32 | .scale(yRange) 33 | .tickSize(5) 34 | .orient('left') 35 | .tickSubdivide(true); 36 | 37 | var xAxisElement = svgElement.append('g') 38 | .attr('class', 'x axis') 39 | .attr('transform', 'translate(0,' + (height - margins.bottom ) + ')') 40 | .call(xAxis); 41 | 42 | /* 43 | Adds a label to the middle of the xAxis. 44 | */ 45 | var xAxisWidth = ((width - margins.right) - margins.left) / 2; 46 | xAxisElement.append('text') 47 | .attr('x', margins.left + xAxisWidth ) 48 | .attr('y', 0) 49 | .attr('dy', '3em') 50 | .style('text-anchor', 'middle') 51 | .text('Time'); 52 | 53 | var yAxisElement = svgElement.append('g') 54 | .attr('class', 'y axis') 55 | .attr('transform', 'translate(' + margins.left + ',0)') 56 | .call(yAxis); 57 | 58 | /* 59 | Adds a label to the middle of the y axis. 60 | */ 61 | var yAxisHeight = ((height - margins.bottom) - margins.top) / 2; 62 | yAxisElement.append('text') 63 | .attr('transform', 'rotate(-90)') 64 | .attr('y', 0) 65 | .attr('x', -(margins.top + yAxisHeight)) 66 | .attr('dy', '-3.5em') 67 | .style('text-anchor', 'middle') 68 | .text('Updates per second'); 69 | 70 | /* 71 | Defines our line series. 72 | */ 73 | var lineFunc = d3.svg.line() 74 | .x(function(d){ return xRange(d.x); }) 75 | .y(function(d){ return yRange(d.y); }) 76 | .interpolate('linear'); 77 | 78 | svgElement.append('defs') 79 | .append('clipPath') 80 | .attr('id', 'clip') 81 | .append('rect') 82 | .attr('x', margins.left) 83 | .attr('y', margins.top) 84 | .attr('width', width ) 85 | .attr('height', height ) 86 | ; 87 | 88 | var line = svgElement.append('g') 89 | .attr('clip-path', 'url(#clip)') 90 | .append('path') 91 | .attr('stroke', 'blue') 92 | .attr('fill', 'none') 93 | ; 94 | 95 | /* 96 | Adds a text element below the chart, which will 97 | display the subject of new edits. 98 | */ 99 | svgElement.append('text') 100 | .attr('class', 'edit-text') 101 | .attr('transform', 'translate(' + margins.left + ',' + (height + 20) + ')') 102 | .attr('width', width - margins.left ) 103 | ; 104 | 105 | /* 106 | Adds a text element below the chart, which will 107 | display the times that new users are added. 108 | */ 109 | var newUserTextWith = 150; 110 | svgElement.append('text') 111 | .attr('class', 'new-user-text') 112 | .attr('fill', 'green') 113 | .attr('transform', 'translate(' + (width - margins.right - newUserTextWith) + ',' + (height + 20 ) + ')' ) 114 | .attr('width', newUserTextWith ) 115 | ; 116 | 117 | var samplingTime = 2000; 118 | var maxNumberOfDataPoints = 20; 119 | 120 | 121 | /** 122 | * @private 123 | * @method update 124 | * @params updates 125 | */ 126 | function update(updates) { 127 | /* 128 | Updates the ranges of the chart to reflect the new data. 129 | */ 130 | if (updates.length > 0) { 131 | xRange.domain(d3.extent(updates, function(d) { return d.x; })); 132 | yRange.domain([d3.min(updates, function(d) { return d.y; }), 133 | d3.max(updates, function(d) { return d.y }) ]); 134 | } 135 | 136 | /* 137 | Until we have filled up our data window, we just keep adding 138 | data points to the end of the chart. 139 | */ 140 | if (updates.length < maxNumberOfDataPoints) { 141 | line 142 | .transition() 143 | .ease('linear') 144 | .attr('d', lineFunc(updates)); 145 | 146 | svgElement 147 | .selectAll('g.x.axis') 148 | .transition() 149 | .ease('linear') 150 | .call(xAxis); 151 | } 152 | /* 153 | Once we have filled up the window, we then remove points from 154 | the start of the chart, and move the data over so the chart looks 155 | like it is scrolling forwards in time. 156 | */ 157 | else { 158 | 159 | /* 160 | Calculates the amount of translation on the xAxis which 161 | equate to the time between two samples. 162 | */ 163 | var xTranslation = xRange(updates[0].x) - yRange(updates[1].x); 164 | 165 | /* 166 | Transform our line series immediately, then translate it from 167 | right to left. This gives the effect of our chart scrolling 168 | forwards in time. 169 | */ 170 | line 171 | .attr('d', lineFunc(updates)) 172 | .attr('transform', null ) 173 | .transition() 174 | .duration(samplingTime - 20) 175 | .ease('linear') 176 | .attr('transform', 'translate(' + xTranslation + ')') 177 | ; 178 | 179 | svgElement 180 | .selectAll('g.x.axis') 181 | .transition() 182 | .duration(samplingTime - 20) 183 | .ease('linear') 184 | .call(xAxis) 185 | ; 186 | } 187 | 188 | svgElement 189 | .selectAll('g.y.axis') 190 | .transition() 191 | .call(yAxis) 192 | ; 193 | } 194 | 195 | var textUpdateTransitionDuration = 550; 196 | 197 | /** 198 | * @private 199 | * @param newUser 200 | * @method updateNewUser 201 | */ 202 | function updateNewUser(newUser) { 203 | var text = svgElement.selectAll('text.new-user-text').data(newUser); 204 | 205 | text 206 | .transition() 207 | .duration(textUpdateTransitionDuration) 208 | .style('fill-opacity', 1e-6) 209 | .transition() 210 | .duration(textUpdateTransitionDuration) 211 | .style('fill-opacity', 1) 212 | .text(function(d) { 213 | return d; 214 | }) 215 | ; 216 | } 217 | 218 | /** 219 | * @private 220 | * @param latestEdit 221 | * @method updateEditText 222 | */ 223 | function updateEditText(latestEdit) { 224 | var text = svgElement.selectAll('text.edit-text').data(latestEdit); 225 | 226 | text 227 | .transition() 228 | .duration(textUpdateTransitionDuration) 229 | .style('fill-opacity', 1e-6) 230 | .transition() 231 | .duration(textUpdateTransitionDuration) 232 | .text(function(d){ 233 | return d; 234 | }) 235 | ; 236 | } 237 | 238 | 239 | // Create our WebSocket to get wiki updates 240 | var ws = new WebSocket('ws://wiki-update-sockets.herokuapp.com/'); 241 | 242 | var openStream = fromEvent(ws, 'open'); 243 | var closeStream = fromEvent(ws, 'close'); 244 | 245 | var messageStream = fromEvent(ws, 'message').delaySubscription(openStream).takeUntil(closeStream); 246 | 247 | openStream.subscribe(function() { 248 | console.log('Connection opened'); 249 | }); 250 | 251 | closeStream.subscribe(function() { 252 | console.log('Connection closed'); 253 | }); 254 | 255 | var updateStream = messageStream.map(function(event) { 256 | var dataString = event.data; 257 | return JSON.parse(dataString); 258 | }); 259 | 260 | // Filter the 'update' stream for newUser events 261 | var newUserStream = updateStream.filter(function(update) { 262 | return update.type === 'newUser'; 263 | }); 264 | 265 | newUserStream.subscribe(function(results) { 266 | console.log('newUserStream.subscribe() returns: ', results); 267 | var format = d3.time.format('%X'); 268 | updateNewUser(['New user at: ' + format(new Date())]); 269 | }); 270 | 271 | // Filter the 'update' stream for 'unspecified' events, which we're talking 272 | // to mean edits in this case. [ listen for update.edit events ] 273 | var editStream = updateStream.filter(function(update) { 274 | return update.type === 'unspecified'; 275 | }); 276 | editStream.subscribe(function(results) { 277 | console.log('editStream.subscribe() returns: ', results); 278 | updateEditText(['Last edit: ' + results.content]); 279 | }); 280 | 281 | // Calculate the rate of updates over time 282 | var updateCount = updateStream.scan(function(value) { 283 | return ++value; 284 | }, 0); 285 | 286 | var sampleUpdates = updateCount.sample(samplingTime); 287 | var totalUpdatesBeforeLastSample = 0; 288 | sampleUpdates.subscribe(function(value) { 289 | updatesOverTime.push({ 290 | x: new Date(), 291 | y: (value - totalUpdatesBeforeLastSample ) / (samplingTime / 1000) 292 | }); 293 | if (updatesOverTime.length > maxNumberOfDataPoints) { 294 | updatesOverTime.shift(); 295 | } 296 | totalUpdatesBeforeLastSample = value; 297 | update(updatesOverTime); 298 | }); 299 | 300 | 301 | })(); -------------------------------------------------------------------------------- /virtual-list/index.js: -------------------------------------------------------------------------------- 1 | ; (function () { 2 | 'use strict'; 3 | 4 | initScanVelocity(Rx); 5 | initConcatFriction(Rx); 6 | 7 | var sparse = new SparseArray(), 8 | side = "top", 9 | topBoxes = [], 10 | bottomBoxes = [], 11 | boxWidth = 90, boxMargin = 5, 12 | movies = getMovies2(), 13 | movieSizes = movies.map(function (movie) { 14 | return boxWidth; 15 | }); 16 | 17 | sparse.setGap(boxMargin * 2); 18 | 19 | for (var i = -1; ++i < movies.length;) { 20 | sparse.insert(i); 21 | sparse.setItemSize(i, movieSizes[i]); 22 | } 23 | 24 | console.log(sparse.toString()); 25 | 26 | $(function () { 27 | var topContainer = document.getElementsByClassName("top")[0], 28 | bottomContainer = document.getElementsByClassName("bottom")[0], 29 | listPositionInfo = { virtualX: 0, realX: 0, prev: -1 }; 30 | $(window) 31 | .resizeAsObservable() 32 | .throttle(100, Rx.Scheduler.requestAnimationFrame) 33 | .startWith(null) 34 | .doAction(function () { 35 | var box, 36 | rect = topContainer.getBoundingClientRect(), 37 | i = topBoxes.length - 1, 38 | n = 1 + (rect.width / boxWidth) | 0; 39 | 40 | while (++i < n) { 41 | topBoxes[i] = box = new BoxArt(); 42 | topContainer.appendChild(box.div[0]); 43 | bottomBoxes[i] = box = new BoxArt(); 44 | bottomContainer.appendChild(box.div[0]); 45 | } 46 | 47 | i = topBoxes.length + 1; 48 | while (--i > n) { 49 | box = topBoxes.pop().div[0]; 50 | if (topContainer.contains(box)) { 51 | topContainer.removeChild(box); 52 | } 53 | box = bottomBoxes.pop().div[0]; 54 | if (bottomContainer.contains(box)) { 55 | bottomContainer.removeChild(box); 56 | } 57 | } 58 | }) 59 | .flatMapLatest(function () { 60 | return getScrollObs([topContainer, bottomContainer]) 61 | .startWith({ deltaX: 0, deltaY: 0 }) 62 | // .startWith({deltaX:sparse.end(sparse.getLength() - 1) - 200, deltaY:0}) 63 | .scan(listPositionInfo, function (x, e) { 64 | 65 | var virtualX = x.virtualX - e.deltaX, 66 | realX = x.realX - e.deltaX, 67 | index = sparse.indexOf(virtualX), 68 | length = sparse.getLength(), 69 | end = sparse.end(length - 1), 70 | overflow = 0; 71 | 72 | x.virtualX = virtualX; 73 | x.realX = realX; 74 | x.index = index < 0 ? 0 : index >= length ? length - 1 : index; 75 | 76 | if (virtualX < 0) { 77 | overflow = Math.abs((virtualX - boxMargin) / 10); 78 | if (Math.floor(e.velocity) <= 5) { 79 | e.velocity = overflow; 80 | } else if (Math.floor(e.velocity) > overflow) { 81 | e.velocity -= 5; 82 | } 83 | e.angle = Math.PI; 84 | } else if (virtualX > end) { 85 | overflow = Math.abs((virtualX - end + boxMargin) / 10); 86 | if (Math.floor(e.velocity) <= 5) { 87 | e.velocity = overflow; 88 | } else if (Math.floor(e.velocity) > overflow) { 89 | e.velocity -= 5; 90 | } 91 | e.angle = 0; 92 | } 93 | 94 | x.velocity = e.velocity; 95 | x.angle = e.angle; 96 | 97 | return x; 98 | }) 99 | .doAction(function (x) { 100 | translateContainer(topContainer, x.realX * -1); 101 | translateContainer(bottomContainer, x.realX * -1); 102 | }) 103 | .distinctUntilChanged(null, function (b, a) { 104 | return a.prev === a.index ? 105 | (a.prev = a.index) && true : 106 | (a.prev = a.index) && false; 107 | }) 108 | .flatMapLatest(function (x) { 109 | var index = x.index, 110 | videos = side === "top" ? topBoxes : bottomBoxes; 111 | return Rx.Observable 112 | .forkJoin(videos.map(function (box, i) { 113 | return box.load(movies[i + index]); 114 | })) 115 | .doAction(function () { 116 | topContainer.style['z-index'] = Number(side === "top"); 117 | bottomContainer.style['z-index'] = Number(side === "bottom"); 118 | side = side === "bottom" ? "top" : "bottom"; 119 | }) 120 | .defaultIfEmpty() 121 | .select(function () { 122 | return (x.realX = x.virtualX - sparse.start(index)) * -1; 123 | }); 124 | }); 125 | }) 126 | .subscribe(function (x) { 127 | translateContainer(topContainer, x); 128 | translateContainer(bottomContainer, x); 129 | }); 130 | 131 | function translateContainer(container, x) { 132 | var style = container.style; 133 | style["transform"] = 134 | style["webkitTransform"] = "translate3d(" + x + "px, 0, 0)"; 135 | } 136 | }); 137 | 138 | function getScrollObs(containers) { 139 | return $(containers) 140 | .pressAsObservable() 141 | .cancelEvent() 142 | .flatMapLatest(function (x) { 143 | var originX = x.pageX, 144 | originY = x.pageY; 145 | x.absDeltaX = 0; 146 | x.absDeltaY = 0; 147 | return $(window) 148 | .moveAsObservable() 149 | .cancelEvent() 150 | .scan(x, function (a, b) { 151 | b.absDeltaX = a.absDeltaX - (a.pageX - b.pageX); 152 | b.absDeltaY = a.absDeltaY - (a.pageY - b.pageY); 153 | b.x = originX + b.absDeltaX; 154 | b.y = originY + b.absDeltaY; 155 | return b; 156 | }) 157 | .takeUntil($(window).upAsObservable()) 158 | .scanVelocity() 159 | .concatFriction(0.08) 160 | .scan(function (a, b) { 161 | b.deltaX = b.x - a.x; 162 | b.deltaY = b.y - a.y; 163 | return b; 164 | }) 165 | .skip(1) 166 | }); 167 | } 168 | 169 | function getColors() { 170 | return ["red", "green", "blue", "yellow", "purple"]; 171 | } 172 | 173 | function getMovies() { 174 | return ["images/0/822761.jpg", "images/0/829299.jpg", "images/0/829508.jpg", "images/0/830288.jpg", "images/0/840951.jpg", "images/0/841112.jpg", "images/0/841246.jpg", "images/0/841257.jpg", "images/0/841270.jpg", "images/0/841271.jpg", "images/0/841337.jpg", "images/0/850831.jpg", "images/0/851778.jpg", "images/1/868813.jpg", "images/1/868989.jpg", "images/1/868992.jpg", "images/1/868994.jpg", "images/1/869023.jpg", "images/1/869096.jpg", "images/1/869203.jpg", "images/1/869237.jpg", "images/1/869380.jpg", "images/1/869382.jpg", "images/1/869386.jpg", "images/1/869476.jpg", "images/1/869508.jpg", "images/2/1585264.jpg", "images/2/1585360.jpg", "images/2/1641134.jpg", "images/2/869510.jpg", "images/2/869516.jpg", "images/2/869533.jpg", "images/2/869541.jpg", "images/2/869548.jpg", "images/2/872532.jpg", "images/2/925010.jpg", "images/2/925077.jpg", "images/2/928203.jpg", "images/2/929016.jpg", "images/2/930241.jpg", "images/3/1712289.jpg", "images/3/1829772.jpg", "images/3/2036766.jpg", "images/3/2086307.jpg", "images/3/2091847.jpg", "images/3/2171006.jpg", "images/3/2171910.jpg", "images/3/2187391.jpg", "images/3/2316690.jpg", "images/3/2324402.jpg", "images/3/2325775.jpg", "images/3/2365903.jpg", "images/3/2371520.jpg", "images/3/2372627.jpg"]; 175 | } 176 | 177 | function getMovies2() { 178 | return getMovies().reduce(function (a, movie) { 179 | return a.concat(getMovies()); 180 | }, []); 181 | } 182 | 183 | function getMovies3() { 184 | return getMovies2().reduce(function (a, movie) { 185 | return a.concat(getMovies()); 186 | }, []); 187 | } 188 | 189 | function BoxArt() { 190 | this.div = $("
    "); 191 | this.img = $(""); 192 | this.div.append(this.img); 193 | } 194 | 195 | BoxArt.prototype.load = function (url) { 196 | var self = this; 197 | return Rx.Observable.defer(function () { 198 | var obs = self.img.loadAsObservable() 199 | .take(1) 200 | .publishLast(); 201 | obs.connect(); 202 | self.img.attr("src", url); 203 | return obs; 204 | }); 205 | } 206 | 207 | function BoxColor() { 208 | this.div = $("
    ").css('border-width', 10); 209 | } 210 | 211 | BoxColor.prototype.load = function (color) { 212 | this.div.css('border-color', color); 213 | return Rx.Observable.empty(); 214 | } 215 | 216 | BoxColor.prototype.update = function () { 217 | return Rx.Observable.empty(); 218 | } 219 | 220 | function initLargestOfLast(Rx) { 221 | Rx.Observable.prototype.largestOfLast = function (count, largestSelector) { 222 | var list = []; 223 | return this.scan(list, function (list, e) { 224 | list.unshift(e); 225 | return list; 226 | }) 227 | .map(function (xs) { return xs[0]; }) 228 | .skipLast(Number(count > 0)) 229 | .concat(Rx.Observable.defer(function () { 230 | list.length = Math.max(Math.min(list.length, count), 0); 231 | return list.length > 0 ? 232 | Rx.Observable.return(largestSelector(list)) : 233 | Rx.Observable.empty(); 234 | })); 235 | } 236 | } 237 | 238 | function initScanVelocity(Rx) { 239 | Rx.Observable.prototype.scanVelocity = function (xS, yS) { 240 | if (typeof xS !== 'function') 241 | xS = function (m) { return m.x; }; 242 | if (typeof yS !== 'function') 243 | yS = function (m) { return m.y; }; 244 | return this 245 | .timeInterval() 246 | .scan(function (memo, latest) { 247 | var event = latest.value, 248 | interval = latest.interval, 249 | mx = xS(memo.value), 250 | my = yS(memo.value), 251 | ex = xS(event), 252 | ey = yS(event), 253 | vx = (ex - mx) / interval, 254 | vy = (ey - my) / interval; 255 | event.velocity = Math.abs( 256 | Math.sqrt((ex * ex) + (ey * ey)) - 257 | Math.sqrt((mx * mx) + (my * my)) 258 | ); 259 | event.angle = Math.atan2(vy, vx); 260 | return latest; 261 | }) 262 | .skip(1) 263 | .select(function (wrapper) { 264 | return wrapper.value; 265 | }); 266 | } 267 | } 268 | 269 | function initConcatFriction(Rx) { 270 | Rx.Observable.prototype.concatFriction = function (u, xS, yS, vS, aS) { 271 | 272 | if (typeof xS !== 'function') 273 | xS = function (m) { return m.x; }; 274 | if (typeof yS !== 'function') 275 | yS = function (m) { return m.y; }; 276 | if (typeof vS !== 'function') 277 | vS = function (m) { return m.velocity; }; 278 | if (typeof aS !== 'function') 279 | aS = function (m) { return m.angle; }; 280 | 281 | var latest = null; 282 | 283 | return this 284 | .doAction(saveLatest) 285 | .concat(Rx.Observable.defer(fakeDrags)); 286 | 287 | function saveLatest(val) { latest = val; }; 288 | function fakeDrags() { 289 | 290 | if (latest == null) return Rx.Observable.empty(); 291 | 292 | var deceleration = Math.abs(u * 9.8); 293 | 294 | return Rx.Observable.create(function (observer) { 295 | // var scheduler = Rx.Scheduler.timeout; 296 | var scheduler = Rx.Scheduler.requestAnimationFrame; 297 | return scheduler.scheduleRecursive(function (reschedule) { 298 | observer.onNext(0); 299 | reschedule(); 300 | }); 301 | }) 302 | .scan(latest, decelerate) 303 | .takeWhile(hasVelocity); 304 | 305 | function decelerate(memo, b) { 306 | var v = vS(memo), 307 | a = aS(memo), 308 | vx = v * Math.cos(a), 309 | vy = v * Math.sin(a), 310 | x = xS(memo) + vx, 311 | y = yS(memo) + vy; 312 | v -= deceleration; 313 | return { 314 | x: x, 315 | y: y, 316 | angle: a, 317 | velocity: v 318 | }; 319 | } 320 | 321 | function hasVelocity(drag) { 322 | return drag.velocity > 0; 323 | } 324 | } 325 | } 326 | } 327 | })(); -------------------------------------------------------------------------------- /react-demos/react-recompoes-demo/config/webpack.config.dev.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const autoprefixer = require('autoprefixer'); 4 | const path = require('path'); 5 | const webpack = require('webpack'); 6 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 7 | const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); 8 | const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); 9 | const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); 10 | const eslintFormatter = require('react-dev-utils/eslintFormatter'); 11 | const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); 12 | const getClientEnvironment = require('./env'); 13 | const paths = require('./paths'); 14 | 15 | // Webpack uses `publicPath` to determine where the app is being served from. 16 | // In development, we always serve from the root. This makes config easier. 17 | const publicPath = '/'; 18 | // `publicUrl` is just like `publicPath`, but we will provide it to our app 19 | // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. 20 | // Omit trailing slash as %PUBLIC_PATH%/xyz looks better than %PUBLIC_PATH%xyz. 21 | const publicUrl = ''; 22 | // Get environment variables to inject into our app. 23 | const env = getClientEnvironment(publicUrl); 24 | 25 | // This is the development configuration. 26 | // It is focused on developer experience and fast rebuilds. 27 | // The production configuration is different and lives in a separate file. 28 | module.exports = { 29 | // You may want 'eval' instead if you prefer to see the compiled output in DevTools. 30 | // See the discussion in https://github.com/facebookincubator/create-react-app/issues/343. 31 | devtool: 'cheap-module-source-map', 32 | // These are the "entry points" to our application. 33 | // This means they will be the "root" imports that are included in JS bundle. 34 | // The first two entry points enable "hot" CSS and auto-refreshes for JS. 35 | entry: [ 36 | // We ship a few polyfills by default: 37 | require.resolve('./polyfills'), 38 | // Include an alternative client for WebpackDevServer. A client's job is to 39 | // connect to WebpackDevServer by a socket and get notified about changes. 40 | // When you save a file, the client will either apply hot updates (in case 41 | // of CSS changes), or refresh the page (in case of JS changes). When you 42 | // make a syntax error, this client will display a syntax error overlay. 43 | // Note: instead of the default WebpackDevServer client, we use a custom one 44 | // to bring better experience for Create React App users. You can replace 45 | // the line below with these two lines if you prefer the stock client: 46 | // require.resolve('webpack-dev-server/client') + '?/', 47 | // require.resolve('webpack/hot/dev-server'), 48 | require.resolve('react-dev-utils/webpackHotDevClient'), 49 | // Finally, this is your app's code: 50 | paths.appIndexJs, 51 | // We include the app code last so that if there is a runtime error during 52 | // initialization, it doesn't blow up the WebpackDevServer client, and 53 | // changing JS code would still trigger a refresh. 54 | ], 55 | output: { 56 | // Add /* filename */ comments to generated require()s in the output. 57 | pathinfo: true, 58 | // This does not produce a real file. It's just the virtual path that is 59 | // served by WebpackDevServer in development. This is the JS bundle 60 | // containing code from all our entry points, and the Webpack runtime. 61 | filename: 'static/js/bundle.js', 62 | // There are also additional JS chunk files if you use code splitting. 63 | chunkFilename: 'static/js/[name].chunk.js', 64 | // This is the URL that app is served from. We use "/" in development. 65 | publicPath: publicPath, 66 | // Point sourcemap entries to original disk location (format as URL on Windows) 67 | devtoolModuleFilenameTemplate: info => 68 | path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'), 69 | }, 70 | resolve: { 71 | // This allows you to set a fallback for where Webpack should look for modules. 72 | // We placed these paths second because we want `node_modules` to "win" 73 | // if there are any conflicts. This matches Node resolution mechanism. 74 | // https://github.com/facebookincubator/create-react-app/issues/253 75 | modules: ['node_modules', paths.appNodeModules].concat( 76 | // It is guaranteed to exist because we tweak it in `env.js` 77 | process.env.NODE_PATH.split(path.delimiter).filter(Boolean) 78 | ), 79 | // These are the reasonable defaults supported by the Node ecosystem. 80 | // We also include JSX as a common component filename extension to support 81 | // some tools, although we do not recommend using it, see: 82 | // https://github.com/facebookincubator/create-react-app/issues/290 83 | // `web` extension prefixes have been added for better support 84 | // for React Native Web. 85 | extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'], 86 | alias: { 87 | 88 | // Support React Native Web 89 | // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ 90 | 'react-native': 'react-native-web', 91 | }, 92 | plugins: [ 93 | // Prevents users from importing files from outside of src/ (or node_modules/). 94 | // This often causes confusion because we only process files within src/ with babel. 95 | // To fix this, we prevent you from importing files out of src/ -- if you'd like to, 96 | // please link the files into your node_modules/ and let module-resolution kick in. 97 | // Make sure your source files are compiled, as they will not be processed in any way. 98 | new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), 99 | ], 100 | }, 101 | module: { 102 | strictExportPresence: true, 103 | rules: [ 104 | // TODO: Disable require.ensure as it's not a standard language feature. 105 | // We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176. 106 | // { parser: { requireEnsure: false } }, 107 | 108 | // First, run the linter. 109 | // It's important to do this before Babel processes the JS. 110 | { 111 | test: /\.(js|jsx|mjs)$/, 112 | enforce: 'pre', 113 | use: [ 114 | { 115 | options: { 116 | formatter: eslintFormatter, 117 | eslintPath: require.resolve('eslint'), 118 | 119 | }, 120 | loader: require.resolve('eslint-loader'), 121 | }, 122 | ], 123 | include: paths.appSrc, 124 | }, 125 | { 126 | // "oneOf" will traverse all following loaders until one will 127 | // match the requirements. When no loader matches it will fall 128 | // back to the "file" loader at the end of the loader list. 129 | oneOf: [ 130 | // "url" loader works like "file" loader except that it embeds assets 131 | // smaller than specified limit in bytes as data URLs to avoid requests. 132 | // A missing `test` is equivalent to a match. 133 | { 134 | test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], 135 | loader: require.resolve('url-loader'), 136 | options: { 137 | limit: 10000, 138 | name: 'static/media/[name].[hash:8].[ext]', 139 | }, 140 | }, 141 | // Process JS with Babel. 142 | { 143 | test: /\.(js|jsx|mjs)$/, 144 | include: paths.appSrc, 145 | loader: require.resolve('babel-loader'), 146 | options: { 147 | 148 | // This is a feature of `babel-loader` for webpack (not Babel itself). 149 | // It enables caching results in ./node_modules/.cache/babel-loader/ 150 | // directory for faster rebuilds. 151 | cacheDirectory: true, 152 | }, 153 | }, 154 | // "postcss" loader applies autoprefixer to our CSS. 155 | // "css" loader resolves paths in CSS and adds assets as dependencies. 156 | // "style" loader turns CSS into JS modules that inject