├── Intro.png ├── LICENSE ├── README.MD ├── Schematic.png ├── home-config ├── .gitignore ├── LICENSE ├── index.example.js └── package.json ├── home-records ├── LICENSE ├── index.js ├── node_modules │ ├── .yarn-integrity │ └── immutable │ │ ├── LICENSE │ │ ├── PATENTS │ │ ├── README.md │ │ ├── contrib │ │ └── cursor │ │ │ ├── README.md │ │ │ ├── __tests__ │ │ │ └── Cursor.ts.skip │ │ │ ├── index.d.ts │ │ │ └── index.js │ │ ├── dist │ │ ├── immutable-nonambient.d.ts │ │ ├── immutable.d.ts │ │ ├── immutable.js │ │ ├── immutable.js.flow │ │ └── immutable.min.js │ │ └── package.json ├── package.json └── yarn.lock ├── phone-app ├── .babelrc ├── .buckconfig ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── LICENSE ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── homeapp │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── keystores │ │ ├── BUCK │ │ └── debug.keystore.properties │ └── settings.gradle ├── app │ ├── Home.js │ ├── assets │ │ ├── background.png │ │ ├── color_picker.png │ │ ├── home-app-screenshot.png │ │ ├── icon_ac.png │ │ ├── icon_bulb.png │ │ ├── icon_cold.png │ │ ├── icon_cold_white.png │ │ ├── icon_heat.png │ │ └── icon_hot.png │ ├── components │ │ ├── AC │ │ │ ├── AC.js │ │ │ ├── ModeSwitch │ │ │ │ ├── ModeSwitch.js │ │ │ │ └── styles.js │ │ │ └── styles.js │ │ ├── Common │ │ │ ├── Card │ │ │ │ ├── Card.js │ │ │ │ └── styles.js │ │ │ ├── CardHeader │ │ │ │ ├── CardHeader.js │ │ │ │ └── styles.js │ │ │ └── Slider │ │ │ │ └── Slider.js │ │ ├── FloorLED │ │ │ ├── FloorLED.js │ │ │ ├── panBoilerplate.js │ │ │ └── styles.js │ │ ├── Heating │ │ │ └── Heating.js │ │ ├── Loading │ │ │ ├── Loading.js │ │ │ └── styles.js │ │ └── SofaLED │ │ │ └── SofaLED.js │ ├── containers │ │ └── App.js │ ├── package.json │ └── styles.js ├── index.android.js ├── package.json └── yarn.lock └── server ├── .gitignore ├── LICENSE ├── ac_codes └── lircd.conf ├── index.js ├── modules ├── ac.js ├── heating.js ├── led_floor.js └── led_sofa.js ├── package.json ├── scheduled.js ├── utils ├── pi-blaster.js └── temperature.js └── yarn.lock /Intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/Intro.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Viktor Kirilov 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 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Home Automation with RPi and Javascript 2 | 3 | *Updated article: https://viktorkirilov.me/post/home-automation-raspberry-pi/* 4 | 5 | ![Intro](https://raw.githubusercontent.com/deepsyx/home-automation/master/Intro.png) 6 | 7 | Link to video 8 | ======= 9 | 10 | https://youtu.be/wh0OoLUTeM8 11 | 12 | [![Alt text](http://i.imgur.com/7ZGu5tX.png)](https://youtu.be/wh0OoLUTeM8) 13 | 14 | Introduction 15 | ======= 16 | Since a kid I've been passionate about electronic and different kind of mechanisms. Previously I've done different small projects with arduino, like handbrake for my gaming steering wheel, custom LED tachometer for my car (see my github repo), voice controlled LEDs and so on. Recently I bought Raspberry PI 3 and around all the IoT hype, I decided to start automating my home. 17 | 18 | The Technology Stack 19 | ======= 20 | Since I'm pretty familiar with javascript and its ecosystem I decided to create my own phone app via `react native` and connect it via `websockets` to `NodeJS` backend. I'm using ImmutableJS's Records for consistency of the data structure and the performance of the app. The data is sent between the client and the server via JSON strings, which are converted to back to Immutable records, when received on both sides. I've separated the shared modules(between client and server) to a separate local npm packages - home-records and home-config. For GPIO controlling (pins of the raspberry) I've used `pi-blaster` library, together with `wiringPi`. Since I couldn't find codes for my AC, I've recorded them by myself and added them to `lirc` config file. `Duckdns` cron is used as dynamic dns, because my IP address changes periodically and I won't have to recompile the whole app or change config each time. The server broadcasts the changes to all connected users realtime. The phone app is located in **/phone-app** folder, the server is located in **/server**. Why don't you take a look? 21 | 22 | The Hardware 23 | ======= 24 | `Raspberry Pi 3` is the controlling unit. High-power devices, such as LED strips, are powered via external [`12V 5A power supply`](http://img.dxcdn.com/productimages/sku_152373_2.jpg) (shared ground with the PI). The AC is controlled via 950nm infrared LED. The heating system is wired via [relay](http://img.dxcdn.com/productimages/sku_121354_2.jpg) (on and off) and [servo](https://cdn.instructables.com/FCM/8DKT/IBXMML6A/FCM8DKTIBXMML6A.MEDIUM.jpg) for the thermostat as I was avoid disassembling. Both [white LED strip](http://www.saving-star.com/wp-content/uploads/2015/08/Flexible-Top-LED-Strips-FL-F5060W15F12-12-.jpg) and [the RGB strip](http://www.ledssuperbright.com/images/ledstrip5ft.jpg) are connected via `IRLZ34N` logical transistors to the PI. There is also [IR receiver](https://i.stack.imgur.com/rojKP.jpg), just in case I want to record anything else. Temperature is measured via DS18B20 temperature sensor. 25 | 26 | The wiring looks like: 27 | 28 | ![Schematic](https://raw.githubusercontent.com/deepsyx/home-automation/master/Schematic.png) 29 | 30 | New Ideas 31 | ======= 32 | I'm open for all kind of ideas about different modules. If you have some ideas in mind, I'll be thankful if you add it in the issue tracker and label it as `Idea`. 33 | 34 | Software dependencies 35 | ======== 36 | * Node & Npm 37 | * Pi-Blaster 38 | * WiringPi 39 | * Lirc 40 | -------------------------------------------------------------------------------- /Schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/Schematic.png -------------------------------------------------------------------------------- /home-config/.gitignore: -------------------------------------------------------------------------------- 1 | index.js 2 | -------------------------------------------------------------------------------- /home-config/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Viktor Kirilov 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 | -------------------------------------------------------------------------------- /home-config/index.example.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | panda: true, 3 | bear: true, 4 | authToken: '', 5 | serverUri: '', 6 | }; 7 | -------------------------------------------------------------------------------- /home-config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "home-config", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Viktor Kirilov (deepsybg@gmail.com)", 10 | "license": "MIT" 11 | } 12 | -------------------------------------------------------------------------------- /home-records/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Viktor Kirilov 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 | -------------------------------------------------------------------------------- /home-records/index.js: -------------------------------------------------------------------------------- 1 | const Record = require('immutable').Record; 2 | 3 | // @todo: separate records to different files 4 | 5 | const LedFloor = Record({ 6 | r: 0, 7 | g: 0, 8 | b: 0, 9 | isEnabled: false, 10 | }); 11 | 12 | const LedSofa = Record({ 13 | value: 100, 14 | isEnabled: false, 15 | }); 16 | 17 | const AC = Record({ 18 | mode: 'heat', 19 | fanspeed: 'max', 20 | temperature: '30', 21 | isEnabled: false, 22 | }); 23 | 24 | const Heating = Record({ 25 | value: 0, 26 | isEnabled: false, 27 | }); 28 | 29 | const Temperature = Record({ 30 | temperature: 0, 31 | }); 32 | 33 | const Data = Record({ 34 | Temperature: new Temperature(), 35 | LedFloor: new LedFloor(), 36 | LedSofa: new LedSofa(), 37 | AC: new AC(), 38 | Heating: new Heating(), 39 | }); 40 | 41 | module.exports = { 42 | Modules: { 43 | LedFloor, 44 | LedSofa, 45 | AC, 46 | Heating, 47 | Temperature, 48 | }, 49 | Data, 50 | }; 51 | -------------------------------------------------------------------------------- /home-records/node_modules/.yarn-integrity: -------------------------------------------------------------------------------- 1 | a225542d60b682b81a825c5440cd2950d65a9bf592bb8ec4adfb82a55a9a8486 -------------------------------------------------------------------------------- /home-records/node_modules/immutable/LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For Immutable JS software 4 | 5 | Copyright (c) 2014-2015, Facebook, Inc. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name Facebook nor the names of its contributors may be used to 18 | endorse or promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /home-records/node_modules/immutable/PATENTS: -------------------------------------------------------------------------------- 1 | Additional Grant of Patent Rights Version 2 2 | 3 | "Software" means the Immutable JS software distributed by Facebook, Inc. 4 | 5 | Facebook, Inc. (“Facebook”) hereby grants to each recipient of the Software (“you”) a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (subject to the termination provision below) license under any Necessary Claims, to make, have made, use, sell, offer to sell, import, and otherwise transfer the Software. For avoidance of doubt, no license is granted under Facebook’s rights in any patent claims that are infringed by (i) modifications to the Software made by you or any third party or (ii) the Software in combination with any software or other technology. 6 | 7 | The license granted hereunder will terminate, automatically and without notice, if you (or any of your subsidiaries, corporate affiliates or agents) initiate directly or indirectly, or take a direct financial interest in, any Patent Assertion: (i) against Facebook or any of its subsidiaries or corporate affiliates, (ii) against any party if such Patent Assertion arises in whole or in part from any software, technology, product or service of Facebook or any of its subsidiaries or corporate affiliates, or (iii) against any party relating to the Software. Notwithstanding the foregoing, if Facebook or any of its subsidiaries or corporate affiliates files a lawsuit alleging patent infringement against you in the first instance, and you respond by filing a patent infringement counterclaim in that lawsuit against that party that is unrelated to the Software, the license granted hereunder will not terminate under section (i) of this paragraph due to such counterclaim. 8 | 9 | A “Necessary Claim” is a claim of a patent owned by Facebook that is necessarily infringed by the Software standing alone. 10 | 11 | A “Patent Assertion” is any lawsuit or other action alleging direct, indirect, or contributory infringement or inducement to infringe any patent, including a cross-claim or counterclaim. 12 | -------------------------------------------------------------------------------- /home-records/node_modules/immutable/README.md: -------------------------------------------------------------------------------- 1 | Immutable collections for JavaScript 2 | ==================================== 3 | 4 | [![Build Status](https://travis-ci.org/facebook/immutable-js.svg)](https://travis-ci.org/facebook/immutable-js) 5 | 6 | [Immutable][] data cannot be changed once created, leading to much simpler 7 | application development, no defensive copying, and enabling advanced memoization 8 | and change detection techniques with simple logic. [Persistent][] data presents 9 | a mutative API which does not update the data in-place, but instead always 10 | yields new updated data. 11 | 12 | Immutable.js provides many Persistent Immutable data structures including: 13 | `List`, `Stack`, `Map`, `OrderedMap`, `Set`, `OrderedSet` and `Record`. 14 | 15 | These data structures are highly efficient on modern JavaScript VMs by using 16 | structural sharing via [hash maps tries][] and [vector tries][] as popularized 17 | by Clojure and Scala, minimizing the need to copy or cache data. 18 | 19 | `Immutable` also provides a lazy `Seq`, allowing efficient 20 | chaining of collection methods like `map` and `filter` without creating 21 | intermediate representations. Create some `Seq` with `Range` and `Repeat`. 22 | 23 | Want to hear more? Watch the presentation about Immutable.js: 24 | 25 | 26 | 27 | [Persistent]: http://en.wikipedia.org/wiki/Persistent_data_structure 28 | [Immutable]: http://en.wikipedia.org/wiki/Immutable_object 29 | [hash maps tries]: http://en.wikipedia.org/wiki/Hash_array_mapped_trie 30 | [vector tries]: http://hypirion.com/musings/understanding-persistent-vector-pt-1 31 | 32 | 33 | Getting started 34 | --------------- 35 | 36 | Install `immutable` using npm. 37 | 38 | ```shell 39 | npm install immutable 40 | ``` 41 | 42 | Then require it into any module. 43 | 44 | ```javascript 45 | var Immutable = require('immutable'); 46 | var map1 = Immutable.Map({a:1, b:2, c:3}); 47 | var map2 = map1.set('b', 50); 48 | map1.get('b'); // 2 49 | map2.get('b'); // 50 50 | ``` 51 | 52 | ### Browser 53 | 54 | To use `immutable` from a browser, download [dist/immutable.min.js](https://github.com/facebook/immutable-js/blob/master/dist/immutable.min.js) 55 | or use a CDN such as [CDNJS](https://cdnjs.com/libraries/immutable) 56 | or [jsDelivr](http://www.jsdelivr.com/#!immutable.js). 57 | 58 | Then, add it as a script tag to your page: 59 | 60 | ```html 61 | 62 | 68 | ``` 69 | 70 | Or use an AMD loader (such as [RequireJS](http://requirejs.org/)): 71 | 72 | ```javascript 73 | require(['./immutable.min.js'], function (Immutable) { 74 | var map1 = Immutable.Map({a:1, b:2, c:3}); 75 | var map2 = map1.set('b', 50); 76 | map1.get('b'); // 2 77 | map2.get('b'); // 50 78 | }); 79 | ``` 80 | 81 | If you're using [browserify](http://browserify.org/), the `immutable` npm module 82 | also works from the browser. 83 | 84 | ### TypeScript 85 | 86 | Use these Immutable collections and sequences as you would use native 87 | collections in your [TypeScript](http://typescriptlang.org) programs while still taking 88 | advantage of type generics, error detection, and auto-complete in your IDE. 89 | 90 | Just add a reference with a relative path to the type declarations at the top 91 | of your file. 92 | 93 | ```javascript 94 | /// 95 | import Immutable = require('immutable'); 96 | var map1: Immutable.Map; 97 | map1 = Immutable.Map({a:1, b:2, c:3}); 98 | var map2 = map1.set('b', 50); 99 | map1.get('b'); // 2 100 | map2.get('b'); // 50 101 | ``` 102 | 103 | 104 | The case for Immutability 105 | ------------------------- 106 | 107 | Much of what makes application development difficult is tracking mutation and 108 | maintaining state. Developing with immutable data encourages you to think 109 | differently about how data flows through your application. 110 | 111 | Subscribing to data events throughout your application creates a huge overhead of 112 | book-keeping which can hurt performance, sometimes dramatically, and creates 113 | opportunities for areas of your application to get out of sync with each other 114 | due to easy to make programmer error. Since immutable data never changes, 115 | subscribing to changes throughout the model is a dead-end and new data can only 116 | ever be passed from above. 117 | 118 | This model of data flow aligns well with the architecture of [React][] 119 | and especially well with an application designed using the ideas of [Flux][]. 120 | 121 | When data is passed from above rather than being subscribed to, and you're only 122 | interested in doing work when something has changed, you can use equality. 123 | 124 | Immutable collections should be treated as *values* rather than *objects*. While 125 | objects represents some thing which could change over time, a value represents 126 | the state of that thing at a particular instance of time. This principle is most 127 | important to understanding the appropriate use of immutable data. In order to 128 | treat Immutable.js collections as values, it's important to use the 129 | `Immutable.is()` function or `.equals()` method to determine value equality 130 | instead of the `===` operator which determines object reference identity. 131 | 132 | ```javascript 133 | var map1 = Immutable.Map({a:1, b:2, c:3}); 134 | var map2 = map1.set('b', 2); 135 | assert(map1.equals(map2) === true); 136 | var map3 = map1.set('b', 50); 137 | assert(map1.equals(map3) === false); 138 | ``` 139 | 140 | Note: As a performance optimization `Immutable` attempts to return the existing 141 | collection when an operation would result in an identical collection, allowing 142 | for using `===` reference equality to determine if something definitely has not 143 | changed. This can be extremely useful when used within memoization function 144 | which would prefer to re-run the function if a deeper equality check could 145 | potentially be more costly. The `===` equality check is also used internally by 146 | `Immutable.is` and `.equals()` as a performance optimization. 147 | 148 | If an object is immutable, it can be "copied" simply by making another reference 149 | to it instead of copying the entire object. Because a reference is much smaller 150 | than the object itself, this results in memory savings and a potential boost in 151 | execution speed for programs which rely on copies (such as an undo-stack). 152 | 153 | ```javascript 154 | var map1 = Immutable.Map({a:1, b:2, c:3}); 155 | var clone = map1; 156 | ``` 157 | 158 | [React]: http://facebook.github.io/react/ 159 | [Flux]: http://facebook.github.io/flux/docs/overview.html 160 | 161 | 162 | JavaScript-first API 163 | -------------------- 164 | 165 | While `immutable` is inspired by Clojure, Scala, Haskell and other functional 166 | programming environments, it's designed to bring these powerful concepts to 167 | JavaScript, and therefore has an Object-Oriented API that closely mirrors that 168 | of [ES6][] [Array][], [Map][], and [Set][]. 169 | 170 | [ES6]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_6_support_in_Mozilla 171 | [Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array 172 | [Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map 173 | [Set]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set 174 | 175 | The difference for the immutable collections is that methods which would mutate 176 | the collection, like `push`, `set`, `unshift` or `splice` instead return a new 177 | immutable collection. Methods which return new arrays like `slice` or `concat` 178 | instead return new immutable collections. 179 | 180 | ```javascript 181 | var list1 = Immutable.List.of(1, 2); 182 | var list2 = list1.push(3, 4, 5); 183 | var list3 = list2.unshift(0); 184 | var list4 = list1.concat(list2, list3); 185 | assert(list1.size === 2); 186 | assert(list2.size === 5); 187 | assert(list3.size === 6); 188 | assert(list4.size === 13); 189 | assert(list4.get(0) === 1); 190 | ``` 191 | 192 | Almost all of the methods on [Array][] will be found in similar form on 193 | `Immutable.List`, those of [Map][] found on `Immutable.Map`, and those of [Set][] 194 | found on `Immutable.Set`, including collection operations like `forEach()` 195 | and `map()`. 196 | 197 | ```javascript 198 | var alpha = Immutable.Map({a:1, b:2, c:3, d:4}); 199 | alpha.map((v, k) => k.toUpperCase()).join(); 200 | // 'A,B,C,D' 201 | ``` 202 | 203 | ### Accepts raw JavaScript objects. 204 | 205 | Designed to inter-operate with your existing JavaScript, `immutable` 206 | accepts plain JavaScript Arrays and Objects anywhere a method expects an 207 | `Iterable` with no performance penalty. 208 | 209 | ```javascript 210 | var map1 = Immutable.Map({a:1, b:2, c:3, d:4}); 211 | var map2 = Immutable.Map({c:10, a:20, t:30}); 212 | var obj = {d:100, o:200, g:300}; 213 | var map3 = map1.merge(map2, obj); 214 | // Map { a: 20, b: 2, c: 10, d: 100, t: 30, o: 200, g: 300 } 215 | ``` 216 | 217 | This is possible because `immutable` can treat any JavaScript Array or Object 218 | as an Iterable. You can take advantage of this in order to get sophisticated 219 | collection methods on JavaScript Objects, which otherwise have a very sparse 220 | native API. Because Seq evaluates lazily and does not cache intermediate 221 | results, these operations can be extremely efficient. 222 | 223 | ```javascript 224 | var myObject = {a:1,b:2,c:3}; 225 | Immutable.Seq(myObject).map(x => x * x).toObject(); 226 | // { a: 1, b: 4, c: 9 } 227 | ``` 228 | 229 | Keep in mind, when using JS objects to construct Immutable Maps, that 230 | JavaScript Object properties are always strings, even if written in a quote-less 231 | shorthand, while Immutable Maps accept keys of any type. 232 | 233 | ```js 234 | var obj = { 1: "one" }; 235 | Object.keys(obj); // [ "1" ] 236 | obj["1"]; // "one" 237 | obj[1]; // "one" 238 | 239 | var map = Immutable.fromJS(obj); 240 | map.get("1"); // "one" 241 | map.get(1); // undefined 242 | ``` 243 | 244 | Property access for JavaScript Objects first converts the key to a string, but 245 | since Immutable Map keys can be of any type the argument to `get()` is 246 | not altered. 247 | 248 | 249 | ### Converts back to raw JavaScript objects. 250 | 251 | All `immutable` Iterables can be converted to plain JavaScript Arrays and 252 | Objects shallowly with `toArray()` and `toObject()` or deeply with `toJS()`. 253 | All Immutable Iterables also implement `toJSON()` allowing them to be passed to 254 | `JSON.stringify` directly. 255 | 256 | ```javascript 257 | var deep = Immutable.Map({ a: 1, b: 2, c: Immutable.List.of(3, 4, 5) }); 258 | deep.toObject() // { a: 1, b: 2, c: List [ 3, 4, 5 ] } 259 | deep.toArray() // [ 1, 2, List [ 3, 4, 5 ] ] 260 | deep.toJS() // { a: 1, b: 2, c: [ 3, 4, 5 ] } 261 | JSON.stringify(deep) // '{"a":1,"b":2,"c":[3,4,5]}' 262 | ``` 263 | 264 | ### Embraces ES6 265 | 266 | `Immutable` takes advantage of features added to JavaScript in [ES6][], 267 | the latest standard version of ECMAScript (JavaScript), including [Iterators][], 268 | [Arrow Functions][], [Classes][], and [Modules][]. It's also inspired by the 269 | [Map][] and [Set][] collections added to ES6. The library is "transpiled" to ES3 270 | in order to support all modern browsers. 271 | 272 | All examples are presented in ES6. To run in all browsers, they need to be 273 | translated to ES3. 274 | 275 | ```js 276 | // ES6 277 | foo.map(x => x * x); 278 | // ES3 279 | foo.map(function (x) { return x * x; }); 280 | ``` 281 | 282 | [Iterators]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/The_Iterator_protocol 283 | [Arrow Functions]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions 284 | [Classes]: http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes 285 | [Modules]: http://www.2ality.com/2014/09/es6-modules-final.html 286 | 287 | 288 | Nested Structures 289 | ----------------- 290 | 291 | The collections in `immutable` are intended to be nested, allowing for deep 292 | trees of data, similar to JSON. 293 | 294 | ```javascript 295 | var nested = Immutable.fromJS({a:{b:{c:[3,4,5]}}}); 296 | // Map { a: Map { b: Map { c: List [ 3, 4, 5 ] } } } 297 | ``` 298 | 299 | A few power-tools allow for reading and operating on nested data. The 300 | most useful are `mergeDeep`, `getIn`, `setIn`, and `updateIn`, found on `List`, 301 | `Map` and `OrderedMap`. 302 | 303 | ```javascript 304 | var nested2 = nested.mergeDeep({a:{b:{d:6}}}); 305 | // Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 6 } } } 306 | ``` 307 | 308 | ```javascript 309 | nested2.getIn(['a', 'b', 'd']); // 6 310 | 311 | var nested3 = nested2.updateIn(['a', 'b', 'd'], value => value + 1); 312 | // Map { a: Map { b: Map { c: List [ 3, 4, 5 ], d: 7 } } } 313 | 314 | var nested4 = nested3.updateIn(['a', 'b', 'c'], list => list.push(6)); 315 | // Map { a: Map { b: Map { c: List [ 3, 4, 5, 6 ], d: 7 } } } 316 | ``` 317 | 318 | 319 | Lazy Seq 320 | -------- 321 | 322 | `Seq` describes a lazy operation, allowing them to efficiently chain 323 | use of all the Iterable methods (such as `map` and `filter`). 324 | 325 | **Seq is immutable** — Once a Seq is created, it cannot be 326 | changed, appended to, rearranged or otherwise modified. Instead, any mutative 327 | method called on a Seq will return a new Seq. 328 | 329 | **Seq is lazy** — Seq does as little work as necessary to respond to any 330 | method call. 331 | 332 | For example, the following does not perform any work, because the resulting 333 | Seq is never used: 334 | 335 | var oddSquares = Immutable.Seq.of(1,2,3,4,5,6,7,8) 336 | .filter(x => x % 2).map(x => x * x); 337 | 338 | Once the Seq is used, it performs only the work necessary. In this 339 | example, no intermediate arrays are ever created, filter is called three times, 340 | and map is only called twice: 341 | 342 | console.log(oddSquares.get(1)); // 9 343 | 344 | Any collection can be converted to a lazy Seq with `.toSeq()`. 345 | 346 | var seq = Immutable.Map({a:1, b:1, c:1}).toSeq(); 347 | 348 | Seq allow for the efficient chaining of sequence operations, especially when 349 | converting to a different concrete type (such as to a JS object): 350 | 351 | seq.flip().map(key => key.toUpperCase()).flip().toObject(); 352 | // Map { A: 1, B: 1, C: 1 } 353 | 354 | As well as expressing logic that would otherwise seem memory-limited: 355 | 356 | Immutable.Range(1, Infinity) 357 | .skip(1000) 358 | .map(n => -n) 359 | .filter(n => n % 2 === 0) 360 | .take(2) 361 | .reduce((r, n) => r * n, 1); 362 | // 1006008 363 | 364 | Note: An iterable is always iterated in the same order, however that order may 365 | not always be well defined, as is the case for the `Map`. 366 | 367 | 368 | Equality treats Collections as Data 369 | ----------------------------------- 370 | 371 | `Immutable` provides equality which treats immutable data structures as pure 372 | data, performing a deep equality check if necessary. 373 | 374 | ```javascript 375 | var map1 = Immutable.Map({a:1, b:1, c:1}); 376 | var map2 = Immutable.Map({a:1, b:1, c:1}); 377 | assert(map1 !== map2); // two different instances 378 | assert(Immutable.is(map1, map2)); // have equivalent values 379 | assert(map1.equals(map2)); // alternatively use the equals method 380 | ``` 381 | 382 | `Immutable.is()` uses the same measure of equality as [Object.is][] 383 | including if both are immutable and all keys and values are equal 384 | using the same measure of equality. 385 | 386 | [Object.is]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is 387 | 388 | 389 | Batching Mutations 390 | ------------------ 391 | 392 | > If a tree falls in the woods, does it make a sound? 393 | > 394 | > If a pure function mutates some local data in order to produce an immutable 395 | > return value, is that ok? 396 | > 397 | > — Rich Hickey, Clojure 398 | 399 | Applying a mutation to create a new immutable object results in some overhead, 400 | which can add up to a minor performance penalty. If you need to apply a series 401 | of mutations locally before returning, `Immutable` gives you the ability to 402 | create a temporary mutable (transient) copy of a collection and apply a batch of 403 | mutations in a performant manner by using `withMutations`. In fact, this is 404 | exactly how `Immutable` applies complex mutations itself. 405 | 406 | As an example, building `list2` results in the creation of 1, not 3, new 407 | immutable Lists. 408 | 409 | ```javascript 410 | var list1 = Immutable.List.of(1,2,3); 411 | var list2 = list1.withMutations(function (list) { 412 | list.push(4).push(5).push(6); 413 | }); 414 | assert(list1.size === 3); 415 | assert(list2.size === 6); 416 | ``` 417 | 418 | Note: `immutable` also provides `asMutable` and `asImmutable`, but only 419 | encourages their use when `withMutations` will not suffice. Use caution to not 420 | return a mutable copy, which could result in undesired behavior. 421 | 422 | *Important!*: Only a select few methods can be used in `withMutations` including 423 | `set`, `push` and `pop`. These methods can be applied directly against a 424 | persistent data-structure where other methods like `map`, `filter`, `sort`, 425 | and `splice` will always return new immutable data-structures and never mutate 426 | a mutable collection. 427 | 428 | 429 | Documentation 430 | ------------- 431 | 432 | [Read the docs](http://facebook.github.io/immutable-js/docs/) and eat your vegetables. 433 | 434 | Docs are automatically generated from [Immutable.d.ts](https://github.com/facebook/immutable-js/blob/master/type-definitions/Immutable.d.ts). 435 | Please contribute! 436 | 437 | Also, don't miss the [Wiki](https://github.com/facebook/immutable-js/wiki) which 438 | contains articles on specific topics. Can't find something? Open an [issue](https://github.com/facebook/immutable-js/issues). 439 | 440 | 441 | Testing 442 | ------- 443 | 444 | If you are using the [Chai Assertion Library](http://chaijs.com/), [Chai Immutable](https://github.com/astorije/chai-immutable) provides a set of assertions to use against `Immutable` collections. 445 | 446 | 447 | Contribution 448 | ------------ 449 | 450 | Use [Github issues](https://github.com/facebook/immutable-js/issues) for requests. 451 | 452 | We actively welcome pull requests, learn how to [contribute](./CONTRIBUTING.md). 453 | 454 | 455 | Changelog 456 | --------- 457 | 458 | Changes are tracked as [Github releases](https://github.com/facebook/immutable-js/releases). 459 | 460 | 461 | Thanks 462 | ------ 463 | 464 | [Phil Bagwell](https://www.youtube.com/watch?v=K2NYwP90bNs), for his inspiration 465 | and research in persistent data structures. 466 | 467 | [Hugh Jackson](https://github.com/hughfdjackson/), for providing the npm package 468 | name. If you're looking for his unsupported package, see [this repository](https://github.com/hughfdjackson/immutable). 469 | 470 | 471 | License 472 | ------- 473 | 474 | `Immutable` is [BSD-licensed](./LICENSE). We also provide an additional [patent grant](./PATENTS). 475 | -------------------------------------------------------------------------------- /home-records/node_modules/immutable/contrib/cursor/README.md: -------------------------------------------------------------------------------- 1 | Cursors 2 | ------- 3 | 4 | Cursors allow you to hold a reference to a path in a nested immutable data 5 | structure, allowing you to pass smaller sections of a larger nested 6 | collection to portions of your application while maintaining a central point 7 | aware of changes to the entire data structure: an `onChange` function which is 8 | called whenever a cursor or sub-cursor calls `update`. 9 | 10 | This is particularly useful when used in conjuction with component-based UI 11 | libraries like [React](http://facebook.github.io/react/) or to simulate 12 | "state" throughout an application while maintaining a single flow of logic. 13 | 14 | 15 | ```javascript 16 | var Immutable = require('immutable'); 17 | var Cursor = require('immutable/contrib/cursor'); 18 | 19 | var data = Immutable.fromJS({ a: { b: { c: 1 } } }); 20 | var cursor = Cursor.from(data, ['a', 'b'], newData => { 21 | data = newData; 22 | }); 23 | 24 | // ... elsewhere ... 25 | 26 | cursor.get('c'); // 1 27 | cursor = cursor.update('c', x => x + 1); 28 | cursor.get('c'); // 2 29 | 30 | // ... back to data ... 31 | 32 | data.getIn(['a', 'b', 'c']); // 2 33 | ``` 34 | -------------------------------------------------------------------------------- /home-records/node_modules/immutable/contrib/cursor/__tests__/Cursor.ts.skip: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | 5 | jest.autoMockOff(); 6 | 7 | import Immutable = require('immutable'); 8 | import Cursor = require('immutable/contrib/cursor'); 9 | 10 | describe('Cursor', () => { 11 | 12 | beforeEach(function () { 13 | this.addMatchers({ 14 | toValueEqual: function (expected) { 15 | var actual = this.actual; 16 | if (!Immutable.is(expected, this.actual)) { 17 | this.message = 'Expected\n' + this.actual + '\nto equal\n' + expected; 18 | return false; 19 | } 20 | return true; 21 | } 22 | }); 23 | }); 24 | 25 | var json = { a: { b: { c: 1 } } }; 26 | 27 | it('gets from its path', () => { 28 | var data = Immutable.fromJS(json); 29 | var cursor = Cursor.from(data); 30 | 31 | expect(cursor.deref()).toBe(data); 32 | 33 | var deepCursor = cursor.cursor(['a', 'b']); 34 | expect(deepCursor.deref().toJS()).toEqual(json.a.b); 35 | expect(deepCursor.deref()).toBe(data.getIn(['a', 'b'])); 36 | expect(deepCursor.get('c')).toBe(1); 37 | 38 | var leafCursor = deepCursor.cursor('c'); 39 | expect(leafCursor.deref()).toBe(1); 40 | 41 | var missCursor = leafCursor.cursor('d'); 42 | expect(missCursor.deref()).toBe(undefined); 43 | }); 44 | 45 | it('gets return new cursors', () => { 46 | var data = Immutable.fromJS(json); 47 | var cursor = Cursor.from(data); 48 | var deepCursor = cursor.getIn(['a', 'b']); 49 | expect(deepCursor.deref()).toBe(data.getIn(['a', 'b'])); 50 | }); 51 | 52 | it('gets return new cursors using List', () => { 53 | var data = Immutable.fromJS(json); 54 | var cursor = Cursor.from(data); 55 | var deepCursor = cursor.getIn(Immutable.fromJS(['a', 'b'])); 56 | expect(deepCursor.deref()).toBe(data.getIn(Immutable.fromJS(['a', 'b']))); 57 | }); 58 | 59 | it('cursor return new cursors of correct type', () => { 60 | var data = Immutable.fromJS({ a: [1, 2, 3] }); 61 | var cursor = Cursor.from(data); 62 | var deepCursor = cursor.cursor('a'); 63 | expect(deepCursor.findIndex).toBeDefined(); 64 | }); 65 | 66 | it('can be treated as a value', () => { 67 | var data = Immutable.fromJS(json); 68 | var cursor = Cursor.from(data, ['a', 'b']); 69 | expect(cursor.toJS()).toEqual(json.a.b); 70 | expect(cursor).toValueEqual(data.getIn(['a', 'b'])); 71 | expect(cursor.size).toBe(1); 72 | expect(cursor.get('c')).toBe(1); 73 | }); 74 | 75 | it('can be value compared to a primitive', () => { 76 | var data = Immutable.Map({ a: 'A' }); 77 | var aCursor = Cursor.from(data, 'a'); 78 | expect(aCursor.size).toBe(undefined); 79 | expect(aCursor.deref()).toBe('A'); 80 | expect(Immutable.is(aCursor, 'A')).toBe(true); 81 | }); 82 | 83 | it('updates at its path', () => { 84 | var onChange = jest.genMockFunction(); 85 | 86 | var data = Immutable.fromJS(json); 87 | var aCursor = Cursor.from(data, 'a', onChange); 88 | 89 | var deepCursor = aCursor.cursor(['b', 'c']); 90 | expect(deepCursor.deref()).toBe(1); 91 | 92 | // cursor edits return new cursors: 93 | var newDeepCursor = deepCursor.update(x => x + 1); 94 | expect(newDeepCursor.deref()).toBe(2); 95 | var call1 = onChange.mock.calls[0]; 96 | expect(call1[0]).toValueEqual(Immutable.fromJS({a:{b:{c:2}}})); 97 | expect(call1[1]).toBe(data); 98 | expect(call1[2]).toEqual(['a', 'b', 'c']); 99 | 100 | var newestDeepCursor = newDeepCursor.update(x => x + 1); 101 | expect(newestDeepCursor.deref()).toBe(3); 102 | var call2 = onChange.mock.calls[1]; 103 | expect(call2[0]).toValueEqual(Immutable.fromJS({a:{b:{c:3}}})); 104 | expect(call2[1]).toValueEqual(Immutable.fromJS({a:{b:{c:2}}})); 105 | expect(call2[2]).toEqual(['a', 'b', 'c']); 106 | 107 | // meanwhile, data is still immutable: 108 | expect(data.toJS()).toEqual(json); 109 | 110 | // as is the original cursor. 111 | expect(deepCursor.deref()).toBe(1); 112 | var otherNewDeepCursor = deepCursor.update(x => x + 10); 113 | expect(otherNewDeepCursor.deref()).toBe(11); 114 | var call3 = onChange.mock.calls[2]; 115 | expect(call3[0]).toValueEqual(Immutable.fromJS({a:{b:{c:11}}})); 116 | expect(call3[1]).toBe(data); 117 | expect(call3[2]).toEqual(['a', 'b', 'c']); 118 | 119 | // and update has been called exactly thrice. 120 | expect(onChange.mock.calls.length).toBe(3); 121 | }); 122 | 123 | it('updates with the return value of onChange', () => { 124 | var onChange = jest.genMockFunction(); 125 | 126 | var data = Immutable.fromJS(json); 127 | var deepCursor = Cursor.from(data, ['a', 'b', 'c'], onChange); 128 | 129 | onChange.mockReturnValueOnce(undefined); 130 | // onChange returning undefined has no effect 131 | var newCursor = deepCursor.update(x => x + 1); 132 | expect(newCursor.deref()).toBe(2); 133 | var call1 = onChange.mock.calls[0]; 134 | expect(call1[0]).toValueEqual(Immutable.fromJS({a:{b:{c:2}}})); 135 | expect(call1[1]).toBe(data); 136 | expect(call1[2]).toEqual(['a', 'b', 'c']); 137 | 138 | onChange.mockReturnValueOnce(Immutable.fromJS({a:{b:{c:11}}})); 139 | // onChange returning something else has an effect 140 | newCursor = newCursor.update(x => 999); 141 | expect(newCursor.deref()).toBe(11); 142 | var call2 = onChange.mock.calls[1]; 143 | expect(call2[0]).toValueEqual(Immutable.fromJS({a:{b:{c:999}}})); 144 | expect(call2[1]).toValueEqual(Immutable.fromJS({a:{b:{c:2}}})); 145 | expect(call2[2]).toEqual(['a', 'b', 'c']); 146 | 147 | // and update has been called exactly twice 148 | expect(onChange.mock.calls.length).toBe(2); 149 | }); 150 | 151 | it('has map API for update shorthand', () => { 152 | var onChange = jest.genMockFunction(); 153 | 154 | var data = Immutable.fromJS(json); 155 | var aCursor = Cursor.from(data, 'a', onChange); 156 | var bCursor = aCursor.cursor('b'); 157 | var cCursor = bCursor.cursor('c'); 158 | 159 | expect(bCursor.set('c', 10).deref()).toValueEqual( 160 | Immutable.fromJS({ c: 10 }) 161 | ); 162 | 163 | var call1 = onChange.mock.calls[0]; 164 | expect(call1[0]).toValueEqual(Immutable.fromJS({a:{b:{c:10}}})); 165 | expect(call1[1]).toBe(data); 166 | expect(call1[2]).toEqual(['a', 'b', 'c']); 167 | }); 168 | 169 | it('creates maps as necessary', () => { 170 | var data = Immutable.Map(); 171 | var cursor = Cursor.from(data, ['a', 'b', 'c']); 172 | expect(cursor.deref()).toBe(undefined); 173 | cursor = cursor.set('d', 3); 174 | expect(cursor.deref()).toValueEqual(Immutable.Map({d: 3})); 175 | }); 176 | 177 | it('can set undefined', () => { 178 | var data = Immutable.Map(); 179 | var cursor = Cursor.from(data, ['a', 'b', 'c']); 180 | expect(cursor.deref()).toBe(undefined); 181 | cursor = cursor.set('d', undefined); 182 | expect(cursor.toJS()).toEqual({d: undefined}); 183 | }); 184 | 185 | it('has the sequence API', () => { 186 | var data = Immutable.Map({a: 1, b: 2, c: 3}); 187 | var cursor = Cursor.from(data); 188 | expect(cursor.map((x: number) => x * x)).toValueEqual(Immutable.Map({a: 1, b: 4, c: 9})); 189 | }); 190 | 191 | it('can push values on a List', () => { 192 | var onChange = jest.genMockFunction(); 193 | var data = Immutable.fromJS({a: {b: [0, 1, 2]}}); 194 | var cursor = Cursor.from(data, ['a', 'b'], onChange); 195 | 196 | expect(cursor.push(3,4)).toValueEqual(Immutable.List([0, 1, 2, 3, 4])); 197 | 198 | var call = onChange.mock.calls[0]; 199 | expect(call[0]).toValueEqual(Immutable.fromJS({a: {b: [0, 1, 2, 3, 4]}})); 200 | expect(call[1]).toBe(data); 201 | expect(call[2]).toEqual(['a', 'b']); 202 | }); 203 | 204 | it('can pop values of a List', () => { 205 | var onChange = jest.genMockFunction(); 206 | var data = Immutable.fromJS({a: {b: [0, 1, 2]}}); 207 | var cursor = Cursor.from(data, ['a', 'b'], onChange); 208 | 209 | expect(cursor.pop()).toValueEqual(Immutable.List([0, 1])); 210 | 211 | var call = onChange.mock.calls[0]; 212 | expect(call[0]).toValueEqual(Immutable.fromJS({a: {b: [0, 1]}})); 213 | expect(call[1]).toBe(data); 214 | expect(call[2]).toEqual(['a', 'b']); 215 | }); 216 | 217 | it('can unshift values on a List', () => { 218 | var onChange = jest.genMockFunction(); 219 | var data = Immutable.fromJS({a: {b: [0, 1, 2]}}); 220 | var cursor = Cursor.from(data, ['a', 'b'], onChange); 221 | 222 | expect(cursor.unshift(-2, -1)).toValueEqual(Immutable.List([-2, -1, 0, 1, 2])); 223 | 224 | var call = onChange.mock.calls[0]; 225 | expect(call[0]).toValueEqual(Immutable.fromJS({a: {b: [-2, -1, 0, 1, 2]}})); 226 | expect(call[1]).toBe(data); 227 | expect(call[2]).toEqual(['a', 'b']); 228 | }); 229 | 230 | it('can shift values of a List', () => { 231 | var onChange = jest.genMockFunction(); 232 | var data = Immutable.fromJS({a: {b: [0, 1, 2]}}); 233 | var cursor = Cursor.from(data, ['a', 'b'], onChange); 234 | 235 | expect(cursor.shift()).toValueEqual(Immutable.List([1, 2])); 236 | 237 | var call = onChange.mock.calls[0]; 238 | expect(call[0]).toValueEqual(Immutable.fromJS({a: {b: [1, 2]}})); 239 | expect(call[1]).toBe(data); 240 | expect(call[2]).toEqual(['a', 'b']); 241 | }); 242 | 243 | 244 | it('returns wrapped values for sequence API', () => { 245 | var data = Immutable.fromJS({a: {v: 1}, b: {v: 2}, c: {v: 3}}); 246 | var onChange = jest.genMockFunction(); 247 | var cursor = Cursor.from(data, onChange); 248 | 249 | var found = cursor.find(map => map.get('v') === 2); 250 | expect(typeof found.deref).toBe('function'); // is a cursor! 251 | found = found.set('v', 20); 252 | 253 | var call = onChange.mock.calls[0]; 254 | expect(call[0]).toValueEqual(Immutable.fromJS({a: {v: 1}, b: {v: 20}, c: {v: 3}})); 255 | expect(call[1]).toBe(data); 256 | expect(call[2]).toEqual(['b', 'v']); 257 | }); 258 | 259 | it('returns wrapped values for iteration API', () => { 260 | var jsData = [{val: 0}, {val: 1}, {val: 2}]; 261 | var data = Immutable.fromJS(jsData); 262 | var cursor = Cursor.from(data); 263 | cursor.forEach(function (c, i) { 264 | expect(typeof c.deref).toBe('function'); // is a cursor! 265 | expect(c.get('val')).toBe(i); 266 | }); 267 | }); 268 | 269 | it('can map over values to get subcursors', () => { 270 | var data = Immutable.fromJS({a: {v: 1}, b: {v: 2}, c: {v: 3}}); 271 | var cursor = Cursor.from(data); 272 | 273 | var mapped = cursor.map(val => { 274 | expect(typeof val.deref).toBe('function'); // mapped values are cursors. 275 | return val; 276 | }).toMap(); 277 | // Mapped is not a cursor, but it is a sequence of cursors. 278 | expect(typeof (mapped).deref).not.toBe('function'); 279 | expect(typeof (mapped.get('a')).deref).toBe('function'); 280 | 281 | // Same for indexed cursors 282 | var data2 = Immutable.fromJS({x: [{v: 1}, {v: 2}, {v: 3}]}); 283 | var cursor2 = Cursor.from(data2); 284 | 285 | var mapped2 = cursor2.get('x').map(val => { 286 | expect(typeof val.deref).toBe('function'); // mapped values are cursors. 287 | return val; 288 | }).toList(); 289 | // Mapped is not a cursor, but it is a sequence of cursors. 290 | expect(typeof mapped2.deref).not.toBe('function'); 291 | expect(typeof mapped2.get(0).deref).toBe('function'); 292 | }); 293 | 294 | it('can have mutations apply with a single callback', () => { 295 | var onChange = jest.genMockFunction(); 296 | var data = Immutable.fromJS({'a': 1}); 297 | 298 | var c1 = Cursor.from(data, onChange); 299 | var c2 = c1.withMutations(m => m.set('b', 2).set('c', 3).set('d', 4)); 300 | 301 | expect(c1.deref().toObject()).toEqual({'a': 1}); 302 | expect(c2.deref().toObject()).toEqual({'a': 1, 'b': 2, 'c': 3, 'd': 4}); 303 | expect(onChange.mock.calls.length).toBe(1); 304 | }); 305 | 306 | it('can use withMutations on an unfulfilled cursor', () => { 307 | var onChange = jest.genMockFunction(); 308 | var data = Immutable.fromJS({}); 309 | 310 | var c1 = Cursor.from(data, ['a', 'b', 'c'], onChange); 311 | var c2 = c1.withMutations(m => m.set('x', 1).set('y', 2).set('z', 3)); 312 | 313 | expect(c1.deref()).toEqual(undefined); 314 | expect(c2.deref()).toValueEqual(Immutable.fromJS( 315 | { x: 1, y: 2, z: 3 } 316 | )); 317 | expect(onChange.mock.calls.length).toBe(1); 318 | }); 319 | 320 | it('maintains indexed sequences', () => { 321 | var data = Immutable.fromJS([]); 322 | var c = Cursor.from(data); 323 | expect(c.toJS()).toEqual([]); 324 | }); 325 | 326 | it('properly acts as an iterable', () => { 327 | var data = Immutable.fromJS({key: {val: 1}}); 328 | var c = Cursor.from(data).values(); 329 | var c1 = c.next().value.get('val'); 330 | expect(c1).toBe(1); 331 | }); 332 | 333 | it('can update deeply', () => { 334 | var onChange = jest.genMockFunction(); 335 | var data = Immutable.fromJS({a:{b:{c:1}}}); 336 | var c = Cursor.from(data, ['a'], onChange); 337 | var c1 = c.updateIn(['b', 'c'], x => x * 10); 338 | expect(c1.getIn(['b', 'c'])).toBe(10); 339 | 340 | var call = onChange.mock.calls[0]; 341 | expect(call[0]).toValueEqual(Immutable.fromJS({a:{b:{c:10}}})); 342 | expect(call[1]).toBe(data); 343 | expect(call[2]).toEqual(['a', 'b', 'c']); 344 | }); 345 | 346 | it('can set deeply', () => { 347 | var onChange = jest.genMockFunction(); 348 | var data = Immutable.fromJS({a:{b:{c:1}}}); 349 | var c = Cursor.from(data, ['a'], onChange); 350 | var c1 = c.setIn(['b', 'c'], 10); 351 | expect(c1.getIn(['b', 'c'])).toBe(10); 352 | 353 | var call = onChange.mock.calls[0]; 354 | expect(call[0]).toValueEqual(Immutable.fromJS({a:{b:{c:10}}})); 355 | expect(call[1]).toBe(data); 356 | expect(call[2]).toEqual(['a', 'b', 'c']); 357 | }); 358 | 359 | it('can get Record value as a property', () => { 360 | var User = Immutable.Record({ name: 'John' }); 361 | var users = Immutable.List.of(new User()); 362 | var data = Immutable.Map({'users': users}); 363 | var cursor = Cursor.from(data, ['users']); 364 | expect(cursor.first().name).toBe('John'); 365 | }); 366 | 367 | it('can set value of a cursor directly', () => { 368 | var onChange = jest.genMockFunction(); 369 | var data = Immutable.fromJS({a:1}); 370 | var c = Cursor.from(data, ['a'], onChange); 371 | var c1 = c.set(2); 372 | expect(c1.deref()).toBe(2); 373 | 374 | var call = onChange.mock.calls[0]; 375 | expect(call[0]).toValueEqual(Immutable.fromJS({a:2})); 376 | expect(call[1]).toBe(data); 377 | expect(call[2]).toEqual(['a']); 378 | }); 379 | 380 | it('can set value of a cursor to undefined directly', () => { 381 | var onChange = jest.genMockFunction(); 382 | var data = Immutable.fromJS({a:1}); 383 | var c = Cursor.from(data, ['a'], onChange); 384 | var c1 = c.set(undefined); 385 | expect(c1.deref()).toBe(undefined); 386 | 387 | var call = onChange.mock.calls[0]; 388 | expect(call[0]).toValueEqual(Immutable.fromJS({a:undefined})); 389 | expect(call[1]).toBe(data); 390 | expect(call[2]).toEqual(['a']); 391 | }); 392 | 393 | }); 394 | -------------------------------------------------------------------------------- /home-records/node_modules/immutable/contrib/cursor/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014-2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | 11 | /** 12 | * Cursors 13 | * ------- 14 | * 15 | * Cursors allow you to hold a reference to a path in a nested immutable data 16 | * structure, allowing you to pass smaller sections of a larger nested 17 | * collection to portions of your application while maintaining a central point 18 | * aware of changes to the entire data structure. 19 | * 20 | * This is particularly useful when used in conjuction with component-based UI 21 | * libraries like [React](http://facebook.github.io/react/) or to simulate 22 | * "state" throughout an application while maintaining a single flow of logic. 23 | * 24 | * Cursors provide a simple API for getting the value at that path 25 | * (the equivalent of `this.getIn(keyPath)`), updating the value at that path 26 | * (the equivalent of `this.updateIn(keyPath)`), and getting a sub-cursor 27 | * starting from that path. 28 | * 29 | * When updated, a new root collection is created and provided to the `onChange` 30 | * function provided to the first call to `Cursor(map, onChange)`. 31 | * 32 | * When this cursor's (or any of its sub-cursors') `update` method is called, 33 | * the resulting new data structure will be provided to the `onChange` 34 | * function. Use this callback to keep track of the most current value or 35 | * update the rest of your application. 36 | */ 37 | 38 | /// 39 | 40 | declare module __Cursor { 41 | 42 | export function from( 43 | collection: Immutable.Collection, 44 | onChange?: (newValue: any, oldValue?: any, keyPath?: Array) => any 45 | ): Cursor; 46 | export function from( 47 | collection: Immutable.Collection, 48 | keyPath: Array, 49 | onChange?: (newValue: any, oldValue?: any, keyPath?: Array) => any 50 | ): Cursor; 51 | export function from( 52 | collection: Immutable.Collection, 53 | key: any, 54 | onChange?: (newValue: any, oldValue?: any, keyPath?: Array) => any 55 | ): Cursor; 56 | 57 | 58 | export interface Cursor extends Immutable.Seq { 59 | 60 | /** 61 | * Returns a sub-cursor following the key-path starting from this cursor. 62 | */ 63 | cursor(subKeyPath: Array): Cursor; 64 | cursor(subKey: any): Cursor; 65 | 66 | /** 67 | * Returns the value at the cursor, if the cursor path does not yet exist, 68 | * returns `notSetValue`. 69 | */ 70 | deref(notSetValue?: any): any; 71 | 72 | /** 73 | * Returns the value at the `key` in the cursor, or `notSetValue` if it 74 | * does not exist. 75 | * 76 | * If the key would return a collection, a new Cursor is returned. 77 | */ 78 | get(key: any, notSetValue?: any): any; 79 | 80 | /** 81 | * Returns the value at the `keyPath` in the cursor, or `notSetValue` if it 82 | * does not exist. 83 | * 84 | * If the keyPath would return a collection, a new Cursor is returned. 85 | */ 86 | getIn(keyPath: Array, notSetValue?: any): any; 87 | getIn(keyPath: Immutable.Iterable, notSetValue?: any): any; 88 | 89 | /** 90 | * Sets `value` at `key` in the cursor, returning a new cursor to the same 91 | * point in the new data. 92 | * 93 | * If only one parameter is provided, it is set directly as the cursor's value. 94 | */ 95 | set(key: any, value: any): Cursor; 96 | set(value: any): Cursor; 97 | 98 | /** 99 | * Deletes `key` from the cursor, returning a new cursor to the same 100 | * point in the new data. 101 | * 102 | * Note: `delete` cannot be safely used in IE8 103 | * @alias remove 104 | */ 105 | delete(key: any): Cursor; 106 | remove(key: any): Cursor; 107 | 108 | /** 109 | * Clears the value at this cursor, returning a new cursor to the same 110 | * point in the new data. 111 | */ 112 | clear(): Cursor; 113 | 114 | /** 115 | * Updates the value in the data this cursor points to, triggering the 116 | * callback for the root cursor and returning a new cursor pointing to the 117 | * new data. 118 | */ 119 | update(updater: (value: any) => any): Cursor; 120 | update(key: any, updater: (value: any) => any): Cursor; 121 | update(key: any, notSetValue: any, updater: (value: any) => any): Cursor; 122 | 123 | /** 124 | * @see `Map#merge` 125 | */ 126 | merge(...iterables: Immutable.Iterable[]): Cursor; 127 | merge(...iterables: {[key: string]: any}[]): Cursor; 128 | 129 | /** 130 | * @see `Map#mergeWith` 131 | */ 132 | mergeWith( 133 | merger: (previous?: any, next?: any) => any, 134 | ...iterables: Immutable.Iterable[] 135 | ): Cursor; 136 | mergeWith( 137 | merger: (previous?: any, next?: any) => any, 138 | ...iterables: {[key: string]: any}[] 139 | ): Cursor; 140 | 141 | /** 142 | * @see `Map#mergeDeep` 143 | */ 144 | mergeDeep(...iterables: Immutable.Iterable[]): Cursor; 145 | mergeDeep(...iterables: {[key: string]: any}[]): Cursor; 146 | 147 | /** 148 | * @see `Map#mergeDeepWith` 149 | */ 150 | mergeDeepWith( 151 | merger: (previous?: any, next?: any) => any, 152 | ...iterables: Immutable.Iterable[] 153 | ): Cursor; 154 | mergeDeepWith( 155 | merger: (previous?: any, next?: any) => any, 156 | ...iterables: {[key: string]: any}[] 157 | ): Cursor; 158 | 159 | // Deep persistent changes 160 | 161 | /** 162 | * Returns a new Cursor having set `value` at this `keyPath`. If any keys in 163 | * `keyPath` do not exist, a new immutable Map will be created at that key. 164 | */ 165 | setIn(keyPath: Array, value: any): Cursor; 166 | setIn(keyPath: Immutable.Iterable, value: any): Cursor; 167 | 168 | /** 169 | * Returns a new Cursor with provided `values` appended 170 | */ 171 | push(...values: Array): Cursor; 172 | 173 | /** 174 | * Returns a new Cursor with a size ones less than this Cursor, 175 | * excluding the last index in this Cursor. 176 | */ 177 | pop(): Cursor; 178 | 179 | /** 180 | * Returns a new Cursor with the provided `values` prepended, 181 | * shifting other values ahead to higher indices. 182 | */ 183 | unshift(...values: Array): Cursor; 184 | 185 | /** 186 | * Returns a new Cursor with a size ones less than this Cursor, excluding 187 | * the first index in this Cursor, shifting all other values to a lower index. 188 | */ 189 | shift(): Cursor; 190 | 191 | /** 192 | * Returns a new Cursor having removed the value at this `keyPath`. 193 | * 194 | * @alias removeIn 195 | */ 196 | deleteIn(keyPath: Array): Cursor; 197 | deleteIn(keyPath: Immutable.Iterable): Cursor; 198 | removeIn(keyPath: Array): Cursor; 199 | removeIn(keyPath: Immutable.Iterable): Cursor; 200 | 201 | /** 202 | * Returns a new Cursor having applied the `updater` to the value found at 203 | * the keyPath. 204 | * 205 | * If any keys in `keyPath` do not exist, new Immutable `Map`s will 206 | * be created at those keys. If the `keyPath` does not already contain a 207 | * value, the `updater` function will be called with `notSetValue`, if 208 | * provided, otherwise `undefined`. 209 | * 210 | * If the `updater` function returns the same value it was called with, then 211 | * no change will occur. This is still true if `notSetValue` is provided. 212 | */ 213 | updateIn( 214 | keyPath: Array, 215 | updater: (value: any) => any 216 | ): Cursor; 217 | updateIn( 218 | keyPath: Array, 219 | notSetValue: any, 220 | updater: (value: any) => any 221 | ): Cursor; 222 | updateIn( 223 | keyPath: Immutable.Iterable, 224 | updater: (value: any) => any 225 | ): Cursor; 226 | updateIn( 227 | keyPath: Immutable.Iterable, 228 | notSetValue: any, 229 | updater: (value: any) => any 230 | ): Cursor; 231 | 232 | /** 233 | * A combination of `updateIn` and `merge`, returning a new Cursor, but 234 | * performing the merge at a point arrived at by following the keyPath. 235 | * In other words, these two lines are equivalent: 236 | * 237 | * x.updateIn(['a', 'b', 'c'], abc => abc.merge(y)); 238 | * x.mergeIn(['a', 'b', 'c'], y); 239 | * 240 | */ 241 | mergeIn( 242 | keyPath: Immutable.Iterable, 243 | ...iterables: Immutable.Iterable[] 244 | ): Cursor; 245 | mergeIn( 246 | keyPath: Array, 247 | ...iterables: Immutable.Iterable[] 248 | ): Cursor; 249 | mergeIn( 250 | keyPath: Array, 251 | ...iterables: {[key: string]: any}[] 252 | ): Cursor; 253 | 254 | /** 255 | * A combination of `updateIn` and `mergeDeep`, returning a new Cursor, but 256 | * performing the deep merge at a point arrived at by following the keyPath. 257 | * In other words, these two lines are equivalent: 258 | * 259 | * x.updateIn(['a', 'b', 'c'], abc => abc.mergeDeep(y)); 260 | * x.mergeDeepIn(['a', 'b', 'c'], y); 261 | * 262 | */ 263 | mergeDeepIn( 264 | keyPath: Immutable.Iterable, 265 | ...iterables: Immutable.Iterable[] 266 | ): Cursor; 267 | mergeDeepIn( 268 | keyPath: Array, 269 | ...iterables: Immutable.Iterable[] 270 | ): Cursor; 271 | mergeDeepIn( 272 | keyPath: Array, 273 | ...iterables: {[key: string]: any}[] 274 | ): Cursor; 275 | 276 | // Transient changes 277 | 278 | /** 279 | * Every time you call one of the above functions, a new immutable value is 280 | * created and the callback is triggered. If you need to apply a series of 281 | * mutations to a Cursor without triggering the callback repeatedly, 282 | * `withMutations()` creates a temporary mutable copy of the value which 283 | * can apply mutations in a highly performant manner. Afterwards the 284 | * callback is triggered with the final value. 285 | */ 286 | withMutations(mutator: (mutable: any) => any): Cursor; 287 | } 288 | 289 | } 290 | 291 | declare module 'immutable/contrib/cursor' { 292 | export = __Cursor 293 | } -------------------------------------------------------------------------------- /home-records/node_modules/immutable/contrib/cursor/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014-2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | /** 11 | * Cursor is expected to be required in a node or other CommonJS context: 12 | * 13 | * var Cursor = require('immutable/contrib/cursor'); 14 | * 15 | * If you wish to use it in the browser, please check out Browserify or WebPack! 16 | */ 17 | 18 | var Immutable = require('../../'); 19 | var Iterable = Immutable.Iterable; 20 | var Iterator = Iterable.Iterator; 21 | var Seq = Immutable.Seq; 22 | var Map = Immutable.Map; 23 | var Record = Immutable.Record; 24 | 25 | 26 | function cursorFrom(rootData, keyPath, onChange) { 27 | if (arguments.length === 1) { 28 | keyPath = []; 29 | } else if (typeof keyPath === 'function') { 30 | onChange = keyPath; 31 | keyPath = []; 32 | } else { 33 | keyPath = valToKeyPath(keyPath); 34 | } 35 | return makeCursor(rootData, keyPath, onChange); 36 | } 37 | 38 | 39 | var KeyedCursorPrototype = Object.create(Seq.Keyed.prototype); 40 | var IndexedCursorPrototype = Object.create(Seq.Indexed.prototype); 41 | 42 | function KeyedCursor(rootData, keyPath, onChange, size) { 43 | this.size = size; 44 | this._rootData = rootData; 45 | this._keyPath = keyPath; 46 | this._onChange = onChange; 47 | } 48 | KeyedCursorPrototype.constructor = KeyedCursor; 49 | 50 | function IndexedCursor(rootData, keyPath, onChange, size) { 51 | this.size = size; 52 | this._rootData = rootData; 53 | this._keyPath = keyPath; 54 | this._onChange = onChange; 55 | } 56 | IndexedCursorPrototype.constructor = IndexedCursor; 57 | 58 | KeyedCursorPrototype.toString = function() { 59 | return this.__toString('Cursor {', '}'); 60 | } 61 | IndexedCursorPrototype.toString = function() { 62 | return this.__toString('Cursor [', ']'); 63 | } 64 | 65 | KeyedCursorPrototype.deref = 66 | KeyedCursorPrototype.valueOf = 67 | IndexedCursorPrototype.deref = 68 | IndexedCursorPrototype.valueOf = function(notSetValue) { 69 | return this._rootData.getIn(this._keyPath, notSetValue); 70 | } 71 | 72 | KeyedCursorPrototype.get = 73 | IndexedCursorPrototype.get = function(key, notSetValue) { 74 | return this.getIn([key], notSetValue); 75 | } 76 | 77 | KeyedCursorPrototype.getIn = 78 | IndexedCursorPrototype.getIn = function(keyPath, notSetValue) { 79 | keyPath = listToKeyPath(keyPath); 80 | if (keyPath.length === 0) { 81 | return this; 82 | } 83 | var value = this._rootData.getIn(newKeyPath(this._keyPath, keyPath), NOT_SET); 84 | return value === NOT_SET ? notSetValue : wrappedValue(this, keyPath, value); 85 | } 86 | 87 | IndexedCursorPrototype.set = 88 | KeyedCursorPrototype.set = function(key, value) { 89 | if(arguments.length === 1) { 90 | return updateCursor(this, function() { return key; }, []); 91 | } else { 92 | return updateCursor(this, function (m) { return m.set(key, value); }, [key]); 93 | } 94 | } 95 | 96 | IndexedCursorPrototype.push = function(/* values */) { 97 | var args = arguments; 98 | return updateCursor(this, function (m) { 99 | return m.push.apply(m, args); 100 | }); 101 | } 102 | 103 | IndexedCursorPrototype.pop = function() { 104 | return updateCursor(this, function (m) { 105 | return m.pop(); 106 | }); 107 | } 108 | 109 | IndexedCursorPrototype.unshift = function(/* values */) { 110 | var args = arguments; 111 | return updateCursor(this, function (m) { 112 | return m.unshift.apply(m, args); 113 | }); 114 | } 115 | 116 | IndexedCursorPrototype.shift = function() { 117 | return updateCursor(this, function (m) { 118 | return m.shift(); 119 | }); 120 | } 121 | 122 | IndexedCursorPrototype.setIn = 123 | KeyedCursorPrototype.setIn = Map.prototype.setIn; 124 | 125 | KeyedCursorPrototype.remove = 126 | KeyedCursorPrototype['delete'] = 127 | IndexedCursorPrototype.remove = 128 | IndexedCursorPrototype['delete'] = function(key) { 129 | return updateCursor(this, function (m) { return m.remove(key); }, [key]); 130 | } 131 | 132 | IndexedCursorPrototype.removeIn = 133 | IndexedCursorPrototype.deleteIn = 134 | KeyedCursorPrototype.removeIn = 135 | KeyedCursorPrototype.deleteIn = Map.prototype.deleteIn; 136 | 137 | KeyedCursorPrototype.clear = 138 | IndexedCursorPrototype.clear = function() { 139 | return updateCursor(this, function (m) { return m.clear(); }); 140 | } 141 | 142 | IndexedCursorPrototype.update = 143 | KeyedCursorPrototype.update = function(keyOrFn, notSetValue, updater) { 144 | return arguments.length === 1 ? 145 | updateCursor(this, keyOrFn) : 146 | this.updateIn([keyOrFn], notSetValue, updater); 147 | } 148 | 149 | IndexedCursorPrototype.updateIn = 150 | KeyedCursorPrototype.updateIn = function(keyPath, notSetValue, updater) { 151 | return updateCursor(this, function (m) { 152 | return m.updateIn(keyPath, notSetValue, updater); 153 | }, keyPath); 154 | } 155 | 156 | IndexedCursorPrototype.merge = 157 | KeyedCursorPrototype.merge = function(/*...iters*/) { 158 | var args = arguments; 159 | return updateCursor(this, function (m) { 160 | return m.merge.apply(m, args); 161 | }); 162 | } 163 | 164 | IndexedCursorPrototype.mergeWith = 165 | KeyedCursorPrototype.mergeWith = function(merger/*, ...iters*/) { 166 | var args = arguments; 167 | return updateCursor(this, function (m) { 168 | return m.mergeWith.apply(m, args); 169 | }); 170 | } 171 | 172 | IndexedCursorPrototype.mergeIn = 173 | KeyedCursorPrototype.mergeIn = Map.prototype.mergeIn; 174 | 175 | IndexedCursorPrototype.mergeDeep = 176 | KeyedCursorPrototype.mergeDeep = function(/*...iters*/) { 177 | var args = arguments; 178 | return updateCursor(this, function (m) { 179 | return m.mergeDeep.apply(m, args); 180 | }); 181 | } 182 | 183 | IndexedCursorPrototype.mergeDeepWith = 184 | KeyedCursorPrototype.mergeDeepWith = function(merger/*, ...iters*/) { 185 | var args = arguments; 186 | return updateCursor(this, function (m) { 187 | return m.mergeDeepWith.apply(m, args); 188 | }); 189 | } 190 | 191 | IndexedCursorPrototype.mergeDeepIn = 192 | KeyedCursorPrototype.mergeDeepIn = Map.prototype.mergeDeepIn; 193 | 194 | KeyedCursorPrototype.withMutations = 195 | IndexedCursorPrototype.withMutations = function(fn) { 196 | return updateCursor(this, function (m) { 197 | return (m || Map()).withMutations(fn); 198 | }); 199 | } 200 | 201 | KeyedCursorPrototype.cursor = 202 | IndexedCursorPrototype.cursor = function(subKeyPath) { 203 | subKeyPath = valToKeyPath(subKeyPath); 204 | return subKeyPath.length === 0 ? this : subCursor(this, subKeyPath); 205 | } 206 | 207 | /** 208 | * All iterables need to implement __iterate 209 | */ 210 | KeyedCursorPrototype.__iterate = 211 | IndexedCursorPrototype.__iterate = function(fn, reverse) { 212 | var cursor = this; 213 | var deref = cursor.deref(); 214 | return deref && deref.__iterate ? deref.__iterate( 215 | function (v, k) { return fn(wrappedValue(cursor, [k], v), k, cursor); }, 216 | reverse 217 | ) : 0; 218 | } 219 | 220 | /** 221 | * All iterables need to implement __iterator 222 | */ 223 | KeyedCursorPrototype.__iterator = 224 | IndexedCursorPrototype.__iterator = function(type, reverse) { 225 | var deref = this.deref(); 226 | var cursor = this; 227 | var iterator = deref && deref.__iterator && 228 | deref.__iterator(Iterator.ENTRIES, reverse); 229 | return new Iterator(function () { 230 | if (!iterator) { 231 | return { value: undefined, done: true }; 232 | } 233 | var step = iterator.next(); 234 | if (step.done) { 235 | return step; 236 | } 237 | var entry = step.value; 238 | var k = entry[0]; 239 | var v = wrappedValue(cursor, [k], entry[1]); 240 | return { 241 | value: type === Iterator.KEYS ? k : type === Iterator.VALUES ? v : [k, v], 242 | done: false 243 | }; 244 | }); 245 | } 246 | 247 | KeyedCursor.prototype = KeyedCursorPrototype; 248 | IndexedCursor.prototype = IndexedCursorPrototype; 249 | 250 | 251 | var NOT_SET = {}; // Sentinel value 252 | 253 | function makeCursor(rootData, keyPath, onChange, value) { 254 | if (arguments.length < 4) { 255 | value = rootData.getIn(keyPath); 256 | } 257 | var size = value && value.size; 258 | var CursorClass = Iterable.isIndexed(value) ? IndexedCursor : KeyedCursor; 259 | var cursor = new CursorClass(rootData, keyPath, onChange, size); 260 | 261 | if (value instanceof Record) { 262 | defineRecordProperties(cursor, value); 263 | } 264 | 265 | return cursor; 266 | } 267 | 268 | function defineRecordProperties(cursor, value) { 269 | try { 270 | value._keys.forEach(setProp.bind(undefined, cursor)); 271 | } catch (error) { 272 | // Object.defineProperty failed. Probably IE8. 273 | } 274 | } 275 | 276 | function setProp(prototype, name) { 277 | Object.defineProperty(prototype, name, { 278 | get: function() { 279 | return this.get(name); 280 | }, 281 | set: function(value) { 282 | if (!this.__ownerID) { 283 | throw new Error('Cannot set on an immutable record.'); 284 | } 285 | } 286 | }); 287 | } 288 | 289 | function wrappedValue(cursor, keyPath, value) { 290 | return Iterable.isIterable(value) ? subCursor(cursor, keyPath, value) : value; 291 | } 292 | 293 | function subCursor(cursor, keyPath, value) { 294 | if (arguments.length < 3) { 295 | return makeCursor( // call without value 296 | cursor._rootData, 297 | newKeyPath(cursor._keyPath, keyPath), 298 | cursor._onChange 299 | ); 300 | } 301 | return makeCursor( 302 | cursor._rootData, 303 | newKeyPath(cursor._keyPath, keyPath), 304 | cursor._onChange, 305 | value 306 | ); 307 | } 308 | 309 | function updateCursor(cursor, changeFn, changeKeyPath) { 310 | var deepChange = arguments.length > 2; 311 | var newRootData = cursor._rootData.updateIn( 312 | cursor._keyPath, 313 | deepChange ? Map() : undefined, 314 | changeFn 315 | ); 316 | var keyPath = cursor._keyPath || []; 317 | var result = cursor._onChange && cursor._onChange.call( 318 | undefined, 319 | newRootData, 320 | cursor._rootData, 321 | deepChange ? newKeyPath(keyPath, changeKeyPath) : keyPath 322 | ); 323 | if (result !== undefined) { 324 | newRootData = result; 325 | } 326 | return makeCursor(newRootData, cursor._keyPath, cursor._onChange); 327 | } 328 | 329 | function newKeyPath(head, tail) { 330 | return head.concat(listToKeyPath(tail)); 331 | } 332 | 333 | function listToKeyPath(list) { 334 | return Array.isArray(list) ? list : Immutable.Iterable(list).toArray(); 335 | } 336 | 337 | function valToKeyPath(val) { 338 | return Array.isArray(val) ? val : 339 | Iterable.isIterable(val) ? val.toArray() : 340 | [val]; 341 | } 342 | 343 | exports.from = cursorFrom; 344 | -------------------------------------------------------------------------------- /home-records/node_modules/immutable/dist/immutable.js.flow: -------------------------------------------------------------------------------- 1 | /** 2 | * This file provides type definitions for use with the Flow type checker. 3 | * 4 | * An important caveat when using these definitions is that the types for 5 | * `Iterable.Keyed`, `Iterable.Indexed`, `Seq.Keyed`, and so on are stubs. 6 | * When referring to those types, you can get the proper definitions by 7 | * importing the types `KeyedIterable`, `IndexedIterable`, `KeyedSeq`, etc. 8 | * For example, 9 | * 10 | * import { Seq } from 'immutable' 11 | * import type { IndexedIterable, IndexedSeq } from 'immutable' 12 | * 13 | * const someSeq: IndexedSeq = Seq.Indexed.of(1, 2, 3) 14 | * 15 | * function takesASeq>(iter: TS): TS { 16 | * return iter.butLast() 17 | * } 18 | * 19 | * takesASeq(someSeq) 20 | * 21 | * @flow 22 | */ 23 | 24 | /* 25 | * Alias for ECMAScript `Iterable` type, declared in 26 | * https://github.com/facebook/flow/blob/master/lib/core.js 27 | * 28 | * Note that Immutable values implement the `ESIterable` interface. 29 | */ 30 | type ESIterable = $Iterable; 31 | 32 | declare class Iterable extends _Iterable {} 33 | 34 | declare class _Iterable { 35 | static Keyed: KI; 36 | static Indexed: II; 37 | static Set: SI; 38 | 39 | static isIterable(maybeIterable: any): boolean; 40 | static isKeyed(maybeKeyed: any): boolean; 41 | static isIndexed(maybeIndexed: any): boolean; 42 | static isAssociative(maybeAssociative: any): boolean; 43 | static isOrdered(maybeOrdered: any): boolean; 44 | 45 | equals(other: Iterable): boolean; 46 | hashCode(): number; 47 | get(key: K): V; 48 | get(key: K, notSetValue: V_): V|V_; 49 | has(key: K): boolean; 50 | includes(value: V): boolean; 51 | contains(value: V): boolean; 52 | first(): V; 53 | last(): V; 54 | 55 | getIn(searchKeyPath: ESIterable, notSetValue: T): T; 56 | getIn(searchKeyPath: ESIterable): T; 57 | hasIn(searchKeyPath: ESIterable): boolean; 58 | 59 | toJS(): any; 60 | toArray(): V[]; 61 | toObject(): { [key: string]: V }; 62 | toMap(): Map; 63 | toOrderedMap(): Map; 64 | toSet(): Set; 65 | toOrderedSet(): Set; 66 | toList(): List; 67 | toStack(): Stack; 68 | toSeq(): Seq; 69 | toKeyedSeq(): KeyedSeq; 70 | toIndexedSeq(): IndexedSeq; 71 | toSetSeq(): SetSeq; 72 | 73 | keys(): Iterator; 74 | values(): Iterator; 75 | entries(): Iterator<[K,V]>; 76 | 77 | keySeq(): IndexedSeq; 78 | valueSeq(): IndexedSeq; 79 | entrySeq(): IndexedSeq<[K,V]>; 80 | 81 | reverse(): this; 82 | sort(comparator?: (valueA: V, valueB: V) => number): this; 83 | 84 | sortBy( 85 | comparatorValueMapper: (value: V, key: K, iter: this) => C, 86 | comparator?: (valueA: C, valueB: C) => number 87 | ): this; 88 | 89 | groupBy( 90 | grouper: (value: V, key: K, iter: this) => G, 91 | context?: any 92 | ): KeyedSeq; 93 | 94 | forEach( 95 | sideEffect: (value: V, key: K, iter: this) => any, 96 | context?: any 97 | ): number; 98 | 99 | slice(begin?: number, end?: number): this; 100 | rest(): this; 101 | butLast(): this; 102 | skip(amount: number): this; 103 | skipLast(amount: number): this; 104 | skipWhile(predicate: (value: V, key: K, iter: this) => mixed, context?: any): this; 105 | skipUntil(predicate: (value: V, key: K, iter: this) => mixed, context?: any): this; 106 | take(amount: number): this; 107 | takeLast(amount: number): this; 108 | takeWhile(predicate: (value: V, key: K, iter: this) => mixed, context?: any): this; 109 | takeUntil(predicate: (value: V, key: K, iter: this) => mixed, context?: any): this; 110 | flatten(depth?: number): /*this*/Iterable; 111 | flatten(shallow?: boolean): /*this*/Iterable; 112 | 113 | filter( 114 | predicate: (value: V, key: K, iter: this) => mixed, 115 | context?: any 116 | ): this; 117 | 118 | filterNot( 119 | predicate: (value: V, key: K, iter: this) => mixed, 120 | context?: any 121 | ): this; 122 | 123 | reduce( 124 | reducer: (reduction: R, value: V, key: K, iter: this) => R, 125 | initialReduction?: R, 126 | context?: any, 127 | ): R; 128 | 129 | reduceRight( 130 | reducer: (reduction: R, value: V, key: K, iter: this) => R, 131 | initialReduction?: R, 132 | context?: any, 133 | ): R; 134 | 135 | every(predicate: (value: V, key: K, iter: this) => mixed, context?: any): boolean; 136 | some(predicate: (value: V, key: K, iter: this) => mixed, context?: any): boolean; 137 | join(separator?: string): string; 138 | isEmpty(): boolean; 139 | count(predicate?: (value: V, key: K, iter: this) => mixed, context?: any): number; 140 | countBy(grouper: (value: V, key: K, iter: this) => G, context?: any): Map; 141 | 142 | find( 143 | predicate: (value: V, key: K, iter: this) => mixed, 144 | context?: any, 145 | ): ?V; 146 | find( 147 | predicate: (value: V, key: K, iter: this) => mixed, 148 | context: any, 149 | notSetValue: V_ 150 | ): V|V_; 151 | 152 | findLast( 153 | predicate: (value: V, key: K, iter: this) => mixed, 154 | context?: any, 155 | ): ?V; 156 | findLast( 157 | predicate: (value: V, key: K, iter: this) => mixed, 158 | context: any, 159 | notSetValue: V_ 160 | ): V|V_; 161 | 162 | 163 | findEntry(predicate: (value: V, key: K, iter: this) => mixed): ?[K,V]; 164 | findLastEntry(predicate: (value: V, key: K, iter: this) => mixed): ?[K,V]; 165 | 166 | findKey(predicate: (value: V, key: K, iter: this) => mixed, context?: any): ?K; 167 | findLastKey(predicate: (value: V, key: K, iter: this) => mixed, context?: any): ?K; 168 | 169 | keyOf(searchValue: V): ?K; 170 | lastKeyOf(searchValue: V): ?K; 171 | 172 | max(comparator?: (valueA: V, valueB: V) => number): V; 173 | maxBy( 174 | comparatorValueMapper: (value: V, key: K, iter: this) => C, 175 | comparator?: (valueA: C, valueB: C) => number 176 | ): V; 177 | min(comparator?: (valueA: V, valueB: V) => number): V; 178 | minBy( 179 | comparatorValueMapper: (value: V, key: K, iter: this) => C, 180 | comparator?: (valueA: C, valueB: C) => number 181 | ): V; 182 | 183 | isSubset(iter: Iterable): boolean; 184 | isSubset(iter: ESIterable): boolean; 185 | isSuperset(iter: Iterable): boolean; 186 | isSuperset(iter: ESIterable): boolean; 187 | } 188 | 189 | declare class KeyedIterable extends Iterable { 190 | static (iter?: ESIterable<[K,V]>): KeyedIterable; 191 | static (obj?: { [key: K]: V }): KeyedIterable; 192 | 193 | @@iterator(): Iterator<[K,V]>; 194 | toSeq(): KeyedSeq; 195 | flip(): /*this*/KeyedIterable; 196 | 197 | mapKeys( 198 | mapper: (key: K, value: V, iter: this) => K_, 199 | context?: any 200 | ): /*this*/KeyedIterable; 201 | 202 | mapEntries( 203 | mapper: (entry: [K,V], index: number, iter: this) => [K_,V_], 204 | context?: any 205 | ): /*this*/KeyedIterable; 206 | 207 | concat(...iters: ESIterable<[K,V]>[]): this; 208 | 209 | map( 210 | mapper: (value: V, key: K, iter: this) => V_, 211 | context?: any 212 | ): /*this*/KeyedIterable; 213 | 214 | flatMap( 215 | mapper: (value: V, key: K, iter: this) => ESIterable<[K_,V_]>, 216 | context?: any 217 | ): /*this*/KeyedIterable; 218 | 219 | flatten(depth?: number): /*this*/KeyedIterable; 220 | flatten(shallow?: boolean): /*this*/KeyedIterable; 221 | } 222 | 223 | declare class IndexedIterable extends Iterable { 224 | static (iter?: ESIterable): IndexedIterable; 225 | 226 | @@iterator(): Iterator; 227 | toSeq(): IndexedSeq; 228 | fromEntrySeq(): KeyedSeq; 229 | interpose(separator: T): this; 230 | interleave(...iterables: ESIterable[]): this; 231 | splice( 232 | index: number, 233 | removeNum: number, 234 | ...values: T[] 235 | ): this; 236 | 237 | zip( 238 | a: ESIterable, 239 | $?: null 240 | ): IndexedIterable<[T,A]>; 241 | zip( 242 | a: ESIterable, 243 | b: ESIterable, 244 | $?: null 245 | ): IndexedIterable<[T,A,B]>; 246 | zip( 247 | a: ESIterable, 248 | b: ESIterable, 249 | c: ESIterable, 250 | $?: null 251 | ): IndexedIterable<[T,A,B,C]>; 252 | zip( 253 | a: ESIterable, 254 | b: ESIterable, 255 | c: ESIterable, 256 | d: ESIterable, 257 | $?: null 258 | ): IndexedIterable<[T,A,B,C,D]>; 259 | zip( 260 | a: ESIterable, 261 | b: ESIterable, 262 | c: ESIterable, 263 | d: ESIterable, 264 | e: ESIterable, 265 | $?: null 266 | ): IndexedIterable<[T,A,B,C,D,E]>; 267 | 268 | zipWith( 269 | zipper: (value: T, a: A) => R, 270 | a: ESIterable, 271 | $?: null 272 | ): IndexedIterable; 273 | zipWith( 274 | zipper: (value: T, a: A, b: B) => R, 275 | a: ESIterable, 276 | b: ESIterable, 277 | $?: null 278 | ): IndexedIterable; 279 | zipWith( 280 | zipper: (value: T, a: A, b: B, c: C) => R, 281 | a: ESIterable, 282 | b: ESIterable, 283 | c: ESIterable, 284 | $?: null 285 | ): IndexedIterable; 286 | zipWith( 287 | zipper: (value: T, a: A, b: B, c: C, d: D) => R, 288 | a: ESIterable, 289 | b: ESIterable, 290 | c: ESIterable, 291 | d: ESIterable, 292 | $?: null 293 | ): IndexedIterable; 294 | zipWith( 295 | zipper: (value: T, a: A, b: B, c: C, d: D, e: E) => R, 296 | a: ESIterable, 297 | b: ESIterable, 298 | c: ESIterable, 299 | d: ESIterable, 300 | e: ESIterable, 301 | $?: null 302 | ): IndexedIterable; 303 | 304 | indexOf(searchValue: T): number; 305 | lastIndexOf(searchValue: T): number; 306 | findIndex( 307 | predicate: (value: T, index: number, iter: this) => mixed, 308 | context?: any 309 | ): number; 310 | findLastIndex( 311 | predicate: (value: T, index: number, iter: this) => mixed, 312 | context?: any 313 | ): number; 314 | 315 | concat(...iters: ESIterable[]): this; 316 | 317 | map( 318 | mapper: (value: T, index: number, iter: this) => U, 319 | context?: any 320 | ): /*this*/IndexedIterable; 321 | 322 | flatMap( 323 | mapper: (value: T, index: number, iter: this) => ESIterable, 324 | context?: any 325 | ): /*this*/IndexedIterable; 326 | 327 | flatten(depth?: number): /*this*/IndexedIterable; 328 | flatten(shallow?: boolean): /*this*/IndexedIterable; 329 | } 330 | 331 | declare class SetIterable extends Iterable { 332 | static (iter?: ESIterable): SetIterable; 333 | 334 | @@iterator(): Iterator; 335 | toSeq(): SetSeq; 336 | 337 | concat(...iters: ESIterable[]): this; 338 | 339 | // `map` and `flatMap` cannot be defined further up the hiearchy, because the 340 | // implementation for `KeyedIterable` allows the value type to change without 341 | // constraining the key type. That does not work for `SetIterable` - the value 342 | // and key types *must* match. 343 | map( 344 | mapper: (value: T, value: T, iter: this) => U, 345 | context?: any 346 | ): /*this*/SetIterable; 347 | 348 | flatMap( 349 | mapper: (value: T, value: T, iter: this) => ESIterable, 350 | context?: any 351 | ): /*this*/SetIterable; 352 | 353 | flatten(depth?: number): /*this*/SetIterable; 354 | flatten(shallow?: boolean): /*this*/SetIterable; 355 | } 356 | 357 | declare class Collection extends _Iterable { 358 | size: number; 359 | } 360 | 361 | declare class KeyedCollection extends Collection mixins KeyedIterable { 362 | toSeq(): KeyedSeq; 363 | } 364 | 365 | declare class IndexedCollection extends Collection mixins IndexedIterable { 366 | toSeq(): IndexedSeq; 367 | } 368 | 369 | declare class SetCollection extends Collection mixins SetIterable { 370 | toSeq(): SetSeq; 371 | } 372 | 373 | declare class Seq extends _Iterable { 374 | static (iter: KeyedSeq): KeyedSeq; 375 | static (iter: SetSeq): SetSeq; 376 | static (iter?: ESIterable): IndexedSeq; 377 | static (iter: { [key: K]: V }): KeyedSeq; 378 | 379 | static isSeq(maybeSeq: any): boolean; 380 | static of(...values: T[]): IndexedSeq; 381 | 382 | size: ?number; 383 | cacheResult(): this; 384 | toSeq(): this; 385 | } 386 | 387 | declare class KeyedSeq extends Seq mixins KeyedIterable { 388 | static (iter?: ESIterable<[K,V]>): KeyedSeq; 389 | static (iter?: { [key: K]: V }): KeyedSeq; 390 | } 391 | 392 | declare class IndexedSeq extends Seq mixins IndexedIterable { 393 | static (iter?: ESIterable): IndexedSeq; 394 | static of(...values: T[]): IndexedSeq; 395 | } 396 | 397 | declare class SetSeq extends Seq mixins SetIterable { 398 | static (iter?: ESIterable): IndexedSeq; 399 | static of(...values: T[]): SetSeq; 400 | } 401 | 402 | declare class List extends IndexedCollection { 403 | static (iterable?: ESIterable): List; 404 | 405 | static isList(maybeList: any): boolean; 406 | static of(...values: T[]): List; 407 | 408 | set(index: number, value: U): List; 409 | delete(index: number): this; 410 | remove(index: number): this; 411 | insert(index: number, value: U): List; 412 | clear(): this; 413 | push(...values: U[]): List; 414 | pop(): this; 415 | unshift(...values: U[]): List; 416 | shift(): this; 417 | 418 | update(updater: (value: this) => List): List; 419 | update(index: number, updater: (value: T) => U): List; 420 | update(index: number, notSetValue: U, updater: (value: T) => U): List; 421 | 422 | merge(...iterables: ESIterable[]): List; 423 | 424 | mergeWith( 425 | merger: (previous: T, next: U, key: number) => V, 426 | ...iterables: ESIterable[] 427 | ): List; 428 | 429 | mergeDeep(...iterables: ESIterable[]): List; 430 | 431 | mergeDeepWith( 432 | merger: (previous: T, next: U, key: number) => V, 433 | ...iterables: ESIterable[] 434 | ): List; 435 | 436 | setSize(size: number): List; 437 | setIn(keyPath: ESIterable, value: any): List; 438 | deleteIn(keyPath: ESIterable, value: any): this; 439 | removeIn(keyPath: ESIterable, value: any): this; 440 | 441 | updateIn(keyPath: ESIterable, notSetValue: any, value: any): List; 442 | updateIn(keyPath: ESIterable, value: any): List; 443 | 444 | mergeIn(keyPath: ESIterable, ...iterables: ESIterable[]): List; 445 | mergeDeepIn(keyPath: ESIterable, ...iterables: ESIterable[]): List; 446 | 447 | withMutations(mutator: (mutable: this) => any): this; 448 | asMutable(): this; 449 | asImmutable(): this; 450 | 451 | // Overrides that specialize return types 452 | map( 453 | mapper: (value: T, index: number, iter: this) => M, 454 | context?: any 455 | ): List; 456 | 457 | flatMap( 458 | mapper: (value: T, index: number, iter: this) => ESIterable, 459 | context?: any 460 | ): List; 461 | 462 | flatten(depth?: number): /*this*/List; 463 | flatten(shallow?: boolean): /*this*/List; 464 | } 465 | 466 | declare class Map extends KeyedCollection { 467 | static (): Map; 468 | static (obj?: {[key: string]: V}): Map; 469 | static (iterable?: ESIterable<[K,V]>): Map; 470 | 471 | static isMap(maybeMap: any): boolean; 472 | 473 | set(key: K_, value: V_): Map; 474 | delete(key: K): this; 475 | remove(key: K): this; 476 | clear(): this; 477 | 478 | update(updater: (value: this) => Map): Map; 479 | update(key: K, updater: (value: V) => V_): Map; 480 | update(key: K, notSetValue: V_, updater: (value: V) => V_): Map; 481 | 482 | merge( 483 | ...iterables: (ESIterable<[K_,V_]> | { [key: K_]: V_ })[] 484 | ): Map; 485 | 486 | mergeWith( 487 | merger: (previous: V, next: W, key: number) => X, 488 | ...iterables: ESIterable[] 489 | ): Map; 490 | 491 | mergeDeep( 492 | ...iterables: (ESIterable<[K_,V_]> | { [key: K_]: V_ })[] 493 | ): Map; 494 | 495 | mergeDeepWith( 496 | merger: (previous: V, next: W, key: number) => X, 497 | ...iterables: ESIterable[] 498 | ): Map; 499 | 500 | setIn(keyPath: ESIterable, value: any): Map; 501 | deleteIn(keyPath: ESIterable, value: any): this; 502 | removeIn(keyPath: ESIterable, value: any): this; 503 | 504 | updateIn(keyPath: ESIterable, notSetValue: any, value: any): Map; 505 | updateIn(keyPath: ESIterable, value: any): Map; 506 | 507 | mergeIn(keyPath: ESIterable, ...iterables: ESIterable[]): Map; 508 | mergeDeepIn(keyPath: ESIterable, ...iterables: ESIterable[]): Map; 509 | 510 | withMutations(mutator: (mutable: this) => any): this; 511 | asMutable(): this; 512 | asImmutable(): this; 513 | 514 | // Overrides that specialize return types 515 | 516 | map( 517 | mapper: (value: V, key: K, iter: this) => V_, 518 | context?: any 519 | ): Map; 520 | 521 | flatMap( 522 | mapper: (value: V, key: K, iter: this) => ESIterable<[K_,V_]>, 523 | context?: any 524 | ): Map; 525 | 526 | flip(): Map; 527 | 528 | mapKeys( 529 | mapper: (key: K, value: V, iter: this) => K_, 530 | context?: any 531 | ): Map; 532 | 533 | flatten(depth?: number): /*this*/Map; 534 | flatten(shallow?: boolean): /*this*/Map; 535 | } 536 | 537 | // OrderedMaps have nothing that Maps do not have. We do not need to override constructor & other statics 538 | declare class OrderedMap extends Map { 539 | static isOrderedMap(maybeOrderedMap: any): bool; 540 | } 541 | 542 | declare class Set extends SetCollection { 543 | static (iterable?: ESIterable): Set; 544 | 545 | static isSet(maybeSet: any): boolean; 546 | static of(...values: T[]): Set; 547 | static fromKeys(iter: ESIterable<[T,any]>): Set; 548 | static fromKeys(iter: { [key: string]: any }): Set; 549 | 550 | add(value: U): Set; 551 | delete(value: T): this; 552 | remove(value: T): this; 553 | clear(): this; 554 | union(...iterables: ESIterable[]): Set; 555 | merge(...iterables: ESIterable[]): Set; 556 | intersect(...iterables: ESIterable[]): Set; 557 | subtract(...iterables: ESIterable[]): Set; 558 | 559 | withMutations(mutator: (mutable: this) => any): this; 560 | asMutable(): this; 561 | asImmutable(): this; 562 | 563 | // Overrides that specialize return types 564 | 565 | map( 566 | mapper: (value: T, value: T, iter: this) => M, 567 | context?: any 568 | ): Set; 569 | 570 | flatMap( 571 | mapper: (value: T, value: T, iter: this) => ESIterable, 572 | context?: any 573 | ): Set; 574 | 575 | flatten(depth?: number): /*this*/Set; 576 | flatten(shallow?: boolean): /*this*/Set; 577 | } 578 | 579 | // OrderedSets have nothing that Sets do not have. We do not need to override constructor & other statics 580 | declare class OrderedSet extends Set { 581 | static isOrderedSet(maybeOrderedSet: any): bool; 582 | } 583 | 584 | declare class Stack extends IndexedCollection { 585 | static (iterable?: ESIterable): Stack; 586 | 587 | static isStack(maybeStack: any): boolean; 588 | static of(...values: T[]): Stack; 589 | 590 | peek(): T; 591 | clear(): this; 592 | unshift(...values: U[]): Stack; 593 | unshiftAll(iter: ESIterable): Stack; 594 | shift(): this; 595 | push(...values: U[]): Stack; 596 | pushAll(iter: ESIterable): Stack; 597 | pop(): this; 598 | 599 | withMutations(mutator: (mutable: this) => any): this; 600 | asMutable(): this; 601 | asImmutable(): this; 602 | 603 | // Overrides that specialize return types 604 | 605 | map( 606 | mapper: (value: T, index: number, iter: this) => U, 607 | context?: any 608 | ): Stack; 609 | 610 | flatMap( 611 | mapper: (value: T, index: number, iter: this) => ESIterable, 612 | context?: any 613 | ): Stack; 614 | 615 | flatten(depth?: number): /*this*/Stack; 616 | flatten(shallow?: boolean): /*this*/Stack; 617 | } 618 | 619 | declare function Range(start?: number, end?: number, step?: number): IndexedSeq; 620 | declare function Repeat(value: T, times?: number): IndexedSeq; 621 | 622 | //TODO: Once flow can extend normal Objects we can change this back to actually reflect Record behavior. 623 | // For now fallback to any to not break existing Code 624 | declare class Record { 625 | static (spec: T, name?: string): /*T & Record*/any; 626 | get(key: $Keys): A; 627 | set(key: $Keys, value: A): /*T & Record*/this; 628 | remove(key: $Keys): /*T & Record*/this; 629 | } 630 | 631 | declare function fromJS(json: any, reviver?: (k: any, v: Iterable) => any): any; 632 | declare function is(first: any, second: any): boolean; 633 | 634 | export { 635 | Iterable, 636 | Collection, 637 | Seq, 638 | 639 | // These classes do not actually exist under these names. But it is useful to 640 | // have the types available. 641 | KeyedIterable, 642 | IndexedIterable, 643 | SetIterable, 644 | KeyedCollection, 645 | IndexedCollection, 646 | SetCollection, 647 | KeyedSeq, 648 | IndexedSeq, 649 | SetSeq, 650 | 651 | List, 652 | Map, 653 | OrderedMap, 654 | OrderedSet, 655 | Range, 656 | Repeat, 657 | Record, 658 | Set, 659 | Stack, 660 | 661 | fromJS, 662 | is, 663 | } 664 | -------------------------------------------------------------------------------- /home-records/node_modules/immutable/dist/immutable.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014-2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.Immutable=e()}(this,function(){"use strict";function t(t,e){e&&(t.prototype=Object.create(e.prototype)),t.prototype.constructor=t}function e(t){return o(t)?t:O(t)}function r(t){return u(t)?t:x(t)}function n(t){return s(t)?t:k(t)}function i(t){return o(t)&&!a(t)?t:A(t)}function o(t){return!(!t||!t[ar])}function u(t){return!(!t||!t[hr])}function s(t){return!(!t||!t[fr])}function a(t){return u(t)||s(t)}function h(t){return!(!t||!t[cr])}function f(t){return t.value=!1,t}function c(t){t&&(t.value=!0)}function _(){}function p(t,e){e=e||0;for(var r=Math.max(0,t.length-e),n=Array(r),i=0;r>i;i++)n[i]=t[i+e];return n}function v(t){return void 0===t.size&&(t.size=t.__iterate(y)),t.size}function l(t,e){if("number"!=typeof e){var r=e>>>0;if(""+r!==e||4294967295===r)return NaN;e=r}return 0>e?v(t)+e:e}function y(){return!0}function d(t,e,r){return(0===t||void 0!==r&&-r>=t)&&(void 0===e||void 0!==r&&e>=r)}function m(t,e){return w(t,e,0)}function g(t,e){return w(t,e,e)}function w(t,e,r){return void 0===t?r:0>t?Math.max(0,e+t):void 0===e?t:Math.min(e,t)}function S(t){this.next=t}function z(t,e,r,n){var i=0===t?e:1===t?r:[e,r];return n?n.value=i:n={value:i,done:!1},n}function I(){return{value:void 0,done:!0}}function b(t){return!!M(t)}function q(t){return t&&"function"==typeof t.next}function D(t){var e=M(t);return e&&e.call(t)}function M(t){var e=t&&(zr&&t[zr]||t[Ir]);return"function"==typeof e?e:void 0}function E(t){return t&&"number"==typeof t.length}function O(t){return null===t||void 0===t?T():o(t)?t.toSeq():C(t)}function x(t){return null===t||void 0===t?T().toKeyedSeq():o(t)?u(t)?t.toSeq():t.fromEntrySeq():B(t)}function k(t){return null===t||void 0===t?T():o(t)?u(t)?t.entrySeq():t.toIndexedSeq():W(t)}function A(t){return(null===t||void 0===t?T():o(t)?u(t)?t.entrySeq():t:W(t)).toSetSeq()}function j(t){this._array=t,this.size=t.length}function R(t){var e=Object.keys(t);this._object=t,this._keys=e, 10 | this.size=e.length}function U(t){this._iterable=t,this.size=t.length||t.size}function K(t){this._iterator=t,this._iteratorCache=[]}function L(t){return!(!t||!t[qr])}function T(){return Dr||(Dr=new j([]))}function B(t){var e=Array.isArray(t)?new j(t).fromEntrySeq():q(t)?new K(t).fromEntrySeq():b(t)?new U(t).fromEntrySeq():"object"==typeof t?new R(t):void 0;if(!e)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+t);return e}function W(t){var e=J(t);if(!e)throw new TypeError("Expected Array or iterable object of values: "+t);return e}function C(t){var e=J(t)||"object"==typeof t&&new R(t);if(!e)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+t);return e}function J(t){return E(t)?new j(t):q(t)?new K(t):b(t)?new U(t):void 0}function N(t,e,r,n){var i=t._cache;if(i){for(var o=i.length-1,u=0;o>=u;u++){var s=i[r?o-u:u];if(e(s[1],n?s[0]:u,t)===!1)return u+1}return u}return t.__iterateUncached(e,r)}function P(t,e,r,n){var i=t._cache;if(i){var o=i.length-1,u=0;return new S(function(){var t=i[r?o-u:u];return u++>o?I():z(e,n?t[0]:u-1,t[1])})}return t.__iteratorUncached(e,r)}function H(t,e){return e?V(e,t,"",{"":t}):Y(t)}function V(t,e,r,n){return Array.isArray(e)?t.call(n,r,k(e).map(function(r,n){return V(t,r,n,e)})):Q(e)?t.call(n,r,x(e).map(function(r,n){return V(t,r,n,e)})):e}function Y(t){return Array.isArray(t)?k(t).map(Y).toList():Q(t)?x(t).map(Y).toMap():t}function Q(t){return t&&(t.constructor===Object||void 0===t.constructor)}function X(t,e){if(t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1;if("function"==typeof t.valueOf&&"function"==typeof e.valueOf){if(t=t.valueOf(),e=e.valueOf(),t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1}return"function"==typeof t.equals&&"function"==typeof e.equals&&t.equals(e)?!0:!1}function F(t,e){if(t===e)return!0;if(!o(e)||void 0!==t.size&&void 0!==e.size&&t.size!==e.size||void 0!==t.__hash&&void 0!==e.__hash&&t.__hash!==e.__hash||u(t)!==u(e)||s(t)!==s(e)||h(t)!==h(e))return!1;if(0===t.size&&0===e.size)return!0; 11 | var r=!a(t);if(h(t)){var n=t.entries();return e.every(function(t,e){var i=n.next().value;return i&&X(i[1],t)&&(r||X(i[0],e))})&&n.next().done}var i=!1;if(void 0===t.size)if(void 0===e.size)"function"==typeof t.cacheResult&&t.cacheResult();else{i=!0;var f=t;t=e,e=f}var c=!0,_=e.__iterate(function(e,n){return(r?t.has(e):i?X(e,t.get(n,yr)):X(t.get(n,yr),e))?void 0:(c=!1,!1)});return c&&t.size===_}function G(t,e){if(!(this instanceof G))return new G(t,e);if(this._value=t,this.size=void 0===e?1/0:Math.max(0,e),0===this.size){if(Mr)return Mr;Mr=this}}function Z(t,e){if(!t)throw Error(e)}function $(t,e,r){if(!(this instanceof $))return new $(t,e,r);if(Z(0!==r,"Cannot step a Range by 0"),t=t||0,void 0===e&&(e=1/0),r=void 0===r?1:Math.abs(r),t>e&&(r=-r),this._start=t,this._end=e,this._step=r,this.size=Math.max(0,Math.ceil((e-t)/r-1)+1),0===this.size){if(Er)return Er;Er=this}}function tt(){throw TypeError("Abstract")}function et(){}function rt(){}function nt(){}function it(t){return t>>>1&1073741824|3221225471&t}function ot(t){if(t===!1||null===t||void 0===t)return 0;if("function"==typeof t.valueOf&&(t=t.valueOf(),t===!1||null===t||void 0===t))return 0;if(t===!0)return 1;var e=typeof t;if("number"===e){if(t!==t||t===1/0)return 0;var r=0|t;for(r!==t&&(r^=4294967295*t);t>4294967295;)t/=4294967295,r^=t;return it(r)}if("string"===e)return t.length>Kr?ut(t):st(t);if("function"==typeof t.hashCode)return t.hashCode();if("object"===e)return at(t);if("function"==typeof t.toString)return st(""+t);throw Error("Value type "+e+" cannot be hashed.")}function ut(t){var e=Br[t];return void 0===e&&(e=st(t),Tr===Lr&&(Tr=0,Br={}),Tr++,Br[t]=e),e}function st(t){for(var e=0,r=0;t.length>r;r++)e=31*e+t.charCodeAt(r)|0;return it(e)}function at(t){var e;if(jr&&(e=Or.get(t),void 0!==e))return e;if(e=t[Ur],void 0!==e)return e;if(!Ar){if(e=t.propertyIsEnumerable&&t.propertyIsEnumerable[Ur],void 0!==e)return e;if(e=ht(t),void 0!==e)return e}if(e=++Rr,1073741824&Rr&&(Rr=0),jr)Or.set(t,e);else{if(void 0!==kr&&kr(t)===!1)throw Error("Non-extensible objects are not allowed as keys."); 12 | if(Ar)Object.defineProperty(t,Ur,{enumerable:!1,configurable:!1,writable:!1,value:e});else if(void 0!==t.propertyIsEnumerable&&t.propertyIsEnumerable===t.constructor.prototype.propertyIsEnumerable)t.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},t.propertyIsEnumerable[Ur]=e;else{if(void 0===t.nodeType)throw Error("Unable to set a non-enumerable property on object.");t[Ur]=e}}return e}function ht(t){if(t&&t.nodeType>0)switch(t.nodeType){case 1:return t.uniqueID;case 9:return t.documentElement&&t.documentElement.uniqueID}}function ft(t){Z(t!==1/0,"Cannot perform this action with an infinite size.")}function ct(t){return null===t||void 0===t?zt():_t(t)&&!h(t)?t:zt().withMutations(function(e){var n=r(t);ft(n.size),n.forEach(function(t,r){return e.set(r,t)})})}function _t(t){return!(!t||!t[Wr])}function pt(t,e){this.ownerID=t,this.entries=e}function vt(t,e,r){this.ownerID=t,this.bitmap=e,this.nodes=r}function lt(t,e,r){this.ownerID=t,this.count=e,this.nodes=r}function yt(t,e,r){this.ownerID=t,this.keyHash=e,this.entries=r}function dt(t,e,r){this.ownerID=t,this.keyHash=e,this.entry=r}function mt(t,e,r){this._type=e,this._reverse=r,this._stack=t._root&&wt(t._root)}function gt(t,e){return z(t,e[0],e[1])}function wt(t,e){return{node:t,index:0,__prev:e}}function St(t,e,r,n){var i=Object.create(Cr);return i.size=t,i._root=e,i.__ownerID=r,i.__hash=n,i.__altered=!1,i}function zt(){return Jr||(Jr=St(0))}function It(t,e,r){var n,i;if(t._root){var o=f(dr),u=f(mr);if(n=bt(t._root,t.__ownerID,0,void 0,e,r,o,u),!u.value)return t;i=t.size+(o.value?r===yr?-1:1:0)}else{if(r===yr)return t;i=1,n=new pt(t.__ownerID,[[e,r]])}return t.__ownerID?(t.size=i,t._root=n,t.__hash=void 0,t.__altered=!0,t):n?St(i,n):zt()}function bt(t,e,r,n,i,o,u,s){return t?t.update(e,r,n,i,o,u,s):o===yr?t:(c(s),c(u),new dt(e,n,[i,o]))}function qt(t){return t.constructor===dt||t.constructor===yt}function Dt(t,e,r,n,i){if(t.keyHash===n)return new yt(e,n,[t.entry,i]);var o,u=(0===r?t.keyHash:t.keyHash>>>r)&lr,s=(0===r?n:n>>>r)&lr,a=u===s?[Dt(t,e,r+pr,n,i)]:(o=new dt(e,n,i), 13 | s>u?[t,o]:[o,t]);return new vt(e,1<o;o++){var u=e[o];i=i.update(t,0,void 0,u[0],u[1])}return i}function Et(t,e,r,n){for(var i=0,o=0,u=Array(r),s=0,a=1,h=e.length;h>s;s++,a<<=1){var f=e[s];void 0!==f&&s!==n&&(i|=a,u[o++]=f)}return new vt(t,i,u)}function Ot(t,e,r,n,i){for(var o=0,u=Array(vr),s=0;0!==r;s++,r>>>=1)u[s]=1&r?e[o++]:void 0;return u[n]=i,new lt(t,o+1,u)}function xt(t,e,n){for(var i=[],u=0;n.length>u;u++){var s=n[u],a=r(s);o(s)||(a=a.map(function(t){return H(t)})),i.push(a)}return jt(t,e,i)}function kt(t,e,r){return t&&t.mergeDeep&&o(e)?t.mergeDeep(e):X(t,e)?t:e}function At(t){return function(e,r,n){if(e&&e.mergeDeepWith&&o(r))return e.mergeDeepWith(t,r);var i=t(e,r,n);return X(e,i)?e:i}}function jt(t,e,r){return r=r.filter(function(t){return 0!==t.size}),0===r.length?t:0!==t.size||t.__ownerID||1!==r.length?t.withMutations(function(t){for(var n=e?function(r,n){t.update(n,yr,function(t){return t===yr?r:e(t,r,n)})}:function(e,r){t.set(r,e)},i=0;r.length>i;i++)r[i].forEach(n)}):t.constructor(r[0])}function Rt(t,e,r,n){var i=t===yr,o=e.next();if(o.done){var u=i?r:t,s=n(u);return s===u?t:s}Z(i||t&&t.set,"invalid keyPath");var a=o.value,h=i?yr:t.get(a,yr),f=Rt(h,e,r,n);return f===h?t:f===yr?t.remove(a):(i?zt():t).set(a,f)}function Ut(t){return t-=t>>1&1431655765,t=(858993459&t)+(t>>2&858993459),t=t+(t>>4)&252645135,t+=t>>8,t+=t>>16,127&t}function Kt(t,e,r,n){var i=n?t:p(t);return i[e]=r,i}function Lt(t,e,r,n){var i=t.length+1;if(n&&e+1===i)return t[e]=r,t;for(var o=Array(i),u=0,s=0;i>s;s++)s===e?(o[s]=r,u=-1):o[s]=t[s+u];return o}function Tt(t,e,r){var n=t.length-1;if(r&&e===n)return t.pop(),t;for(var i=Array(n),o=0,u=0;n>u;u++)u===e&&(o=1),i[u]=t[u+o];return i}function Bt(t){var e=Pt();if(null===t||void 0===t)return e;if(Wt(t))return t;var r=n(t),i=r.size;return 0===i?e:(ft(i),i>0&&vr>i?Nt(0,i,pr,null,new Ct(r.toArray())):e.withMutations(function(t){t.setSize(i),r.forEach(function(e,r){return t.set(r,e)})}))}function Wt(t){ 14 | return!(!t||!t[Vr])}function Ct(t,e){this.array=t,this.ownerID=e}function Jt(t,e){function r(t,e,r){return 0===e?n(t,r):i(t,e,r)}function n(t,r){var n=r===s?a&&a.array:t&&t.array,i=r>o?0:o-r,h=u-r;return h>vr&&(h=vr),function(){if(i===h)return Xr;var t=e?--h:i++;return n&&n[t]}}function i(t,n,i){var s,a=t&&t.array,h=i>o?0:o-i>>n,f=(u-i>>n)+1;return f>vr&&(f=vr),function(){for(;;){if(s){var t=s();if(t!==Xr)return t;s=null}if(h===f)return Xr;var o=e?--f:h++;s=r(a&&a[o],n-pr,i+(o<=t.size||0>e)return t.withMutations(function(t){0>e?Xt(t,e).set(0,r):Xt(t,0,e+1).set(e,r)});e+=t._origin;var n=t._tail,i=t._root,o=f(mr);return e>=Gt(t._capacity)?n=Vt(n,t.__ownerID,0,e,r,o):i=Vt(i,t.__ownerID,t._level,e,r,o),o.value?t.__ownerID?(t._root=i,t._tail=n,t.__hash=void 0,t.__altered=!0,t):Nt(t._origin,t._capacity,t._level,i,n):t}function Vt(t,e,r,n,i,o){var u=n>>>r&lr,s=t&&t.array.length>u;if(!s&&void 0===i)return t;var a;if(r>0){var h=t&&t.array[u],f=Vt(h,e,r-pr,n,i,o);return f===h?t:(a=Yt(t,e),a.array[u]=f,a)}return s&&t.array[u]===i?t:(c(o),a=Yt(t,e),void 0===i&&u===a.array.length-1?a.array.pop():a.array[u]=i,a)}function Yt(t,e){return e&&t&&e===t.ownerID?t:new Ct(t?t.array.slice():[],e)}function Qt(t,e){if(e>=Gt(t._capacity))return t._tail;if(1<e){for(var r=t._root,n=t._level;r&&n>0;)r=r.array[e>>>n&lr],n-=pr;return r}}function Xt(t,e,r){void 0!==e&&(e=0|e),void 0!==r&&(r=0|r);var n=t.__ownerID||new _,i=t._origin,o=t._capacity,u=i+e,s=void 0===r?o:0>r?o+r:i+r;if(u===i&&s===o)return t;if(u>=s)return t.clear();for(var a=t._level,h=t._root,f=0;0>u+f;)h=new Ct(h&&h.array.length?[void 0,h]:[],n),a+=pr,f+=1<=1<p?Qt(t,s-1):p>c?new Ct([],n):v;if(v&&p>c&&o>u&&v.array.length){h=Yt(h,n);for(var y=h,d=a;d>pr;d-=pr){var m=c>>>d&lr;y=y.array[m]=Yt(y.array[m],n)}y.array[c>>>pr&lr]=v}if(o>s&&(l=l&&l.removeAfter(n,0,s)),u>=p)u-=p,s-=p,a=pr,h=null,l=l&&l.removeBefore(n,0,u);else if(u>i||c>p){for(f=0;h;){var g=u>>>a&lr;if(g!==p>>>a&lr)break;g&&(f+=(1<i&&(h=h.removeBefore(n,a,u-f)),h&&c>p&&(h=h.removeAfter(n,a,p-f)),f&&(u-=f,s-=f)}return t.__ownerID?(t.size=s-u,t._origin=u,t._capacity=s,t._level=a,t._root=h,t._tail=l,t.__hash=void 0,t.__altered=!0,t):Nt(u,s,a,h,l)}function Ft(t,e,r){for(var i=[],u=0,s=0;r.length>s;s++){var a=r[s],h=n(a);h.size>u&&(u=h.size),o(a)||(h=h.map(function(t){return H(t)})),i.push(h)}return u>t.size&&(t=t.setSize(u)),jt(t,e,i)}function Gt(t){return vr>t?0:t-1>>>pr<=vr&&u.size>=2*o.size?(i=u.filter(function(t,e){return void 0!==t&&s!==e}),n=i.toKeyedSeq().map(function(t){return t[0]}).flip().toMap(),t.__ownerID&&(n.__ownerID=i.__ownerID=t.__ownerID)):(n=o.remove(e),i=s===u.size-1?u.pop():u.set(s,void 0))}else if(a){if(r===u.get(s)[1])return t;n=o,i=u.set(s,[e,r])}else n=o.set(e,u.size),i=u.set(u.size,[e,r]);return t.__ownerID?(t.size=n.size,t._map=n,t._list=i,t.__hash=void 0,t):te(n,i)}function ne(t,e){this._iter=t,this._useKeys=e,this.size=t.size}function ie(t){this._iter=t,this.size=t.size}function oe(t){this._iter=t,this.size=t.size}function ue(t){this._iter=t,this.size=t.size}function se(t){var e=Ee(t);return e._iter=t,e.size=t.size,e.flip=function(){return t},e.reverse=function(){var e=t.reverse.apply(this); 16 | return e.flip=function(){return t.reverse()},e},e.has=function(e){return t.includes(e)},e.includes=function(e){return t.has(e)},e.cacheResult=Oe,e.__iterateUncached=function(e,r){var n=this;return t.__iterate(function(t,r){return e(r,t,n)!==!1},r)},e.__iteratorUncached=function(e,r){if(e===Sr){var n=t.__iterator(e,r);return new S(function(){var t=n.next();if(!t.done){var e=t.value[0];t.value[0]=t.value[1],t.value[1]=e}return t})}return t.__iterator(e===wr?gr:wr,r)},e}function ae(t,e,r){var n=Ee(t);return n.size=t.size,n.has=function(e){return t.has(e)},n.get=function(n,i){var o=t.get(n,yr);return o===yr?i:e.call(r,o,n,t)},n.__iterateUncached=function(n,i){var o=this;return t.__iterate(function(t,i,u){return n(e.call(r,t,i,u),i,o)!==!1},i)},n.__iteratorUncached=function(n,i){var o=t.__iterator(Sr,i);return new S(function(){var i=o.next();if(i.done)return i;var u=i.value,s=u[0];return z(n,s,e.call(r,u[1],s,t),i)})},n}function he(t,e){var r=Ee(t);return r._iter=t,r.size=t.size,r.reverse=function(){return t},t.flip&&(r.flip=function(){var e=se(t);return e.reverse=function(){return t.flip()},e}),r.get=function(r,n){return t.get(e?r:-1-r,n)},r.has=function(r){return t.has(e?r:-1-r)},r.includes=function(e){return t.includes(e)},r.cacheResult=Oe,r.__iterate=function(e,r){var n=this;return t.__iterate(function(t,r){return e(t,r,n)},!r)},r.__iterator=function(e,r){return t.__iterator(e,!r)},r}function fe(t,e,r,n){var i=Ee(t);return n&&(i.has=function(n){var i=t.get(n,yr);return i!==yr&&!!e.call(r,i,n,t)},i.get=function(n,i){var o=t.get(n,yr);return o!==yr&&e.call(r,o,n,t)?o:i}),i.__iterateUncached=function(i,o){var u=this,s=0;return t.__iterate(function(t,o,a){return e.call(r,t,o,a)?(s++,i(t,n?o:s-1,u)):void 0},o),s},i.__iteratorUncached=function(i,o){var u=t.__iterator(Sr,o),s=0;return new S(function(){for(;;){var o=u.next();if(o.done)return o;var a=o.value,h=a[0],f=a[1];if(e.call(r,f,h,t))return z(i,n?h:s++,f,o)}})},i}function ce(t,e,r){var n=ct().asMutable();return t.__iterate(function(i,o){n.update(e.call(r,i,o,t),0,function(t){ 17 | return t+1})}),n.asImmutable()}function _e(t,e,r){var n=u(t),i=(h(t)?Zt():ct()).asMutable();t.__iterate(function(o,u){i.update(e.call(r,o,u,t),function(t){return t=t||[],t.push(n?[u,o]:o),t})});var o=Me(t);return i.map(function(e){return be(t,o(e))})}function pe(t,e,r,n){var i=t.size;if(void 0!==e&&(e=0|e),void 0!==r&&(r=r===1/0?i:0|r),d(e,r,i))return t;var o=m(e,i),u=g(r,i);if(o!==o||u!==u)return pe(t.toSeq().cacheResult(),e,r,n);var s,a=u-o;a===a&&(s=0>a?0:a);var h=Ee(t);return h.size=0===s?s:t.size&&s||void 0,!n&&L(t)&&s>=0&&(h.get=function(e,r){return e=l(this,e),e>=0&&s>e?t.get(e+o,r):r}),h.__iterateUncached=function(e,r){var i=this;if(0===s)return 0;if(r)return this.cacheResult().__iterate(e,r);var u=0,a=!0,h=0;return t.__iterate(function(t,r){return a&&(a=u++s)return I();var t=i.next();return n||e===wr?t:e===gr?z(e,a-1,void 0,t):z(e,a-1,t.value[1],t)})},h}function ve(t,e,r){var n=Ee(t);return n.__iterateUncached=function(n,i){var o=this;if(i)return this.cacheResult().__iterate(n,i);var u=0;return t.__iterate(function(t,i,s){return e.call(r,t,i,s)&&++u&&n(t,i,o)}),u},n.__iteratorUncached=function(n,i){var o=this;if(i)return this.cacheResult().__iterator(n,i);var u=t.__iterator(Sr,i),s=!0;return new S(function(){if(!s)return I();var t=u.next();if(t.done)return t;var i=t.value,a=i[0],h=i[1];return e.call(r,h,a,o)?n===Sr?t:z(n,a,h,t):(s=!1,I())})},n}function le(t,e,r,n){var i=Ee(t);return i.__iterateUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterate(i,o);var s=!0,a=0;return t.__iterate(function(t,o,h){return s&&(s=e.call(r,t,o,h))?void 0:(a++,i(t,n?o:a-1,u))}),a},i.__iteratorUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterator(i,o);var s=t.__iterator(Sr,o),a=!0,h=0;return new S(function(){var t,o,f;do{if(t=s.next(),t.done)return n||i===wr?t:i===gr?z(i,h++,void 0,t):z(i,h++,t.value[1],t); 18 | var c=t.value;o=c[0],f=c[1],a&&(a=e.call(r,f,o,u))}while(a);return i===Sr?t:z(i,o,f,t)})},i}function ye(t,e){var n=u(t),i=[t].concat(e).map(function(t){return o(t)?n&&(t=r(t)):t=n?B(t):W(Array.isArray(t)?t:[t]),t}).filter(function(t){return 0!==t.size});if(0===i.length)return t;if(1===i.length){var a=i[0];if(a===t||n&&u(a)||s(t)&&s(a))return a}var h=new j(i);return n?h=h.toKeyedSeq():s(t)||(h=h.toSetSeq()),h=h.flatten(!0),h.size=i.reduce(function(t,e){if(void 0!==t){var r=e.size;if(void 0!==r)return t+r}},0),h}function de(t,e,r){var n=Ee(t);return n.__iterateUncached=function(n,i){function u(t,h){var f=this;t.__iterate(function(t,i){return(!e||e>h)&&o(t)?u(t,h+1):n(t,r?i:s++,f)===!1&&(a=!0),!a},i)}var s=0,a=!1;return u(t,0),s},n.__iteratorUncached=function(n,i){var u=t.__iterator(n,i),s=[],a=0;return new S(function(){for(;u;){var t=u.next();if(t.done===!1){var h=t.value;if(n===Sr&&(h=h[1]),e&&!(e>s.length)||!o(h))return r?t:z(n,a++,h,t);s.push(u),u=h.__iterator(n,i)}else u=s.pop()}return I()})},n}function me(t,e,r){var n=Me(t);return t.toSeq().map(function(i,o){return n(e.call(r,i,o,t))}).flatten(!0)}function ge(t,e){var r=Ee(t);return r.size=t.size&&2*t.size-1,r.__iterateUncached=function(r,n){var i=this,o=0;return t.__iterate(function(t,n){return(!o||r(e,o++,i)!==!1)&&r(t,o++,i)!==!1},n),o},r.__iteratorUncached=function(r,n){var i,o=t.__iterator(wr,n),u=0;return new S(function(){return(!i||u%2)&&(i=o.next(),i.done)?i:u%2?z(r,u++,e):z(r,u++,i.value,i)})},r}function we(t,e,r){e||(e=xe);var n=u(t),i=0,o=t.toSeq().map(function(e,n){return[n,e,i++,r?r(e,n,t):e]}).toArray();return o.sort(function(t,r){return e(t[3],r[3])||t[2]-r[2]}).forEach(n?function(t,e){o[e].length=2}:function(t,e){o[e]=t[1]}),n?x(o):s(t)?k(o):A(o)}function Se(t,e,r){if(e||(e=xe),r){var n=t.toSeq().map(function(e,n){return[e,r(e,n,t)]}).reduce(function(t,r){return ze(e,t[1],r[1])?r:t});return n&&n[0]}return t.reduce(function(t,r){return ze(e,t,r)?r:t})}function ze(t,e,r){var n=t(r,e);return 0===n&&r!==e&&(void 0===r||null===r||r!==r)||n>0}function Ie(t,r,n){ 19 | var i=Ee(t);return i.size=new j(n).map(function(t){return t.size}).min(),i.__iterate=function(t,e){for(var r,n=this.__iterator(wr,e),i=0;!(r=n.next()).done&&t(r.value,i++,this)!==!1;);return i},i.__iteratorUncached=function(t,i){var o=n.map(function(t){return t=e(t),D(i?t.reverse():t)}),u=0,s=!1;return new S(function(){var e;return s||(e=o.map(function(t){return t.next()}),s=e.some(function(t){return t.done})),s?I():z(t,u++,r.apply(null,e.map(function(t){return t.value})))})},i}function be(t,e){return L(t)?e:t.constructor(e)}function qe(t){if(t!==Object(t))throw new TypeError("Expected [K, V] tuple: "+t)}function De(t){return ft(t.size),v(t)}function Me(t){return u(t)?r:s(t)?n:i}function Ee(t){return Object.create((u(t)?x:s(t)?k:A).prototype)}function Oe(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):O.prototype.cacheResult.call(this)}function xe(t,e){return t>e?1:e>t?-1:0}function ke(t){var r=D(t);if(!r){if(!E(t))throw new TypeError("Expected iterable or array-like: "+t);r=D(e(t))}return r}function Ae(t,e){var r,n=function(o){if(o instanceof n)return o;if(!(this instanceof n))return new n(o);if(!r){r=!0;var u=Object.keys(t);Ue(i,u),i.size=u.length,i._name=e,i._keys=u,i._defaultValues=t}this._map=ct(o)},i=n.prototype=Object.create(Gr);return i.constructor=n,n}function je(t,e,r){var n=Object.create(Object.getPrototypeOf(t));return n._map=e,n.__ownerID=r,n}function Re(t){return t._name||t.constructor.name||"Record"}function Ue(t,e){try{e.forEach(Ke.bind(void 0,t))}catch(r){}}function Ke(t,e){Object.defineProperty(t,e,{get:function(){return this.get(e)},set:function(t){Z(this.__ownerID,"Cannot set on an immutable record."),this.set(e,t)}})}function Le(t){return null===t||void 0===t?Ce():Te(t)&&!h(t)?t:Ce().withMutations(function(e){var r=i(t);ft(r.size),r.forEach(function(t){return e.add(t)})})}function Te(t){return!(!t||!t[Zr])}function Be(t,e){return t.__ownerID?(t.size=e.size,t._map=e,t):e===t._map?t:0===e.size?t.__empty():t.__make(e)}function We(t,e){var r=Object.create($r); 20 | return r.size=t?t.size:0,r._map=t,r.__ownerID=e,r}function Ce(){return tn||(tn=We(zt()))}function Je(t){return null===t||void 0===t?He():Ne(t)?t:He().withMutations(function(e){var r=i(t);ft(r.size),r.forEach(function(t){return e.add(t)})})}function Ne(t){return Te(t)&&h(t)}function Pe(t,e){var r=Object.create(en);return r.size=t?t.size:0,r._map=t,r.__ownerID=e,r}function He(){return rn||(rn=Pe(ee()))}function Ve(t){return null===t||void 0===t?Xe():Ye(t)?t:Xe().unshiftAll(t)}function Ye(t){return!(!t||!t[nn])}function Qe(t,e,r,n){var i=Object.create(on);return i.size=t,i._head=e,i.__ownerID=r,i.__hash=n,i.__altered=!1,i}function Xe(){return un||(un=Qe(0))}function Fe(t,e){var r=function(r){t.prototype[r]=e[r]};return Object.keys(e).forEach(r),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(e).forEach(r),t}function Ge(t,e){return e}function Ze(t,e){return[e,t]}function $e(t){return function(){return!t.apply(this,arguments)}}function tr(t){return function(){return-t.apply(this,arguments)}}function er(t){return"string"==typeof t?JSON.stringify(t):t+""}function rr(){return p(arguments)}function nr(t,e){return e>t?1:t>e?-1:0}function ir(t){if(t.size===1/0)return 0;var e=h(t),r=u(t),n=e?1:0,i=t.__iterate(r?e?function(t,e){n=31*n+ur(ot(t),ot(e))|0}:function(t,e){n=n+ur(ot(t),ot(e))|0}:e?function(t){n=31*n+ot(t)|0}:function(t){n=n+ot(t)|0});return or(i,n)}function or(t,e){return e=xr(e,3432918353),e=xr(e<<15|e>>>-15,461845907),e=xr(e<<13|e>>>-13,5),e=(e+3864292196|0)^t,e=xr(e^e>>>16,2246822507),e=xr(e^e>>>13,3266489909),e=it(e^e>>>16)}function ur(t,e){return t^e+2654435769+(t<<6)+(t>>2)|0}var sr=Array.prototype.slice;t(r,e),t(n,e),t(i,e),e.isIterable=o,e.isKeyed=u,e.isIndexed=s,e.isAssociative=a,e.isOrdered=h,e.Keyed=r,e.Indexed=n,e.Set=i;var ar="@@__IMMUTABLE_ITERABLE__@@",hr="@@__IMMUTABLE_KEYED__@@",fr="@@__IMMUTABLE_INDEXED__@@",cr="@@__IMMUTABLE_ORDERED__@@",_r="delete",pr=5,vr=1<=i;i++)if(t(r[e?n-i:i],i,this)===!1)return i+1;return i},j.prototype.__iterator=function(t,e){var r=this._array,n=r.length-1,i=0;return new S(function(){return i>n?I():z(t,i,r[e?n-i++:i++])})},t(R,x),R.prototype.get=function(t,e){return void 0===e||this.has(t)?this._object[t]:e},R.prototype.has=function(t){return this._object.hasOwnProperty(t)},R.prototype.__iterate=function(t,e){for(var r=this._object,n=this._keys,i=n.length-1,o=0;i>=o;o++){var u=n[e?i-o:o];if(t(r[u],u,this)===!1)return o+1}return o},R.prototype.__iterator=function(t,e){var r=this._object,n=this._keys,i=n.length-1,o=0;return new S(function(){var u=n[e?i-o:o];return o++>i?I():z(t,u,r[u])})},R.prototype[cr]=!0,t(U,k),U.prototype.__iterateUncached=function(t,e){if(e)return this.cacheResult().__iterate(t,e); 22 | var r=this._iterable,n=D(r),i=0;if(q(n))for(var o;!(o=n.next()).done&&t(o.value,i++,this)!==!1;);return i},U.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e);var r=this._iterable,n=D(r);if(!q(n))return new S(I);var i=0;return new S(function(){var e=n.next();return e.done?e:z(t,i++,e.value)})},t(K,k),K.prototype.__iterateUncached=function(t,e){if(e)return this.cacheResult().__iterate(t,e);for(var r=this._iterator,n=this._iteratorCache,i=0;n.length>i;)if(t(n[i],i++,this)===!1)return i;for(var o;!(o=r.next()).done;){var u=o.value;if(n[i]=u,t(u,i++,this)===!1)break}return i},K.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e);var r=this._iterator,n=this._iteratorCache,i=0;return new S(function(){if(i>=n.length){var e=r.next();if(e.done)return e;n[i]=e.value}return z(t,i,n[i++])})};var Dr;t(G,k),G.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},G.prototype.get=function(t,e){return this.has(t)?this._value:e},G.prototype.includes=function(t){return X(this._value,t)},G.prototype.slice=function(t,e){var r=this.size;return d(t,e,r)?this:new G(this._value,g(e,r)-m(t,r))},G.prototype.reverse=function(){return this},G.prototype.indexOf=function(t){return X(this._value,t)?0:-1},G.prototype.lastIndexOf=function(t){return X(this._value,t)?this.size:-1},G.prototype.__iterate=function(t,e){for(var r=0;this.size>r;r++)if(t(this._value,r,this)===!1)return r+1;return r},G.prototype.__iterator=function(t,e){var r=this,n=0;return new S(function(){return r.size>n?z(t,n++,r._value):I()})},G.prototype.equals=function(t){return t instanceof G?X(this._value,t._value):F(t)};var Mr;t($,k),$.prototype.toString=function(){return 0===this.size?"Range []":"Range [ "+this._start+"..."+this._end+(1!==this._step?" by "+this._step:"")+" ]"},$.prototype.get=function(t,e){return this.has(t)?this._start+l(this,t)*this._step:e},$.prototype.includes=function(t){var e=(t-this._start)/this._step;return e>=0&&this.size>e&&e===Math.floor(e); 23 | },$.prototype.slice=function(t,e){return d(t,e,this.size)?this:(t=m(t,this.size),e=g(e,this.size),t>=e?new $(0,0):new $(this.get(t,this._end),this.get(e,this._end),this._step))},$.prototype.indexOf=function(t){var e=t-this._start;if(e%this._step===0){var r=e/this._step;if(r>=0&&this.size>r)return r}return-1},$.prototype.lastIndexOf=function(t){return this.indexOf(t)},$.prototype.__iterate=function(t,e){for(var r=this.size-1,n=this._step,i=e?this._start+r*n:this._start,o=0;r>=o;o++){if(t(i,o,this)===!1)return o+1;i+=e?-n:n}return o},$.prototype.__iterator=function(t,e){var r=this.size-1,n=this._step,i=e?this._start+r*n:this._start,o=0;return new S(function(){var u=i;return i+=e?-n:n,o>r?I():z(t,o++,u)})},$.prototype.equals=function(t){return t instanceof $?this._start===t._start&&this._end===t._end&&this._step===t._step:F(this,t)};var Er;t(tt,e),t(et,tt),t(rt,tt),t(nt,tt),tt.Keyed=et,tt.Indexed=rt,tt.Set=nt;var Or,xr="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(t,e){t=0|t,e=0|e;var r=65535&t,n=65535&e;return r*n+((t>>>16)*n+r*(e>>>16)<<16>>>0)|0},kr=Object.isExtensible,Ar=function(){try{return Object.defineProperty({},"@",{}),!0}catch(t){return!1}}(),jr="function"==typeof WeakMap;jr&&(Or=new WeakMap);var Rr=0,Ur="__immutablehash__";"function"==typeof Symbol&&(Ur=Symbol(Ur));var Kr=16,Lr=255,Tr=0,Br={};t(ct,et),ct.of=function(){var t=sr.call(arguments,0);return zt().withMutations(function(e){for(var r=0;t.length>r;r+=2){if(r+1>=t.length)throw Error("Missing value for key: "+t[r]);e.set(t[r],t[r+1])}})},ct.prototype.toString=function(){return this.__toString("Map {","}")},ct.prototype.get=function(t,e){return this._root?this._root.get(0,void 0,t,e):e},ct.prototype.set=function(t,e){return It(this,t,e)},ct.prototype.setIn=function(t,e){return this.updateIn(t,yr,function(){return e})},ct.prototype.remove=function(t){return It(this,t,yr)},ct.prototype.deleteIn=function(t){return this.updateIn(t,function(){return yr})},ct.prototype.update=function(t,e,r){return 1===arguments.length?t(this):this.updateIn([t],e,r); 24 | },ct.prototype.updateIn=function(t,e,r){r||(r=e,e=void 0);var n=Rt(this,ke(t),e,r);return n===yr?void 0:n},ct.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):zt()},ct.prototype.merge=function(){return xt(this,void 0,arguments)},ct.prototype.mergeWith=function(t){var e=sr.call(arguments,1);return xt(this,t,e)},ct.prototype.mergeIn=function(t){var e=sr.call(arguments,1);return this.updateIn(t,zt(),function(t){return"function"==typeof t.merge?t.merge.apply(t,e):e[e.length-1]})},ct.prototype.mergeDeep=function(){return xt(this,kt,arguments)},ct.prototype.mergeDeepWith=function(t){var e=sr.call(arguments,1);return xt(this,At(t),e)},ct.prototype.mergeDeepIn=function(t){var e=sr.call(arguments,1);return this.updateIn(t,zt(),function(t){return"function"==typeof t.mergeDeep?t.mergeDeep.apply(t,e):e[e.length-1]})},ct.prototype.sort=function(t){return Zt(we(this,t))},ct.prototype.sortBy=function(t,e){return Zt(we(this,e,t))},ct.prototype.withMutations=function(t){var e=this.asMutable();return t(e),e.wasAltered()?e.__ensureOwner(this.__ownerID):this},ct.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new _)},ct.prototype.asImmutable=function(){return this.__ensureOwner()},ct.prototype.wasAltered=function(){return this.__altered},ct.prototype.__iterator=function(t,e){return new mt(this,t,e)},ct.prototype.__iterate=function(t,e){var r=this,n=0;return this._root&&this._root.iterate(function(e){return n++,t(e[1],e[0],r)},e),n},ct.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?St(this.size,this._root,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},ct.isMap=_t;var Wr="@@__IMMUTABLE_MAP__@@",Cr=ct.prototype;Cr[Wr]=!0,Cr[_r]=Cr.remove,Cr.removeIn=Cr.deleteIn,pt.prototype.get=function(t,e,r,n){for(var i=this.entries,o=0,u=i.length;u>o;o++)if(X(r,i[o][0]))return i[o][1];return n},pt.prototype.update=function(t,e,r,n,i,o,u){for(var s=i===yr,a=this.entries,h=0,f=a.length;f>h&&!X(n,a[h][0]);h++); 25 | var _=f>h;if(_?a[h][1]===i:s)return this;if(c(u),(s||!_)&&c(o),!s||1!==a.length){if(!_&&!s&&a.length>=Nr)return Mt(t,a,n,i);var v=t&&t===this.ownerID,l=v?a:p(a);return _?s?h===f-1?l.pop():l[h]=l.pop():l[h]=[n,i]:l.push([n,i]),v?(this.entries=l,this):new pt(t,l)}},vt.prototype.get=function(t,e,r,n){void 0===e&&(e=ot(r));var i=1<<((0===t?e:e>>>t)&lr),o=this.bitmap;return 0===(o&i)?n:this.nodes[Ut(o&i-1)].get(t+pr,e,r,n)},vt.prototype.update=function(t,e,r,n,i,o,u){void 0===r&&(r=ot(n));var s=(0===e?r:r>>>e)&lr,a=1<=Pr)return Ot(t,_,h,s,v);if(f&&!v&&2===_.length&&qt(_[1^c]))return _[1^c];if(f&&v&&1===_.length&&qt(v))return v;var l=t&&t===this.ownerID,y=f?v?h:h^a:h|a,d=f?v?Kt(_,c,v,l):Tt(_,c,l):Lt(_,c,v,l);return l?(this.bitmap=y,this.nodes=d,this):new vt(t,y,d)},lt.prototype.get=function(t,e,r,n){void 0===e&&(e=ot(r));var i=(0===t?e:e>>>t)&lr,o=this.nodes[i];return o?o.get(t+pr,e,r,n):n},lt.prototype.update=function(t,e,r,n,i,o,u){void 0===r&&(r=ot(n));var s=(0===e?r:r>>>e)&lr,a=i===yr,h=this.nodes,f=h[s];if(a&&!f)return this;var c=bt(f,t,e+pr,r,n,i,o,u);if(c===f)return this;var _=this.count;if(f){if(!c&&(_--,Hr>_))return Et(t,h,_,s)}else _++;var p=t&&t===this.ownerID,v=Kt(h,s,c,p);return p?(this.count=_,this.nodes=v,this):new lt(t,_,v)},yt.prototype.get=function(t,e,r,n){for(var i=this.entries,o=0,u=i.length;u>o;o++)if(X(r,i[o][0]))return i[o][1];return n},yt.prototype.update=function(t,e,r,n,i,o,u){void 0===r&&(r=ot(n));var s=i===yr;if(r!==this.keyHash)return s?this:(c(u),c(o),Dt(this,t,e,r,[n,i]));for(var a=this.entries,h=0,f=a.length;f>h&&!X(n,a[h][0]);h++);var _=f>h;if(_?a[h][1]===i:s)return this;if(c(u),(s||!_)&&c(o),s&&2===f)return new dt(t,this.keyHash,a[1^h]);var v=t&&t===this.ownerID,l=v?a:p(a);return _?s?h===f-1?l.pop():l[h]=l.pop():l[h]=[n,i]:l.push([n,i]),v?(this.entries=l,this):new yt(t,this.keyHash,l)},dt.prototype.get=function(t,e,r,n){return X(r,this.entry[0])?this.entry[1]:n; 26 | },dt.prototype.update=function(t,e,r,n,i,o,u){var s=i===yr,a=X(n,this.entry[0]);return(a?i===this.entry[1]:s)?this:(c(u),s?void c(o):a?t&&t===this.ownerID?(this.entry[1]=i,this):new dt(t,this.keyHash,[n,i]):(c(o),Dt(this,t,e,ot(n),[n,i])))},pt.prototype.iterate=yt.prototype.iterate=function(t,e){for(var r=this.entries,n=0,i=r.length-1;i>=n;n++)if(t(r[e?i-n:n])===!1)return!1},vt.prototype.iterate=lt.prototype.iterate=function(t,e){for(var r=this.nodes,n=0,i=r.length-1;i>=n;n++){var o=r[e?i-n:n];if(o&&o.iterate(t,e)===!1)return!1}},dt.prototype.iterate=function(t,e){return t(this.entry)},t(mt,S),mt.prototype.next=function(){for(var t=this._type,e=this._stack;e;){var r,n=e.node,i=e.index++;if(n.entry){if(0===i)return gt(t,n.entry)}else if(n.entries){if(r=n.entries.length-1,r>=i)return gt(t,n.entries[this._reverse?r-i:i])}else if(r=n.nodes.length-1,r>=i){var o=n.nodes[this._reverse?r-i:i];if(o){if(o.entry)return gt(t,o.entry);e=this._stack=wt(o,e)}continue}e=this._stack=this._stack.__prev}return I()};var Jr,Nr=vr/4,Pr=vr/2,Hr=vr/4;t(Bt,rt),Bt.of=function(){return this(arguments)},Bt.prototype.toString=function(){return this.__toString("List [","]")},Bt.prototype.get=function(t,e){if(t=l(this,t),t>=0&&this.size>t){t+=this._origin;var r=Qt(this,t);return r&&r.array[t&lr]}return e},Bt.prototype.set=function(t,e){return Ht(this,t,e)},Bt.prototype.remove=function(t){return this.has(t)?0===t?this.shift():t===this.size-1?this.pop():this.splice(t,1):this},Bt.prototype.insert=function(t,e){return this.splice(t,0,e)},Bt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=pr,this._root=this._tail=null,this.__hash=void 0,this.__altered=!0,this):Pt()},Bt.prototype.push=function(){var t=arguments,e=this.size;return this.withMutations(function(r){Xt(r,0,e+t.length);for(var n=0;t.length>n;n++)r.set(e+n,t[n])})},Bt.prototype.pop=function(){return Xt(this,0,-1)},Bt.prototype.unshift=function(){var t=arguments;return this.withMutations(function(e){Xt(e,-t.length);for(var r=0;t.length>r;r++)e.set(r,t[r]); 27 | })},Bt.prototype.shift=function(){return Xt(this,1)},Bt.prototype.merge=function(){return Ft(this,void 0,arguments)},Bt.prototype.mergeWith=function(t){var e=sr.call(arguments,1);return Ft(this,t,e)},Bt.prototype.mergeDeep=function(){return Ft(this,kt,arguments)},Bt.prototype.mergeDeepWith=function(t){var e=sr.call(arguments,1);return Ft(this,At(t),e)},Bt.prototype.setSize=function(t){return Xt(this,0,t)},Bt.prototype.slice=function(t,e){var r=this.size;return d(t,e,r)?this:Xt(this,m(t,r),g(e,r))},Bt.prototype.__iterator=function(t,e){var r=0,n=Jt(this,e);return new S(function(){var e=n();return e===Xr?I():z(t,r++,e)})},Bt.prototype.__iterate=function(t,e){for(var r,n=0,i=Jt(this,e);(r=i())!==Xr&&t(r,n++,this)!==!1;);return n},Bt.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Nt(this._origin,this._capacity,this._level,this._root,this._tail,t,this.__hash):(this.__ownerID=t,this)},Bt.isList=Wt;var Vr="@@__IMMUTABLE_LIST__@@",Yr=Bt.prototype;Yr[Vr]=!0,Yr[_r]=Yr.remove,Yr.setIn=Cr.setIn,Yr.deleteIn=Yr.removeIn=Cr.removeIn,Yr.update=Cr.update,Yr.updateIn=Cr.updateIn,Yr.mergeIn=Cr.mergeIn,Yr.mergeDeepIn=Cr.mergeDeepIn,Yr.withMutations=Cr.withMutations,Yr.asMutable=Cr.asMutable,Yr.asImmutable=Cr.asImmutable,Yr.wasAltered=Cr.wasAltered,Ct.prototype.removeBefore=function(t,e,r){if(r===e?1<>>e&lr;if(n>=this.array.length)return new Ct([],t);var i,o=0===n;if(e>0){var u=this.array[n];if(i=u&&u.removeBefore(t,e-pr,r),i===u&&o)return this}if(o&&!i)return this;var s=Yt(this,t);if(!o)for(var a=0;n>a;a++)s.array[a]=void 0;return i&&(s.array[n]=i),s},Ct.prototype.removeAfter=function(t,e,r){if(r===(e?1<>>e&lr;if(n>=this.array.length)return this;var i;if(e>0){var o=this.array[n];if(i=o&&o.removeAfter(t,e-pr,r),i===o&&n===this.array.length-1)return this}var u=Yt(this,t);return u.array.splice(n+1),i&&(u.array[n]=i),u};var Qr,Xr={};t(Zt,ct),Zt.of=function(){return this(arguments)},Zt.prototype.toString=function(){return this.__toString("OrderedMap {","}"); 28 | },Zt.prototype.get=function(t,e){var r=this._map.get(t);return void 0!==r?this._list.get(r)[1]:e},Zt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._map.clear(),this._list.clear(),this):ee()},Zt.prototype.set=function(t,e){return re(this,t,e)},Zt.prototype.remove=function(t){return re(this,t,yr)},Zt.prototype.wasAltered=function(){return this._map.wasAltered()||this._list.wasAltered()},Zt.prototype.__iterate=function(t,e){var r=this;return this._list.__iterate(function(e){return e&&t(e[1],e[0],r)},e)},Zt.prototype.__iterator=function(t,e){return this._list.fromEntrySeq().__iterator(t,e)},Zt.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map.__ensureOwner(t),r=this._list.__ensureOwner(t);return t?te(e,r,t,this.__hash):(this.__ownerID=t,this._map=e,this._list=r,this)},Zt.isOrderedMap=$t,Zt.prototype[cr]=!0,Zt.prototype[_r]=Zt.prototype.remove;var Fr;t(ne,x),ne.prototype.get=function(t,e){return this._iter.get(t,e)},ne.prototype.has=function(t){return this._iter.has(t)},ne.prototype.valueSeq=function(){return this._iter.valueSeq()},ne.prototype.reverse=function(){var t=this,e=he(this,!0);return this._useKeys||(e.valueSeq=function(){return t._iter.toSeq().reverse()}),e},ne.prototype.map=function(t,e){var r=this,n=ae(this,t,e);return this._useKeys||(n.valueSeq=function(){return r._iter.toSeq().map(t,e)}),n},ne.prototype.__iterate=function(t,e){var r,n=this;return this._iter.__iterate(this._useKeys?function(e,r){return t(e,r,n)}:(r=e?De(this):0,function(i){return t(i,e?--r:r++,n)}),e)},ne.prototype.__iterator=function(t,e){if(this._useKeys)return this._iter.__iterator(t,e);var r=this._iter.__iterator(wr,e),n=e?De(this):0;return new S(function(){var i=r.next();return i.done?i:z(t,e?--n:n++,i.value,i)})},ne.prototype[cr]=!0,t(ie,k),ie.prototype.includes=function(t){return this._iter.includes(t)},ie.prototype.__iterate=function(t,e){var r=this,n=0;return this._iter.__iterate(function(e){return t(e,n++,r)},e)},ie.prototype.__iterator=function(t,e){var r=this._iter.__iterator(wr,e),n=0; 29 | return new S(function(){var e=r.next();return e.done?e:z(t,n++,e.value,e)})},t(oe,A),oe.prototype.has=function(t){return this._iter.includes(t)},oe.prototype.__iterate=function(t,e){var r=this;return this._iter.__iterate(function(e){return t(e,e,r)},e)},oe.prototype.__iterator=function(t,e){var r=this._iter.__iterator(wr,e);return new S(function(){var e=r.next();return e.done?e:z(t,e.value,e.value,e)})},t(ue,x),ue.prototype.entrySeq=function(){return this._iter.toSeq()},ue.prototype.__iterate=function(t,e){var r=this;return this._iter.__iterate(function(e){if(e){qe(e);var n=o(e);return t(n?e.get(1):e[1],n?e.get(0):e[0],r)}},e)},ue.prototype.__iterator=function(t,e){var r=this._iter.__iterator(wr,e);return new S(function(){for(;;){var e=r.next();if(e.done)return e;var n=e.value;if(n){qe(n);var i=o(n);return z(t,i?n.get(0):n[0],i?n.get(1):n[1],e)}}})},ie.prototype.cacheResult=ne.prototype.cacheResult=oe.prototype.cacheResult=ue.prototype.cacheResult=Oe,t(Ae,et),Ae.prototype.toString=function(){return this.__toString(Re(this)+" {","}")},Ae.prototype.has=function(t){return this._defaultValues.hasOwnProperty(t)},Ae.prototype.get=function(t,e){if(!this.has(t))return e;var r=this._defaultValues[t];return this._map?this._map.get(t,r):r},Ae.prototype.clear=function(){if(this.__ownerID)return this._map&&this._map.clear(),this;var t=this.constructor;return t._empty||(t._empty=je(this,zt()))},Ae.prototype.set=function(t,e){if(!this.has(t))throw Error('Cannot set unknown key "'+t+'" on '+Re(this));if(this._map&&!this._map.has(t)){var r=this._defaultValues[t];if(e===r)return this}var n=this._map&&this._map.set(t,e);return this.__ownerID||n===this._map?this:je(this,n)},Ae.prototype.remove=function(t){if(!this.has(t))return this;var e=this._map&&this._map.remove(t);return this.__ownerID||e===this._map?this:je(this,e)},Ae.prototype.wasAltered=function(){return this._map.wasAltered()},Ae.prototype.__iterator=function(t,e){var n=this;return r(this._defaultValues).map(function(t,e){return n.get(e)}).__iterator(t,e)},Ae.prototype.__iterate=function(t,e){ 30 | var n=this;return r(this._defaultValues).map(function(t,e){return n.get(e)}).__iterate(t,e)},Ae.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map&&this._map.__ensureOwner(t);return t?je(this,e,t):(this.__ownerID=t,this._map=e,this)};var Gr=Ae.prototype;Gr[_r]=Gr.remove,Gr.deleteIn=Gr.removeIn=Cr.removeIn,Gr.merge=Cr.merge,Gr.mergeWith=Cr.mergeWith,Gr.mergeIn=Cr.mergeIn,Gr.mergeDeep=Cr.mergeDeep,Gr.mergeDeepWith=Cr.mergeDeepWith,Gr.mergeDeepIn=Cr.mergeDeepIn,Gr.setIn=Cr.setIn,Gr.update=Cr.update,Gr.updateIn=Cr.updateIn,Gr.withMutations=Cr.withMutations,Gr.asMutable=Cr.asMutable,Gr.asImmutable=Cr.asImmutable,t(Le,nt),Le.of=function(){return this(arguments)},Le.fromKeys=function(t){return this(r(t).keySeq())},Le.prototype.toString=function(){return this.__toString("Set {","}")},Le.prototype.has=function(t){return this._map.has(t)},Le.prototype.add=function(t){return Be(this,this._map.set(t,!0))},Le.prototype.remove=function(t){return Be(this,this._map.remove(t))},Le.prototype.clear=function(){return Be(this,this._map.clear())},Le.prototype.union=function(){var t=sr.call(arguments,0);return t=t.filter(function(t){return 0!==t.size}),0===t.length?this:0!==this.size||this.__ownerID||1!==t.length?this.withMutations(function(e){for(var r=0;t.length>r;r++)i(t[r]).forEach(function(t){return e.add(t)})}):this.constructor(t[0])},Le.prototype.intersect=function(){var t=sr.call(arguments,0);if(0===t.length)return this;t=t.map(function(t){return i(t)});var e=this;return this.withMutations(function(r){e.forEach(function(e){t.every(function(t){return t.includes(e)})||r.remove(e)})})},Le.prototype.subtract=function(){var t=sr.call(arguments,0);if(0===t.length)return this;t=t.map(function(t){return i(t)});var e=this;return this.withMutations(function(r){e.forEach(function(e){t.some(function(t){return t.includes(e)})&&r.remove(e)})})},Le.prototype.merge=function(){return this.union.apply(this,arguments)},Le.prototype.mergeWith=function(t){var e=sr.call(arguments,1);return this.union.apply(this,e)}, 31 | Le.prototype.sort=function(t){return Je(we(this,t))},Le.prototype.sortBy=function(t,e){return Je(we(this,e,t))},Le.prototype.wasAltered=function(){return this._map.wasAltered()},Le.prototype.__iterate=function(t,e){var r=this;return this._map.__iterate(function(e,n){return t(n,n,r)},e)},Le.prototype.__iterator=function(t,e){return this._map.map(function(t,e){return e}).__iterator(t,e)},Le.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map.__ensureOwner(t);return t?this.__make(e,t):(this.__ownerID=t,this._map=e,this)},Le.isSet=Te;var Zr="@@__IMMUTABLE_SET__@@",$r=Le.prototype;$r[Zr]=!0,$r[_r]=$r.remove,$r.mergeDeep=$r.merge,$r.mergeDeepWith=$r.mergeWith,$r.withMutations=Cr.withMutations,$r.asMutable=Cr.asMutable,$r.asImmutable=Cr.asImmutable,$r.__empty=Ce,$r.__make=We;var tn;t(Je,Le),Je.of=function(){return this(arguments)},Je.fromKeys=function(t){return this(r(t).keySeq())},Je.prototype.toString=function(){return this.__toString("OrderedSet {","}")},Je.isOrderedSet=Ne;var en=Je.prototype;en[cr]=!0,en.__empty=He,en.__make=Pe;var rn;t(Ve,rt),Ve.of=function(){return this(arguments)},Ve.prototype.toString=function(){return this.__toString("Stack [","]")},Ve.prototype.get=function(t,e){var r=this._head;for(t=l(this,t);r&&t--;)r=r.next;return r?r.value:e},Ve.prototype.peek=function(){return this._head&&this._head.value},Ve.prototype.push=function(){if(0===arguments.length)return this;for(var t=this.size+arguments.length,e=this._head,r=arguments.length-1;r>=0;r--)e={value:arguments[r],next:e};return this.__ownerID?(this.size=t,this._head=e,this.__hash=void 0,this.__altered=!0,this):Qe(t,e)},Ve.prototype.pushAll=function(t){if(t=n(t),0===t.size)return this;ft(t.size);var e=this.size,r=this._head;return t.reverse().forEach(function(t){e++,r={value:t,next:r}}),this.__ownerID?(this.size=e,this._head=r,this.__hash=void 0,this.__altered=!0,this):Qe(e,r)},Ve.prototype.pop=function(){return this.slice(1)},Ve.prototype.unshift=function(){return this.push.apply(this,arguments)},Ve.prototype.unshiftAll=function(t){ 32 | return this.pushAll(t)},Ve.prototype.shift=function(){return this.pop.apply(this,arguments)},Ve.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):Xe()},Ve.prototype.slice=function(t,e){if(d(t,e,this.size))return this;var r=m(t,this.size),n=g(e,this.size);if(n!==this.size)return rt.prototype.slice.call(this,t,e);for(var i=this.size-r,o=this._head;r--;)o=o.next;return this.__ownerID?(this.size=i,this._head=o,this.__hash=void 0,this.__altered=!0,this):Qe(i,o)},Ve.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Qe(this.size,this._head,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},Ve.prototype.__iterate=function(t,e){if(e)return this.reverse().__iterate(t);for(var r=0,n=this._head;n&&t(n.value,r++,this)!==!1;)n=n.next;return r},Ve.prototype.__iterator=function(t,e){if(e)return this.reverse().__iterator(t);var r=0,n=this._head;return new S(function(){if(n){var e=n.value;return n=n.next,z(t,r++,e)}return I()})},Ve.isStack=Ye;var nn="@@__IMMUTABLE_STACK__@@",on=Ve.prototype;on[nn]=!0,on.withMutations=Cr.withMutations,on.asMutable=Cr.asMutable,on.asImmutable=Cr.asImmutable,on.wasAltered=Cr.wasAltered;var un;e.Iterator=S,Fe(e,{toArray:function(){ft(this.size);var t=Array(this.size||0);return this.valueSeq().__iterate(function(e,r){t[r]=e}),t},toIndexedSeq:function(){return new ie(this)},toJS:function(){return this.toSeq().map(function(t){return t&&"function"==typeof t.toJS?t.toJS():t}).__toJS()},toJSON:function(){return this.toSeq().map(function(t){return t&&"function"==typeof t.toJSON?t.toJSON():t}).__toJS()},toKeyedSeq:function(){return new ne(this,!0)},toMap:function(){return ct(this.toKeyedSeq())},toObject:function(){ft(this.size);var t={};return this.__iterate(function(e,r){t[r]=e}),t},toOrderedMap:function(){return Zt(this.toKeyedSeq())},toOrderedSet:function(){return Je(u(this)?this.valueSeq():this)},toSet:function(){return Le(u(this)?this.valueSeq():this)},toSetSeq:function(){return new oe(this); 33 | },toSeq:function(){return s(this)?this.toIndexedSeq():u(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return Ve(u(this)?this.valueSeq():this)},toList:function(){return Bt(u(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(t,e){return 0===this.size?t+e:t+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+e},concat:function(){var t=sr.call(arguments,0);return be(this,ye(this,t))},includes:function(t){return this.some(function(e){return X(e,t)})},entries:function(){return this.__iterator(Sr)},every:function(t,e){ft(this.size);var r=!0;return this.__iterate(function(n,i,o){return t.call(e,n,i,o)?void 0:(r=!1,!1)}),r},filter:function(t,e){return be(this,fe(this,t,e,!0))},find:function(t,e,r){var n=this.findEntry(t,e);return n?n[1]:r},forEach:function(t,e){return ft(this.size),this.__iterate(e?t.bind(e):t)},join:function(t){ft(this.size),t=void 0!==t?""+t:",";var e="",r=!0;return this.__iterate(function(n){r?r=!1:e+=t,e+=null!==n&&void 0!==n?""+n:""}),e},keys:function(){return this.__iterator(gr)},map:function(t,e){return be(this,ae(this,t,e))},reduce:function(t,e,r){ft(this.size);var n,i;return arguments.length<2?i=!0:n=e,this.__iterate(function(e,o,u){i?(i=!1,n=e):n=t.call(r,n,e,o,u)}),n},reduceRight:function(t,e,r){var n=this.toKeyedSeq().reverse();return n.reduce.apply(n,arguments)},reverse:function(){return be(this,he(this,!0))},slice:function(t,e){return be(this,pe(this,t,e,!0))},some:function(t,e){return!this.every($e(t),e)},sort:function(t){return be(this,we(this,t))},values:function(){return this.__iterator(wr)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some(function(){return!0})},count:function(t,e){return v(t?this.toSeq().filter(t,e):this)},countBy:function(t,e){return ce(this,t,e)},equals:function(t){return F(this,t)},entrySeq:function(){var t=this;if(t._cache)return new j(t._cache);var e=t.toSeq().map(Ze).toIndexedSeq();return e.fromEntrySeq=function(){return t.toSeq()},e},filterNot:function(t,e){ 34 | return this.filter($e(t),e)},findEntry:function(t,e,r){var n=r;return this.__iterate(function(r,i,o){return t.call(e,r,i,o)?(n=[i,r],!1):void 0}),n},findKey:function(t,e){var r=this.findEntry(t,e);return r&&r[0]},findLast:function(t,e,r){return this.toKeyedSeq().reverse().find(t,e,r)},findLastEntry:function(t,e,r){return this.toKeyedSeq().reverse().findEntry(t,e,r)},findLastKey:function(t,e){return this.toKeyedSeq().reverse().findKey(t,e)},first:function(){return this.find(y)},flatMap:function(t,e){return be(this,me(this,t,e))},flatten:function(t){return be(this,de(this,t,!0))},fromEntrySeq:function(){return new ue(this)},get:function(t,e){return this.find(function(e,r){return X(r,t)},void 0,e)},getIn:function(t,e){for(var r,n=this,i=ke(t);!(r=i.next()).done;){var o=r.value;if(n=n&&n.get?n.get(o,yr):yr,n===yr)return e}return n},groupBy:function(t,e){return _e(this,t,e)},has:function(t){return this.get(t,yr)!==yr},hasIn:function(t){return this.getIn(t,yr)!==yr},isSubset:function(t){return t="function"==typeof t.includes?t:e(t),this.every(function(e){return t.includes(e)})},isSuperset:function(t){return t="function"==typeof t.isSubset?t:e(t),t.isSubset(this)},keyOf:function(t){return this.findKey(function(e){return X(e,t)})},keySeq:function(){return this.toSeq().map(Ge).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(t){return this.toKeyedSeq().reverse().keyOf(t)},max:function(t){return Se(this,t)},maxBy:function(t,e){return Se(this,e,t)},min:function(t){return Se(this,t?tr(t):nr)},minBy:function(t,e){return Se(this,e?tr(e):nr,t)},rest:function(){return this.slice(1)},skip:function(t){return this.slice(Math.max(0,t))},skipLast:function(t){return be(this,this.toSeq().reverse().skip(t).reverse())},skipWhile:function(t,e){return be(this,le(this,t,e,!0))},skipUntil:function(t,e){return this.skipWhile($e(t),e)},sortBy:function(t,e){return be(this,we(this,e,t))},take:function(t){return this.slice(0,Math.max(0,t))},takeLast:function(t){return be(this,this.toSeq().reverse().take(t).reverse()); 35 | },takeWhile:function(t,e){return be(this,ve(this,t,e))},takeUntil:function(t,e){return this.takeWhile($e(t),e)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=ir(this))}});var sn=e.prototype;sn[ar]=!0,sn[br]=sn.values,sn.__toJS=sn.toArray,sn.__toStringMapper=er,sn.inspect=sn.toSource=function(){return""+this},sn.chain=sn.flatMap,sn.contains=sn.includes,Fe(r,{flip:function(){return be(this,se(this))},mapEntries:function(t,e){var r=this,n=0;return be(this,this.toSeq().map(function(i,o){return t.call(e,[o,i],n++,r)}).fromEntrySeq())},mapKeys:function(t,e){var r=this;return be(this,this.toSeq().flip().map(function(n,i){return t.call(e,n,i,r)}).flip())}});var an=r.prototype;an[hr]=!0,an[br]=sn.entries,an.__toJS=sn.toObject,an.__toStringMapper=function(t,e){return JSON.stringify(e)+": "+er(t)},Fe(n,{toKeyedSeq:function(){return new ne(this,!1)},filter:function(t,e){return be(this,fe(this,t,e,!1))},findIndex:function(t,e){var r=this.findEntry(t,e);return r?r[0]:-1},indexOf:function(t){var e=this.keyOf(t);return void 0===e?-1:e},lastIndexOf:function(t){var e=this.lastKeyOf(t);return void 0===e?-1:e},reverse:function(){return be(this,he(this,!1))},slice:function(t,e){return be(this,pe(this,t,e,!1))},splice:function(t,e){var r=arguments.length;if(e=Math.max(0|e,0),0===r||2===r&&!e)return this;t=m(t,0>t?this.count():this.size);var n=this.slice(0,t);return be(this,1===r?n:n.concat(p(arguments,2),this.slice(t+e)))},findLastIndex:function(t,e){var r=this.findLastEntry(t,e);return r?r[0]:-1},first:function(){return this.get(0)},flatten:function(t){return be(this,de(this,t,!1))},get:function(t,e){return t=l(this,t),0>t||this.size===1/0||void 0!==this.size&&t>this.size?e:this.find(function(e,r){return r===t},void 0,e)},has:function(t){return t=l(this,t),t>=0&&(void 0!==this.size?this.size===1/0||this.size>t:-1!==this.indexOf(t))},interpose:function(t){return be(this,ge(this,t))},interleave:function(){var t=[this].concat(p(arguments)),e=Ie(this.toSeq(),k.of,t),r=e.flatten(!0);return e.size&&(r.size=e.size*t.length), 36 | be(this,r)},keySeq:function(){return $(0,this.size)},last:function(){return this.get(-1)},skipWhile:function(t,e){return be(this,le(this,t,e,!1))},zip:function(){var t=[this].concat(p(arguments));return be(this,Ie(this,rr,t))},zipWith:function(t){var e=p(arguments);return e[0]=this,be(this,Ie(this,t,e))}}),n.prototype[fr]=!0,n.prototype[cr]=!0,Fe(i,{get:function(t,e){return this.has(t)?t:e},includes:function(t){return this.has(t)},keySeq:function(){return this.valueSeq()}}),i.prototype.has=sn.includes,i.prototype.contains=i.prototype.includes,Fe(x,r.prototype),Fe(k,n.prototype),Fe(A,i.prototype),Fe(et,r.prototype),Fe(rt,n.prototype),Fe(nt,i.prototype);var hn={Iterable:e,Seq:O,Collection:tt,Map:ct,OrderedMap:Zt,List:Bt,Stack:Ve,Set:Le,OrderedSet:Je,Record:Ae,Range:$,Repeat:G,is:X,fromJS:H};return hn}); -------------------------------------------------------------------------------- /home-records/node_modules/immutable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "immutable", 3 | "version": "3.8.1", 4 | "description": "Immutable Data Collections", 5 | "homepage": "https://facebook.github.com/immutable-js", 6 | "author": { 7 | "name": "Lee Byron", 8 | "url": "https://github.com/leebyron" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/facebook/immutable-js.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/facebook/immutable-js/issues" 16 | }, 17 | "main": "dist/immutable.js", 18 | "typings": "dist/immutable-nonambient.d.ts", 19 | "typescript": { 20 | "definition": "dist/immutable.d.ts" 21 | }, 22 | "scripts": { 23 | "build": "grunt default && gulp default", 24 | "lint": "eslint src/ && grunt lint && gulp lint", 25 | "testonly": "./resources/node_test.sh", 26 | "test": "npm run lint && npm run testonly", 27 | "perf": "node ./resources/bench.js", 28 | "start": "npm run build && node ./pages/resources/start.js", 29 | "deploy": "(cd ./pages/out && git init && git config user.name \"Travis CI\" && git config user.email \"github@fb.com\" && git add . && git commit -m \"Deploy to GitHub Pages\" && git push --force --quiet \"https://${GH_TOKEN}@github.com/facebook/immutable-js.git\" master:gh-pages > /dev/null 2>1)" 30 | }, 31 | "jest": { 32 | "scriptPreprocessor": "resources/jestPreprocessor.js", 33 | "testFileExtensions": [ 34 | "js", 35 | "ts" 36 | ], 37 | "persistModuleRegistryBetweenSpecs": true 38 | }, 39 | "devDependencies": { 40 | "acorn": "0.11.x", 41 | "babel-eslint": "^4.1.8", 42 | "benchmark": "^1.0.0", 43 | "bluebird": "3.1.1", 44 | "browser-sync": "2.11.0", 45 | "browserify": "^5.11.2", 46 | "colors": "1.1.2", 47 | "del": "2.2.0", 48 | "es6-transpiler": "0.7.18", 49 | "eslint": "^1.10.3", 50 | "estraverse": "1.9.3", 51 | "express": "^4.13.4", 52 | "fbjs-scripts": "^0.5.0", 53 | "grunt": "0.4.5", 54 | "grunt-cli": "0.1.13", 55 | "grunt-contrib-clean": "0.7.0", 56 | "grunt-contrib-copy": "0.8.2", 57 | "grunt-contrib-jshint": "0.11.3", 58 | "grunt-release": "0.13.0", 59 | "gulp": "3.9.0", 60 | "gulp-concat": "2.6.0", 61 | "gulp-filter": "3.0.1", 62 | "gulp-header": "1.7.1", 63 | "gulp-jest": "^0.2.1", 64 | "gulp-jshint": "^1.8.4", 65 | "gulp-less": "3.0.5", 66 | "gulp-size": "2.0.0", 67 | "gulp-sourcemaps": "1.6.0", 68 | "gulp-uglify": "1.5.1", 69 | "gulp-util": "3.0.7", 70 | "harmonize": "1.4.4", 71 | "jasmine-check": "^0.1.2", 72 | "jest-cli": "^0.5.10", 73 | "jshint-stylish": "^0.4.0", 74 | "magic-string": "0.10.2", 75 | "marked": "0.3.5", 76 | "microtime": "^2.0.0", 77 | "node-jsx": "^0.12.4", 78 | "react": "^0.12.0", 79 | "react-router": "^0.11.2", 80 | "react-tools": "^0.12.0", 81 | "rollup": "0.24.0", 82 | "run-sequence": "1.1.5", 83 | "through2": "2.0.0", 84 | "typescript": "1.7.5", 85 | "uglify-js": "2.6.1", 86 | "vinyl-buffer": "1.0.0", 87 | "vinyl-source-stream": "1.1.0" 88 | }, 89 | "engines": { 90 | "node": ">=0.10.0" 91 | }, 92 | "files": [ 93 | "dist", 94 | "contrib", 95 | "README.md", 96 | "LICENSE", 97 | "PATENTS" 98 | ], 99 | "keywords": [ 100 | "immutable", 101 | "persistent", 102 | "lazy", 103 | "data", 104 | "datastructure", 105 | "functional", 106 | "collection", 107 | "stateless", 108 | "sequence", 109 | "iteration" 110 | ], 111 | "license": "BSD-3-Clause" 112 | } 113 | -------------------------------------------------------------------------------- /home-records/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "home-records", 3 | "version": "1.0.0", 4 | "description": "Immutable data records", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Viktor Kirilov (deepsybg@gmail.com)", 10 | "license": "MIT", 11 | "dependencies": { 12 | "immutable": "^3.8.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /home-records/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | immutable@^3.8.1: 6 | version "3.8.1" 7 | resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2" 8 | -------------------------------------------------------------------------------- /phone-app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } -------------------------------------------------------------------------------- /phone-app/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /phone-app/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | .*/Libraries/react-native/ReactNative.js 16 | 17 | [include] 18 | 19 | [libs] 20 | node_modules/react-native/Libraries/react-native/react-native-interface.js 21 | node_modules/react-native/flow 22 | flow/ 23 | 24 | [options] 25 | module.system=haste 26 | 27 | experimental.strict_type_args=true 28 | 29 | munge_underscores=true 30 | 31 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' 32 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 33 | 34 | suppress_type=$FlowIssue 35 | suppress_type=$FlowFixMe 36 | suppress_type=$FixMe 37 | 38 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-5]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 39 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-5]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 40 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 41 | 42 | unsafe.enable_getters_and_setters=true 43 | 44 | [version] 45 | ^0.35.0 46 | -------------------------------------------------------------------------------- /phone-app/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /phone-app/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | android/app/libs 42 | *.keystore 43 | -------------------------------------------------------------------------------- /phone-app/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /phone-app/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Viktor Kirilov 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 | -------------------------------------------------------------------------------- /phone-app/android/app/BUCK: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # To learn about Buck see [Docs](https://buckbuild.com/). 4 | # To run your application with Buck: 5 | # - install Buck 6 | # - `npm start` - to start the packager 7 | # - `cd android` 8 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 9 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 10 | # - `buck install -r android/app` - compile, install and run application 11 | # 12 | 13 | lib_deps = [] 14 | for jarfile in glob(['libs/*.jar']): 15 | name = 'jars__' + re.sub(r'^.*/([^/]+)\.jar$', r'\1', jarfile) 16 | lib_deps.append(':' + name) 17 | prebuilt_jar( 18 | name = name, 19 | binary_jar = jarfile, 20 | ) 21 | 22 | for aarfile in glob(['libs/*.aar']): 23 | name = 'aars__' + re.sub(r'^.*/([^/]+)\.aar$', r'\1', aarfile) 24 | lib_deps.append(':' + name) 25 | android_prebuilt_aar( 26 | name = name, 27 | aar = aarfile, 28 | ) 29 | 30 | android_library( 31 | name = 'all-libs', 32 | exported_deps = lib_deps 33 | ) 34 | 35 | android_library( 36 | name = 'app-code', 37 | srcs = glob([ 38 | 'src/main/java/**/*.java', 39 | ]), 40 | deps = [ 41 | ':all-libs', 42 | ':build_config', 43 | ':res', 44 | ], 45 | ) 46 | 47 | android_build_config( 48 | name = 'build_config', 49 | package = 'com.homeapp', 50 | ) 51 | 52 | android_resource( 53 | name = 'res', 54 | res = 'src/main/res', 55 | package = 'com.homeapp', 56 | ) 57 | 58 | android_binary( 59 | name = 'app', 60 | package_type = 'debug', 61 | manifest = 'src/main/AndroidManifest.xml', 62 | keystore = '//android/keystores:debug', 63 | deps = [ 64 | ':app-code', 65 | ], 66 | ) 67 | -------------------------------------------------------------------------------- /phone-app/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // the root of your project, i.e. where "package.json" lives 37 | * root: "../../", 38 | * 39 | * // where to put the JS bundle asset in debug mode 40 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 41 | * 42 | * // where to put the JS bundle asset in release mode 43 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 44 | * 45 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 46 | * // require('./image.png')), in debug mode 47 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 48 | * 49 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 50 | * // require('./image.png')), in release mode 51 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 52 | * 53 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 54 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 55 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 56 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 57 | * // for example, you might want to remove it from here. 58 | * inputExcludes: ["android/**", "ios/**"], 59 | * 60 | * // override which node gets called and with what additional arguments 61 | * nodeExecutableAndArgs: ["node"] 62 | * 63 | * // supply additional arguments to the packager 64 | * extraPackagerArgs: [] 65 | * ] 66 | */ 67 | 68 | apply from: "../../node_modules/react-native/react.gradle" 69 | 70 | /** 71 | * Set this to true to create two separate APKs instead of one: 72 | * - An APK that only works on ARM devices 73 | * - An APK that only works on x86 devices 74 | * The advantage is the size of the APK is reduced by about 4MB. 75 | * Upload all the APKs to the Play Store and people will download 76 | * the correct one based on the CPU architecture of their device. 77 | */ 78 | def enableSeparateBuildPerCPUArchitecture = false 79 | 80 | /** 81 | * Run Proguard to shrink the Java bytecode in release builds. 82 | */ 83 | def enableProguardInReleaseBuilds = false 84 | 85 | android { 86 | compileSdkVersion 23 87 | buildToolsVersion "23.0.1" 88 | 89 | defaultConfig { 90 | applicationId "com.homeapp" 91 | minSdkVersion 16 92 | targetSdkVersion 22 93 | versionCode 1 94 | versionName "1.0" 95 | ndk { 96 | abiFilters "armeabi-v7a", "x86" 97 | } 98 | } 99 | splits { 100 | abi { 101 | reset() 102 | enable enableSeparateBuildPerCPUArchitecture 103 | universalApk false // If true, also generate a universal APK 104 | include "armeabi-v7a", "x86" 105 | } 106 | } 107 | buildTypes { 108 | release { 109 | minifyEnabled enableProguardInReleaseBuilds 110 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 111 | } 112 | } 113 | // applicationVariants are e.g. debug, release 114 | applicationVariants.all { variant -> 115 | variant.outputs.each { output -> 116 | // For each separate APK per architecture, set a unique version code as described here: 117 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 118 | def versionCodes = ["armeabi-v7a":1, "x86":2] 119 | def abi = output.getFilter(OutputFile.ABI) 120 | if (abi != null) { // null for the universal-debug, universal-release variants 121 | output.versionCodeOverride = 122 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 123 | } 124 | } 125 | } 126 | } 127 | 128 | dependencies { 129 | compile project(':react-native-material-kit') 130 | compile fileTree(dir: "libs", include: ["*.jar"]) 131 | compile "com.android.support:appcompat-v7:23.0.1" 132 | compile "com.facebook.react:react-native:+" // From node_modules 133 | } 134 | 135 | // Run this once to be able to run the application with BUCK 136 | // puts all compile dependencies into folder libs for BUCK to use 137 | task copyDownloadableDepsToLibs(type: Copy) { 138 | from configurations.compile 139 | into 'libs' 140 | } 141 | -------------------------------------------------------------------------------- /phone-app/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # okhttp 54 | 55 | -keepattributes Signature 56 | -keepattributes *Annotation* 57 | -keep class okhttp3.** { *; } 58 | -keep interface okhttp3.** { *; } 59 | -dontwarn okhttp3.** 60 | 61 | # okio 62 | 63 | -keep class sun.misc.Unsafe { *; } 64 | -dontwarn java.nio.file.* 65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 66 | -dontwarn okio.** 67 | -------------------------------------------------------------------------------- /phone-app/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /phone-app/android/app/src/main/java/com/homeapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.homeapp; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "HomeApp"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /phone-app/android/app/src/main/java/com/homeapp/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.homeapp; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.facebook.react.ReactApplication; 7 | import com.github.xinthink.rnmk.ReactMaterialKitPackage; 8 | import com.facebook.react.ReactInstanceManager; 9 | import com.facebook.react.ReactNativeHost; 10 | import com.facebook.react.ReactPackage; 11 | import com.facebook.react.shell.MainReactPackage; 12 | import com.facebook.soloader.SoLoader; 13 | 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | public class MainApplication extends Application implements ReactApplication { 18 | 19 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 20 | @Override 21 | protected boolean getUseDeveloperSupport() { 22 | return BuildConfig.DEBUG; 23 | } 24 | 25 | @Override 26 | protected List getPackages() { 27 | return Arrays.asList( 28 | new MainReactPackage(), 29 | new ReactMaterialKitPackage() 30 | ); 31 | } 32 | }; 33 | 34 | @Override 35 | public ReactNativeHost getReactNativeHost() { 36 | return mReactNativeHost; 37 | } 38 | 39 | @Override 40 | public void onCreate() { 41 | super.onCreate(); 42 | SoLoader.init(this, /* native exopackage */ false); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /phone-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /phone-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /phone-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /phone-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /phone-app/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | HomeApp 3 | 4 | -------------------------------------------------------------------------------- /phone-app/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /phone-app/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.1' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 21 | url "$rootDir/../node_modules/react-native/android" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /phone-app/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /phone-app/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /phone-app/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 6 | -------------------------------------------------------------------------------- /phone-app/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /phone-app/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /phone-app/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = 'debug', 3 | store = 'debug.keystore', 4 | properties = 'debug.keystore.properties', 5 | visibility = [ 6 | 'PUBLIC', 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /phone-app/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /phone-app/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'HomeApp' 2 | include ':react-native-material-kit' 3 | project(':react-native-material-kit').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-material-kit/android') 4 | 5 | include ':app' 6 | -------------------------------------------------------------------------------- /phone-app/app/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import pureRender from 'pure-render-decorator'; 3 | 4 | import { 5 | StyleSheet, 6 | Text, 7 | View, 8 | Image, 9 | ScrollView, 10 | } from 'react-native'; 11 | 12 | import Heating from 'app/components/Heating/Heating'; 13 | import AC from 'app/components/AC/AC'; 14 | import SofaLED from 'app/components/SofaLED/SofaLED'; 15 | import FloorLED from 'app/components/FloorLED/FloorLED'; 16 | 17 | import styles from 'app/styles'; 18 | const backgroundImage = require('./assets/background.png'); 19 | 20 | function getTemperatureString (temperature) { 21 | return parseFloat(temperature.temperature).toFixed(1); 22 | } 23 | 24 | function Home ({ 25 | items, 26 | dispatch, 27 | }) { 28 | return ( 29 | 32 | 33 | 35 | 36 | 38 | {getTemperatureString(items.Temperature)} °C 39 | 40 | 41 | dispatch('Heating', newValue)} 43 | item={items.Heating} /> 44 | 45 | dispatch('AC', newValue)} 47 | item={items.AC} /> 48 | 49 | dispatch('LedFloor', newValue)} 51 | item={items.LedFloor} /> 52 | 53 | dispatch('LedSofa', newValue)} 55 | item={items.LedSofa} /> 56 | 57 | 58 | 59 | 60 | ); 61 | } 62 | 63 | export default pureRender(Home); 64 | -------------------------------------------------------------------------------- /phone-app/app/assets/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/app/assets/background.png -------------------------------------------------------------------------------- /phone-app/app/assets/color_picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/app/assets/color_picker.png -------------------------------------------------------------------------------- /phone-app/app/assets/home-app-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/app/assets/home-app-screenshot.png -------------------------------------------------------------------------------- /phone-app/app/assets/icon_ac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/app/assets/icon_ac.png -------------------------------------------------------------------------------- /phone-app/app/assets/icon_bulb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/app/assets/icon_bulb.png -------------------------------------------------------------------------------- /phone-app/app/assets/icon_cold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/app/assets/icon_cold.png -------------------------------------------------------------------------------- /phone-app/app/assets/icon_cold_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/app/assets/icon_cold_white.png -------------------------------------------------------------------------------- /phone-app/app/assets/icon_heat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/app/assets/icon_heat.png -------------------------------------------------------------------------------- /phone-app/app/assets/icon_hot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepsyx/home-automation/faacd08df7d4ac67ebe11556fdefbe761a6e01c9/phone-app/app/assets/icon_hot.png -------------------------------------------------------------------------------- /phone-app/app/components/AC/AC.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import pureRender from 'pure-render-decorator'; 3 | 4 | import { 5 | Text, 6 | Image, 7 | View, 8 | StyleSheet, 9 | } from 'react-native'; 10 | 11 | import { 12 | MKSlider, 13 | } from 'react-native-material-kit'; 14 | 15 | import Card from 'app/components/Common/Card/Card'; 16 | import CardHeader from 'app/components/Common/CardHeader/CardHeader'; 17 | import ModeSwitch from './ModeSwitch/ModeSwitch'; 18 | 19 | import styles from './styles'; 20 | 21 | const DEGREE_LABELS = [16, 18, 20, 22, 24, 26, 28, 30]; 22 | 23 | function AC ({ 24 | item, 25 | onChange, 26 | }) { 27 | return ( 28 | 29 | onChange(item.set('isEnabled', state))} /> 34 | 35 | 37 | 38 | 40 | 42 | onChange(item.set('temperature', newValue))} /> 52 | 53 | 54 | 56 | {DEGREE_LABELS.map((item) => { 57 | return ( 58 | 61 | 63 | {item}° 64 | 65 | 66 | ); 67 | })} 68 | 69 | 70 | 71 | 73 | onChange(item.set('mode', state))} /> 76 | 77 | 78 | 79 | 80 | ); 81 | }; 82 | 83 | export default pureRender(AC); 84 | -------------------------------------------------------------------------------- /phone-app/app/components/AC/ModeSwitch/ModeSwitch.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import pureRender from 'pure-render-decorator'; 3 | 4 | import { 5 | View, 6 | Image, 7 | TouchableOpacity, 8 | StyleSheet, 9 | } from 'react-native'; 10 | 11 | import styles from './styles'; 12 | 13 | function ModeSwitch ({ 14 | value, 15 | onChange, 16 | }) { 17 | const isHeatMode = value === 'heat'; 18 | 19 | return ( 20 | onChange(isHeatMode ? 'cool' : 'heat')}> 22 | 23 | 24 | 25 | 27 | 28 | 32 | 33 | 37 | 38 | 39 | ); 40 | }; 41 | 42 | export default pureRender(ModeSwitch); 43 | -------------------------------------------------------------------------------- /phone-app/app/components/AC/ModeSwitch/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export default StyleSheet.create({ 4 | background: { 5 | backgroundColor: "rgb(193,179,185)", 6 | borderRadius: 15, 7 | width: 46, 8 | height: 22, 9 | margin: 5 10 | }, 11 | 12 | circle: { 13 | width: 27, 14 | height: 27, 15 | backgroundColor: "rgb(117, 46, 62)", 16 | borderRadius: 20, 17 | margin: 2, 18 | position: 'absolute' 19 | }, 20 | 21 | iconHot: { 22 | width: 14, 23 | position: 'absolute', 24 | marginTop: -14, 25 | marginLeft: 9 26 | }, 27 | 28 | iconCold: { 29 | width: 14, 30 | position: 'absolute', 31 | marginTop: -2, 32 | marginLeft: 32 33 | }, 34 | }); 35 | -------------------------------------------------------------------------------- /phone-app/app/components/AC/styles.js: -------------------------------------------------------------------------------- 1 | import { 2 | StyleSheet 3 | } from 'react-native'; 4 | 5 | export default StyleSheet.create({ 6 | container: { 7 | flexDirection: 'row', 8 | flex: 1, 9 | marginBottom: 15 10 | }, 11 | 12 | sliderContaner: { 13 | flex: 12, 14 | flexDirection: 'column' 15 | }, 16 | 17 | slider: { 18 | flex: 1 19 | }, 20 | 21 | degreeLabelContainer: { 22 | flex: 1, 23 | flexDirection: 'row', 24 | position: 'absolute', 25 | marginTop: 24, 26 | paddingLeft: 15 27 | }, 28 | 29 | degreeLabelItem: { 30 | flex: 1 31 | }, 32 | 33 | degreeLabelText: { 34 | fontSize: 10, 35 | color: "rgb(83,45,62)" 36 | }, 37 | 38 | modeSwitchContainer: { 39 | flex: 3 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /phone-app/app/components/Common/Card/Card.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import pureRender from 'pure-render-decorator'; 3 | 4 | import { 5 | StyleSheet, 6 | View, 7 | } from 'react-native'; 8 | 9 | import styles from './styles'; 10 | 11 | function Card ({ 12 | children, 13 | }) { 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | }; 20 | 21 | export default pureRender(Card); 22 | -------------------------------------------------------------------------------- /phone-app/app/components/Common/Card/styles.js: -------------------------------------------------------------------------------- 1 | import { 2 | StyleSheet 3 | } from 'react-native'; 4 | 5 | export default StyleSheet.create({ 6 | container: { 7 | flex: 1, 8 | backgroundColor: "#ffffff", 9 | borderRadius: 2, 10 | borderColor: "#ffffff", 11 | borderWidth: 1, 12 | shadowColor: "rgba(0, 0, 0, 0.12)", 13 | shadowOpacity: 0.8, 14 | shadowRadius: 2, 15 | shadowOffset: { 16 | height: 1, 17 | width: 2, 18 | }, 19 | alignSelf: 'stretch', 20 | borderRadius: 12, 21 | marginBottom: 10, 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /phone-app/app/components/Common/CardHeader/CardHeader.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import pureRender from 'pure-render-decorator'; 3 | 4 | import { 5 | Image, 6 | Text, 7 | View, 8 | StyleSheet, 9 | } from 'react-native'; 10 | 11 | import { 12 | MKButton, 13 | MKColor, 14 | MKSwitch, 15 | MKIconToggle, 16 | getTheme, 17 | } from 'react-native-material-kit'; 18 | 19 | import styles from './styles'; 20 | 21 | function CardHeader ({ 22 | isEnabled, 23 | label, 24 | icon, 25 | onChange, 26 | }) { 27 | return ( 28 | 30 | 32 | 36 | 37 | 38 | 40 | 42 | {label} 43 | 44 | 45 | 46 | 48 | onChange(e.checked)}/> 58 | 59 | 60 | ); 61 | } 62 | 63 | export default pureRender(CardHeader); 64 | -------------------------------------------------------------------------------- /phone-app/app/components/Common/CardHeader/styles.js: -------------------------------------------------------------------------------- 1 | import { 2 | StyleSheet 3 | } from 'react-native'; 4 | 5 | export default StyleSheet.create({ 6 | container: { 7 | padding : 0, 8 | flexDirection: 'row', 9 | flex: 1, 10 | }, 11 | 12 | iconContainer: { 13 | flex: 5, 14 | marginTop: 14, 15 | }, 16 | 17 | labelContainer: { 18 | padding: 13, 19 | flex: 32, 20 | }, 21 | 22 | switchContainer: { 23 | flex: 10, 24 | }, 25 | 26 | icon: { 27 | height: 21, 28 | }, 29 | 30 | label: { 31 | fontSize: 18, 32 | fontWeight: 'bold', 33 | color: 'rgb(83,45,62)', 34 | }, 35 | 36 | activeTitle: { 37 | color: 'red', 38 | }, 39 | 40 | switchButton: { 41 | padding: 0, 42 | marginBottom: 0, 43 | marginTop: 10, 44 | }, 45 | }); 46 | -------------------------------------------------------------------------------- /phone-app/app/components/Common/Slider/Slider.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import pureRender from 'pure-render-decorator'; 3 | 4 | import { 5 | MKSlider, 6 | } from 'react-native-material-kit'; 7 | 8 | function Slider (props) { 9 | return ( 10 | 18 | ); 19 | }; 20 | 21 | export default pureRender(Slider); 22 | -------------------------------------------------------------------------------- /phone-app/app/components/FloorLED/FloorLED.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import tinycolor from 'tinycolor2'; 3 | import pureRender from 'pure-render-decorator'; 4 | 5 | import { 6 | StyleSheet, 7 | Text, 8 | View, 9 | Image, 10 | PanResponder, 11 | } from 'react-native'; 12 | 13 | import { 14 | MKSlider, 15 | } from 'react-native-material-kit'; 16 | 17 | import Card from 'app/components/Common/Card/Card'; 18 | import CardHeader from 'app/components/Common/CardHeader/CardHeader'; 19 | 20 | import styles from './styles'; 21 | import panBoilerplate from './panBoilerplate'; 22 | 23 | import icon_bulb from 'app/assets/icon_bulb.png'; 24 | 25 | class FloorLighting extends React.Component { 26 | componentWillMount () { 27 | this.state = { 28 | elemWidth: 0 29 | }; 30 | 31 | this._panResponder = PanResponder.create({ 32 | ...panBoilerplate, 33 | onPanResponderMove: (e, gestureState) => this.onPanMove(e, gestureState) 34 | }); 35 | } 36 | 37 | onPanMove (evt, gestureState) { 38 | const pos = gestureState.moveX - 35; 39 | if (pos < 0 || pos > this.state.elemWidth) { 40 | return; 41 | } 42 | 43 | const percentage = pos / this.state.elemWidth * 100; 44 | 45 | const colors = new tinycolor({ 46 | h: parseInt(percentage / 100 * 360), 47 | s: 100, 48 | v: 100 49 | }).toPercentageRgb(); 50 | 51 | const newState = this.props.item 52 | .set('r', parseInt(colors.r)) 53 | .set('g', parseInt(colors.g)) 54 | .set('b', parseInt(colors.b)); 55 | 56 | this.props.onChange(newState); 57 | } 58 | 59 | measureSliderWidth () { 60 | this.refs.slider.measure((fx, fy, width) => { 61 | const SIDE_PADDING = 15; 62 | this.state.elemWidth = width - SIDE_PADDING * 2; 63 | }); 64 | } 65 | 66 | componentDidMount () { 67 | setTimeout(() => this.measureSliderWidth(), 0); 68 | } 69 | 70 | render () { 71 | const { item } = this.props; 72 | 73 | // colors are in percentage, so 100 * 2.5 = 255 74 | const color = new tinycolor({ 75 | r: item.r * 2.5, 76 | g: item.g * 2.5, 77 | b: item.b * 2.5, 78 | }).toHsv(); 79 | 80 | // hue range is [0, 360] 81 | const pos = (color.h / 360) * this.state.elemWidth; 82 | 83 | return ( 84 | 85 | this.props.onChange(this.props.item.set('isEnabled', state))} /> 90 | 91 | 96 | 97 | 101 | 102 | 107 | 108 | 109 | 110 | ); 111 | } 112 | } 113 | 114 | export default pureRender(FloorLighting); 115 | -------------------------------------------------------------------------------- /phone-app/app/components/FloorLED/panBoilerplate.js: -------------------------------------------------------------------------------- 1 | export default { 2 | onStartShouldSetPanResponder: (evt, gestureState) => true, 3 | onStartShouldSetPanResponderCapture: (evt, gestureState) => true, 4 | onMoveShouldSetPanResponder: (evt, gestureState) => true, 5 | onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, 6 | onPanResponderTerminationRequest: (evt, gestureState) => true, 7 | onShouldBlockNativeResponder: (evt, gestureState) => true, 8 | }; 9 | -------------------------------------------------------------------------------- /phone-app/app/components/FloorLED/styles.js: -------------------------------------------------------------------------------- 1 | import { 2 | StyleSheet 3 | } from 'react-native'; 4 | 5 | export default StyleSheet.create({ 6 | sliderContainer: { 7 | padding: 15, 8 | }, 9 | 10 | sliderPoint: { 11 | width: 16, 12 | height: 16, 13 | borderWidth: 2, 14 | borderColor: "rgb(83,45,62)", 15 | position: 'absolute', 16 | top: 11, 17 | borderRadius: 14, 18 | }, 19 | 20 | colorsImage: { 21 | width: 285, 22 | height: 8, 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /phone-app/app/components/Heating/Heating.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import pureRender from 'pure-render-decorator'; 3 | 4 | import Slider from 'app/components/Common/Slider/Slider'; 5 | import Card from 'app/components/Common/Card/Card'; 6 | import CardHeader from 'app/components/Common/CardHeader/CardHeader'; 7 | 8 | function Heating ({ 9 | item, 10 | onChange, 11 | }) { 12 | return ( 13 | 14 | onChange(item.set('isEnabled', newValue))} /> 19 | 20 | onChange(item.set('value', newValue))} /> 23 | 24 | ); 25 | }; 26 | 27 | export default pureRender(Heating); 28 | -------------------------------------------------------------------------------- /phone-app/app/components/Loading/Loading.js: -------------------------------------------------------------------------------- 1 | import React, { 2 | Component, 3 | } from 'react'; 4 | 5 | import { 6 | StyleSheet, 7 | Text, 8 | View, 9 | } from 'react-native'; 10 | 11 | import { 12 | MKSpinner, 13 | } from 'react-native-material-kit'; 14 | 15 | import styles from './styles'; 16 | 17 | export default function Loading () { 18 | return ( 19 | 20 | 21 | 25 | 26 | Connecting... 27 | 28 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /phone-app/app/components/Loading/styles.js: -------------------------------------------------------------------------------- 1 | import { 2 | StyleSheet, 3 | } from 'react-native'; 4 | 5 | export default StyleSheet.create({ 6 | progress: { 7 | width: 150, 8 | }, 9 | 10 | spinner: { 11 | width: 100, 12 | height: 100, 13 | }, 14 | 15 | container: { 16 | alignItems:'center', 17 | justifyContent:'center', 18 | flexDirection:'row', 19 | flex: 1 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /phone-app/app/components/SofaLED/SofaLED.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import pureRender from 'pure-render-decorator'; 3 | 4 | import Slider from 'app/components/Common/Slider/Slider'; 5 | import Card from 'app/components/Common/Card/Card'; 6 | import CardHeader from 'app/components/Common/CardHeader/CardHeader'; 7 | 8 | function SofaLED ({ 9 | item, 10 | onChange, 11 | }) { 12 | return ( 13 | 14 | onChange(item.set('isEnabled', state))} /> 19 | 20 | onChange(item.set('value', newValue))} /> 23 | 24 | ); 25 | }; 26 | 27 | export default pureRender(SofaLED); 28 | -------------------------------------------------------------------------------- /phone-app/app/containers/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Immutable from 'immutable'; 3 | 4 | import Home from 'app/Home'; 5 | import Loading from 'app/components/Loading/Loading'; 6 | 7 | import config from 'home-config'; 8 | import { 9 | Data, 10 | Modules 11 | } from 'home-records'; 12 | 13 | const States = { 14 | CONNECTED: 'connected', 15 | CONNECTING: 'connecting', 16 | DISCONNECTED: 'disconnected' 17 | }; 18 | 19 | export default class App extends React.Component { 20 | constructor (props) { 21 | super(props); 22 | this.state = { 23 | items: new Data(), 24 | status: States.DISCONNECTED, 25 | interval: null, 26 | }; 27 | } 28 | 29 | componentDidMount () { 30 | this.connect(); 31 | } 32 | 33 | connect () { 34 | this.ws = new WebSocket(config.serverUri); 35 | 36 | this.ws.onopen = () => this.onConnectionOpen(); 37 | this.ws.onmessage = (event) => this.onConnectionMessage(event); 38 | this.ws.onclose = () => this.onConnectionClose(); 39 | this.ws.sendJSON = (obj) => this.ws.send(JSON.stringify(obj)); 40 | 41 | this.state.status = States.CONNECTING; 42 | } 43 | 44 | onConnectionOpen () { 45 | this.ws.sendJSON({ 46 | 'auth': config.authToken, 47 | }); 48 | 49 | this.ws.sendJSON({ 50 | 'key': 'STATUS', 51 | }); 52 | } 53 | 54 | onConnectionMessage (event) { 55 | const data = JSON.parse(event.data); 56 | 57 | if (data.key === 'STATUS') { 58 | this.setState({ 59 | status: States.CONNECTED, 60 | }); 61 | return; 62 | } 63 | 64 | if (!Modules[data.key]) { 65 | throw new Error(`Invalid data key: ${data.key}`); 66 | } 67 | 68 | this.setState({ 69 | items: this.state.items.set( 70 | data.key, 71 | new Modules[data.key](data.value), 72 | ), 73 | }); 74 | } 75 | 76 | onConnectionClose () { 77 | this.setState({ 78 | status: States.DISCONNECTED, 79 | }); 80 | 81 | // reconnect mechanics 82 | this.state.interval = setInterval(() => { 83 | if (this.state.status === States.CONNECTING || this.state.status === States.CONNECTED) { 84 | clearInterval(this.state.interval); 85 | return; 86 | } 87 | 88 | this.connect(); 89 | }, 1000); 90 | } 91 | 92 | componentWillUnmount () { 93 | this.ws.close(); 94 | } 95 | 96 | dispatch (key, value) { 97 | this.ws.sendJSON({ 98 | key, 99 | value, 100 | }); 101 | } 102 | 103 | render () { 104 | if (this.state.status !== States.CONNECTED) { 105 | return ( 106 | 107 | ); 108 | } 109 | 110 | return ( 111 | this.dispatch(k, v)} /> 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /phone-app/app/package.json: -------------------------------------------------------------------------------- 1 | { "name": "app" } 2 | -------------------------------------------------------------------------------- /phone-app/app/styles.js: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | 3 | export default StyleSheet.create({ 4 | background: { 5 | flex: 1, 6 | width: null, 7 | height: null, 8 | }, 9 | 10 | temperatureText: { 11 | marginTop: 20, 12 | marginBottom: 40, 13 | fontSize: 70, 14 | color: '#fff', 15 | fontFamily: 'sans-serif-thin', 16 | }, 17 | 18 | container: { 19 | justifyContent: 'center', 20 | alignItems: 'center', 21 | flexDirection: 'column', 22 | padding: 20, 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /phone-app/index.android.js: -------------------------------------------------------------------------------- 1 | import { 2 | AppRegistry, 3 | } from 'react-native'; 4 | 5 | import App from './app/containers/App'; 6 | 7 | AppRegistry.registerComponent('HomeApp', () => App); 8 | -------------------------------------------------------------------------------- /phone-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "HomeApp", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "start": "node node_modules/react-native/local-cli/cli.js start", 6 | "test": "jest" 7 | }, 8 | "dependencies": { 9 | "home-config": "file:../home-config", 10 | "home-records": "file:../home-records", 11 | "immutable": "^3.8.1", 12 | "pure-render-decorator": "^1.2.1", 13 | "react": "15.4.1", 14 | "react-native": "0.39.2", 15 | "react-native-material-kit": "git://github.com/youennPennarun/react-native-material-kit.git#cc9ac25", 16 | "react-native-selectme": "^1.2.3", 17 | "tinycolor2": "^1.4.1" 18 | }, 19 | "devDependencies": { 20 | "babel-jest": "18.0.0", 21 | "babel-preset-react-native": "1.9.1", 22 | "jest": "18.0.0", 23 | "react-test-renderer": "15.4.1" 24 | }, 25 | "jest": { 26 | "preset": "react-native" 27 | }, 28 | "author": "Viktor Kirilov (deepsybg@gmail.com)", 29 | "license": "MIT" 30 | } 31 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist 4 | .tmp 5 | .env 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /server/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Viktor Kirilov 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 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* 4 | * Third party 5 | */ 6 | 7 | const fs = require('fs'); 8 | const WebSocketServer = require('ws').Server; 9 | 10 | /* 11 | * local files 12 | */ 13 | const homeConfig = require('home-config'); 14 | const scheduled = require('./scheduled'); 15 | const Records = require('home-records').Modules; 16 | 17 | const MODULES_DIR = './modules/'; 18 | 19 | 20 | /* 21 | * Load all modules to array 22 | */ 23 | const modules = fs 24 | .readdirSync(MODULES_DIR) 25 | .map(item => require(MODULES_DIR + item)); 26 | 27 | const connections = {}; 28 | 29 | /* 30 | * Broadcast key-value pair to all connections 31 | */ 32 | function broadcast (key, value) { 33 | for (let id in connections) { 34 | connections[id].send( 35 | JSON.stringify({ 36 | key, 37 | value 38 | }) 39 | ); 40 | } 41 | } 42 | 43 | function onNewConnection (socket) { 44 | let isAuthenticated = false; 45 | const id = Math.random(); 46 | connections[id] = socket; 47 | 48 | socket.on('message', (data) => { 49 | try { 50 | data = JSON.parse(data); 51 | } catch (e) { 52 | data = {}; 53 | } 54 | 55 | // disconnect client if the first message is not valid auth token 56 | if (!isAuthenticated && data.auth !== homeConfig.authToken) { 57 | socket.close(); 58 | return; 59 | } 60 | 61 | // toggle auth flag if there is a valid auth token 62 | if (!isAuthenticated && data.auth === homeConfig.authToken) { 63 | isAuthenticated = true; 64 | return; 65 | } 66 | 67 | // pass message to all modules 68 | modules.forEach(module => 69 | module( 70 | data.key, 71 | Records[data.key] ? 72 | new Records[data.key](data.value) : // create record from json 73 | {}, 74 | broadcast 75 | ) 76 | ); 77 | 78 | if (data.key === 'STATUS') { 79 | setTimeout(() => { 80 | broadcast('STATUS'); 81 | }, 250); 82 | } 83 | }); 84 | 85 | socket.on('close', () => { 86 | // remove connection from the broadcast list 87 | delete connections[id]; 88 | }); 89 | } 90 | 91 | 92 | const wss = new WebSocketServer({ 93 | port: 8080 94 | }); 95 | wss.on('connection', onNewConnection); 96 | 97 | scheduled(broadcast); 98 | 99 | console.log("Running at port 8080\n"); 100 | -------------------------------------------------------------------------------- /server/modules/ac.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').execSync; 2 | const ACRecord = require('home-records').Modules.AC; 3 | 4 | const IRSEND_CMD = 'irsend SEND_ONCE MY_REMOTE '; 5 | 6 | let state = new ACRecord(); 7 | 8 | module.exports = function (key, value, broadcast) { 9 | if (key === 'AC') { 10 | let command = `${value.mode}_swingauto_fan${value.fanspeed}_${value.temperature}`; 11 | 12 | if (!value.isEnabled) { 13 | command = 'off'; 14 | } 15 | 16 | exec(IRSEND_CMD + command); 17 | state = value; 18 | } 19 | 20 | if (key === 'STATUS' || key === 'AC') { 21 | broadcast('AC', state); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server/modules/heating.js: -------------------------------------------------------------------------------- 1 | const piBlaster = require('../utils/pi-blaster'); 2 | const HeatingRecord = require('home-records').Modules.Heating; 3 | 4 | const RELAY_GPIO = 6; 5 | const SERVO_GPIO = 18; 6 | 7 | let state = new HeatingRecord(); 8 | 9 | piBlaster.turn(RELAY_GPIO, state.isEnabled); 10 | piBlaster.pwm(SERVO_GPIO, calculatePwm(state.value)); 11 | 12 | function calculatePwm (percentage) { 13 | return (0.5 + percentage / 100 * 1.9) / 10; 14 | } 15 | 16 | module.exports = function (key, value, broadcast) { 17 | if (key === 'Heating') { 18 | piBlaster.turn(RELAY_GPIO, value.isEnabled); 19 | piBlaster.pwm(SERVO_GPIO, calculatePwm(value.value)); 20 | 21 | state = value; 22 | } 23 | 24 | if (key === 'Heating' || key === 'STATUS') { 25 | broadcast('Heating', state); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/modules/led_floor.js: -------------------------------------------------------------------------------- 1 | const piBlaster = require('../utils/pi-blaster'); 2 | const LedFloorRecord = require('home-records').Modules.LedFloor; 3 | 4 | const FLOOR_LED_GPIO = [17, 27, 22]; 5 | const COLOR_LABEL = ['r', 'g', 'b']; 6 | 7 | let state = new LedFloorRecord(); 8 | 9 | module.exports = function (key, value, broadcast) { 10 | if (key === 'LedFloor') { 11 | if (!value.isEnabled) { 12 | COLOR_LABEL.forEach((color, id) => { 13 | piBlaster.pwm(FLOOR_LED_GPIO[id], 0); 14 | }); 15 | } else { 16 | COLOR_LABEL.forEach((color, id) => { 17 | piBlaster.pwm(FLOOR_LED_GPIO[id], value[color] / 100); 18 | }); 19 | } 20 | 21 | state = value; 22 | } 23 | 24 | if (key === 'LedFloor' || key === 'STATUS') { 25 | broadcast('LedFloor', state); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/modules/led_sofa.js: -------------------------------------------------------------------------------- 1 | const piBlaster = require('../utils/pi-blaster'); 2 | const LedSofaRecord = require('home-records').Modules.LedSofa; 3 | 4 | const SOFA_LED_GPIO_PIN = 23; 5 | 6 | let state = new LedSofaRecord(); 7 | 8 | module.exports = function (key, value, broadcast) { 9 | if (key === 'LedSofa') { 10 | if (!value.isEnabled) { 11 | piBlaster.pwm(SOFA_LED_GPIO_PIN, 0); 12 | } else { 13 | piBlaster.pwm(SOFA_LED_GPIO_PIN, value.value / 100); 14 | } 15 | 16 | state = value; 17 | } 18 | 19 | if (key === 'LedSofa' || key === 'STATUS') { 20 | broadcast('LedSofa', state); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "home-automation", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Viktor Kirilov (deepsybg@gmail.com)", 10 | "license": "MIT", 11 | "dependencies": { 12 | "home-config": "file:../home-config", 13 | "home-records": "file:../home-records", 14 | "ws": "1.1.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/scheduled.js: -------------------------------------------------------------------------------- 1 | const getTemperature = require('./utils/temperature'); 2 | const TemperatureRecord = require('home-records').Modules.Temperature; 3 | 4 | module.exports = function ScheduledJobs (broadcast) { 5 | 6 | // broadcast temperature to all connections every second 7 | setInterval(() => { 8 | getTemperature().then((temperature) => { 9 | broadcast('Temperature', new TemperatureRecord({ 10 | temperature 11 | })); 12 | }); 13 | }, 1000); 14 | } 15 | -------------------------------------------------------------------------------- /server/utils/pi-blaster.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').execSync; 2 | 3 | function pwm (pin, value) { 4 | return exec(`echo "${pin}=${parseFloat(value)}" > /dev/pi-blaster`); 5 | }; 6 | 7 | function turn (pin, state) { 8 | const value = state ? 1 : 0; 9 | exec(`gpio -g mode ${pin} out`); 10 | return exec(`gpio -g write ${pin} ${value}`); 11 | } 12 | 13 | module.exports = { 14 | pwm, 15 | turn, 16 | }; 17 | -------------------------------------------------------------------------------- /server/utils/temperature.js: -------------------------------------------------------------------------------- 1 | const exec = require('child_process').execSync; 2 | const fs = require('fs'); 3 | 4 | exec('modprobe w1-gpio'); 5 | exec('modprobe w1-therm'); 6 | 7 | const DEVICES_DIR = '/sys/bus/w1/devices'; 8 | 9 | const files = fs.readdirSync(DEVICES_DIR); 10 | const deviceFolder = files.find(file => /^10/.test(file)); 11 | const deviceFile = DEVICES_DIR + '/' + deviceFolder + '/w1_slave'; 12 | 13 | function getTemperature () { 14 | const readFilePromise = new Promise((resolve, reject) => { 15 | fs.readFile(deviceFile, 'utf8', (err, result) => { 16 | if (err) { 17 | return reject(err); 18 | } 19 | 20 | resolve(result); 21 | }); 22 | }); 23 | 24 | return readFilePromise.then(file => { 25 | const lines = file.split('\n'); 26 | if (lines[0].substr(-3) !== 'YES') { 27 | throw new Error('Temperature device not connected'); 28 | } 29 | 30 | const tempString = lines[1].split('t=')[1].trim(); 31 | 32 | return parseFloat(tempString) / 1000.0; 33 | }); 34 | } 35 | 36 | module.exports = getTemperature; 37 | -------------------------------------------------------------------------------- /server/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "home-config@file:../home-config": 6 | version "1.0.0" 7 | 8 | "home-records@file:../home-records": 9 | version "1.0.0" 10 | dependencies: 11 | immutable "^3.8.1" 12 | 13 | immutable@^3.8.1: 14 | version "3.8.1" 15 | resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2" 16 | 17 | options@>=0.0.5: 18 | version "0.0.6" 19 | resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" 20 | 21 | ultron@1.0.x: 22 | version "1.0.2" 23 | resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" 24 | 25 | ws@1.1.1: 26 | version "1.1.1" 27 | resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.1.tgz#082ddb6c641e85d4bb451f03d52f06eabdb1f018" 28 | dependencies: 29 | options ">=0.0.5" 30 | ultron "1.0.x" 31 | --------------------------------------------------------------------------------