diff --git a/LICENSE b/LICENSE index b20c0a16e..1a7e30ed5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - Project Pacifica + Project Device Simulator Express Copyright (c) Microsoft Corporation. All rights reserved. diff --git a/PRIVACY.md b/PRIVACY.md index 41e1f30ca..c97adaf9a 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -6,7 +6,7 @@ The software may collect information about you and your use of the software and ## Disable Telemetry -The Microsoft Pacifica Extension for Visual Studio Code collects usage +The Microsoft Device Simulator Express Extension for Visual Studio Code collects usage data and sends it to Microsoft to help improve our products and services. Read our [privacy statement](https://privacy.microsoft.com/privacystatement) to diff --git a/README.md b/README.md index a1bd260be..f58a1cde4 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,222 @@ -# Project Pacifica +# Device Simulator Express, a Microsoft Garage project -This project is a VS Code extension for makers to write for their microcontrollers and provide them a simulator to test their code. -This extension currently supports some features of the Adafruit Circuit Playground Express. +Make without limit! Device Simulator Express, a Microsoft Garage project, allows you to code in CircuitPython for your awesome +Circuit Playground Express (CPX) projects! Test and debug your code on the device simulator and see the same +result when you plug in your actual microcontroller. Curious about the output of the device, the serial +monitor allows you to observe the device output. -## Build Status +![CircuitPlayground Express](https://www.microsoft.com/en-us/garage/wp-content/uploads/sites/5/2019/08/cpx.jpg) -| Branch | Build Status | -| :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -| dev | [![Build status](https://microsoftgarage.visualstudio.com/Intern%20GitHub/_apis/build/status/Adafruit/Intern%20GitHub-CI?branchName=dev)](https://microsoftgarage.visualstudio.com/Intern%20GitHub/_build/latest?definitionId=304&branchName=dev) | -| staging | [![Build Status](https://microsoftgarage.visualstudio.com/Intern%20GitHub/_apis/build/status/Adafruit/Intern%20GitHub-CI?branchName=staging)](https://microsoftgarage.visualstudio.com/Intern%20GitHub/_build/latest?definitionId=304&branchName=staging) | -| master | [![Build Status](https://microsoftgarage.visualstudio.com/Intern%20GitHub/_apis/build/status/Adafruit/Intern%20GitHub-CI?branchName=master)](https://microsoftgarage.visualstudio.com/Intern%20GitHub/_build/latest?definitionId=304&branchName=master) | +## Features -## Code of conduct +- IntelliSense and syntax highlighting for CircuitPython code (only supports CPX Express library) +- Template file generation +- Integrated Python Debugging for the Simulator +- Serial monitor (available on Windows and Mac only) +- Output panel for the simulator +- Deploy CircuitPython code to the physical device. +- Simulation of the Adafruit Circuit Playground Express device, including: + - Green LED + - Red LED + - Push Buttons A and B + - Slider Switch + - Speaker: Play .wav file + - 10 NeoPixels + - Light sensor + - Motion sensors + - Acceleration detection + - Device shake detection + - Temperature sensor + - 7 Capacitive Touch sensors -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +The simulator supports most of the sensors on CPX except **IR transmitter & Receiver**, **Sound Sensor (microphone)**, **Speaker (Play Tone)** and the **“tap” on Motion Sensor**. +The code related to these sensors can still run on the actual CPX board and be deployed using Device Simulator Express. +As we only support CPX library now, other libraries (i.e. simpleio) can’t run on the simulator. But they will work on the actual device! -## Documentation +## Prerequisites -- [Installation instructions](/docs/install.md) -- [How to use the Extension](/docs/how-to-use.md) -- [Setup for developers](/docs/developers-setup.md) -- [Contributing](CONTRIBUTING.md) +The following dependencies are required to install before launching Device Simulator Express. +You will be prompted to install the Python dependencies during the first use. + +- _**[Visual Studio Code](https://code.visualstudio.com/)**_ +- _**[Node](https://nodejs.org/en/download/)**_ +- _**[Python 3.7.4](https://www.python.org/downloads/)**_: Make sure you've added python and pip to your PATH in your environment variables. (1) +- _**[Python VS Code extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python)**_: This will be installed automatically from the marketplace when you install Device Simulator Express. + +The following dependecies can be installed for you by the extension by clicking yes when you are prompted to (**except** `pywin32` which is needed only on Windows platform). (2) + +- _**Playsound**_ + install by typing the following commands in a console: `pip install playsound` + +- _**Pywin 32**_ + install by typing the following commands in a console (only for Windows computers, you must run it manually): `pip install pywin32` +- _**Python-Socketio**_ + install by typing the following commands in a console: `pip install python-socketio` +- _**Requests**_ + install by typing the following commands in a console: `pip install requests` +- _**Application Insights**_ + install by typing the following commands in a console: `pip install applicationinsights` + +## Useful Links + +- Tutorials and Example Code for Adafruit CPX: + - Adafruit CPX library tutorial: (https://learn.adafruit.com/circuitpython-made-easy-on-circuit-playground-express/circuit-playground-express-library) + - Adafruit CPX Examples on GitHub: (https://github.com/adafruit/Adafruit_CircuitPython_CircuitPlayground/tree/master/examples) + - Adafruit CPX Guided Tour (Intro for the Hardware) (https://learn.adafruit.com/adafruit-circuit-playground-express/guided-tour) +- Format Adafruit CPX device: + - Tutorial for formatting Adafruit CPX for CircuitPython (https://learn.adafruit.com/welcome-to-circuitpython/installing-circuitpython) + - Download Firmware .uf2 file (https://learn.adafruit.com/adafruit-circuit-playground-express/circuitpython-quickstart) + - Download the latest version of the Adafruit CPX library (link: https://learn.adafruit.com/welcome-to-circuitpython/circuitpython-libraries) + +## How to use + +To use Device Simulator Express, install the extension from the marketplace and reload VS Code. + +### 1. Start with the “New File” Command. + +1. Type in Device Simulator Express: New File” in the command palette(`CTRL+SHIFT+P`to open the command palette). + !["New File" animation](https://www.microsoft.com/en-us/garage/wp-content/uploads/sites/5/2019/08/newFile.gif) +2. Name and save your file somewhere, and we’re good to go!(3) +3. Start with some examples: you can find examples files and tutorials inside the comments, + as well as in the notification pop up when you run the `“Device Simulator Express: New File”` Command. + +![How to find example code screenshot](https://www.microsoft.com/en-us/garage/wp-content/uploads/sites/5/2019/08/findExamples.jpg) + +### 2. Start from an existing python file. + +1. Open the folder or your .py file in Visual Studio Code. +2. Run `open Simulator` from the command palette or icon in the editor toolbar. + +### 3. Run your code on the simulator . + +![How to run the simulator animation](https://www.microsoft.com/en-us/garage/wp-content/uploads/sites/5/2019/08/run.gif) + +- Run `Run Simulator` from the command palette or icon in the editor toolbar. +- You can use the `Play` or `Refresh` button on the simulator webview. + +### 4. Deploy your code to the physical device + +Before deploying the python code to your CPX device, you need to format your device following these tutorials: + +1. Download the firmware with the .uf2 file (link: https://learn.adafruit.com/adafruit-circuit-playground-express/circuitpython-quickstart) +2. Download the lastest version of the cpx library (link: https://learn.adafruit.com/welcome-to-circuitpython/circuitpython-libraries). + **_Note:_** Make sure you name your file main.py or code.py: the device automatically runs the first file that is likely named. + +!["Deploy to Device" example](https://www.microsoft.com/en-us/garage/wp-content/uploads/sites/5/2019/08/deployToBoard.png) + +### 5. Use the Serial Monitor for your Adafruit CPX device(available Windows and Mac only) + +1. Plug in your CPX device (make sure it’s formatted properly already) +2. Run the command `Device Simulator Express: Open Serial Monitor` +3. Select your baud rate for the serial port +4. The print() statements in your code will show in the output console + +### 6. Use the sensors in the Device Simulator Express + +Generating input for the sensors can be done by interacting directly with device on the webview +or by using the toolbar. + +- **Switch, push buttons and capacitive touch:** click directly on the corresponding element on the device or use the keybindings. +- **Temperature sensor, Light sensor, acceleration:** click on the corresponding button in the toolbar and change the value using the slider or the input box attached to it. +- **Shake detection:** go to the motion sensor section in the toolbar and click on the shake button. + +### 7. Debug your project on the simulator + +1. Add breakpoints in your code +2. Press F5 to enter the debugging mode, and you can start debugging line by line! + +## Commands + +Device Simulator Express provides several commands in the Command Palette (F1 or Ctrl + Shift + P/ Cmd + Shift + P for Mac OS) for working with \*.py files: + +- `Device Simulator Express: New File`: Opens an unsaved .py file with template code, also open the simulator. +- `Device Simulator Express: Open Simulator`: Opens the simulator in the webView +- `Device Simulator Express: Run on Simulator`: Runs python code on the simulator +- `Device Simulator Express: Deploy to Board`: Copies & Pastes the code.py or main.py file to CIRCUITPY drive if detected a CPX is plugged in +- `Device Simulator Express: Open Serial Monitor`: Opens the serial monitor in the integrated output window. +- `Device Simulator Express: Close Serial Monitor`: Stops the serial monitor and releases the serial port. +- `Device Simulator Express: Change Baud Rate`: Changes the baud rate of the selected serial port. For Adafruit CPX, the default baud rate is 115200. +- `Device Simulator Express: Select Serial Port`: Changes the current serial port. + +## Keybindings + +In Device Simulator Express, you can use keyboard to interact with the device: + +- Push Button `A & B: A B` +- Capacitive Touch Sensor `A1 – A7: SHIFT + 1~7` +- Slider Switch: `SHIFT + S` +- Refresh the simulator: `SHIFT + R` + +## Provide feedback + +To report issues, provide feedback or requests, please use this link: [Provide Feedback](https://aka.ms/AA5xpxx). +We would love to hear from you about your experience to keep improving our project. ## Privacy and Telemetry Notice -- [Data collection](PRIVACY.md) +### Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft's privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. + +### Disable Telemetry + +The Microsoft Device Simulator Express Extension for Visual Studio Code collects usage +data and sends it to Microsoft to help improve our products and +services. Read our +[privacy statement](https://privacy.microsoft.com/privacystatement) to +learn more. This extension respects the `telemetry.enableTelemetry` +setting which you can learn more about at +https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting. + +To disable telemetry, follow these steps: +1) Open **File** (Open **Code** on macOS) +2) Select **Preferences** +3) Select **Settings** +4) Search for `telemetry` +5) Uncheck the **Telemetry: Enable Telemetry** setting ## Third Party Notice -- [Third Party Notice](ThirdPartyNotices.txt) +A `ThirdPartyNotices.txt` file is provided in the extension's source code listing the appropriate third-party notices. + +## Troubleshooting Tips + +- The first time you install the extension, you'll need to execute the `run` command at least once in order to access auto-completion. +- While running a code file, if you get an error saying it can't find the file, make sure you've clicked on a valid Python code file before running it. +- To open the output panel again after closing it go to VS Code menu: `View->Output`. +- If you have pylint enabled, it might underline the import of the adafruit_circuitplayground library, but it will work correctly. +- If you try to deploy to the device while it's plugged in but you still get an error saying it cannot find the board, make sure your Circuit Playground Express is formatted correctly and that its name matches `CIRCUITPY`. +- If you can't get the Simulator communication working while debugging, try to open your `Settings` and check the port used under `'Device Simulator Express: Debugger Server Port'`. You can either change it (usually ports above 5000 should work) or try to free it, then start debugging again. +- When you are using the serial monitor, if you get some unusual error messages, unplug the device and reload the VS Code windows. + +## License + + Device Simulator Express, a Microsoft Garage project + + Copyright (c) Microsoft Corporation. All rights reserved. + + MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +## Notes + +(1) Note: the easiest way to do it is to select the "Add to PATH" option directly when you install Python. Otherwise you can search how to insert it manually, but make sure that when you type _python_ in a terminal, the command is recognized and have the correct version. +(2) You can chose to see to see the prompt or not by changing the extension configirations. +(3) To be able to run the file from your physical device, it should either be named code.py or main.py. diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 3ba03826d..e1c8043aa 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -1,60 +1,3688 @@ +NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. +Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, +or you may send a check or money order for US $5.00, including the product name, +the open source component name, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent +required to debug changes to any libraries licensed under the GNU Lesser General Public License. + + +------------------------------------------------------------------- + +detect-node 2.0.4 - ISC +https://github.com/iliakan/detect-node +Copyright (c) 2017 Ilya Kantor + +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +inherits 2.0.3 - ISC +https://github.com/isaacs/inherits#readme +Copyright (c) Isaac Z. Schlueter + +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +semver 5.7.0 - ISC +https://github.com/npm/node-semver#readme +Copyright Isaac Z. +Copyright Isaac Z. Schlueter +Copyright (c) Isaac Z. Schlueter and Contributors + +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +setprototypeof 1.1.0 - ISC +https://github.com/wesleytodd/setprototypeof +Copyright (c) 2015, Wes Todd + +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + + + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +@types/node 10.14.7 - MIT +Copyright (c) Microsoft Corporation. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +@types/open 6.1.0 - MIT + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +@types/socket.io 2.1.2 - MIT +Copyright (c) Microsoft Corporation. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +accepts 1.3.7 - MIT +https://github.com/jshttp/accepts#readme +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +after 0.8.2 - MIT +https://github.com/Raynos/after#readme +Copyright (c) 2011 Raynos. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +applicationinsights 1.0.8 - MIT +https://github.com/Microsoft/ApplicationInsights-node.js#readme +Copyright (c) Microsoft Corporation. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +arraybuffer.slice 0.0.7 - MIT +https://github.com/rase-/arraybuffer.slice +Copyright (c) 2013 Rase + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +async 1.5.2 - MIT +https://github.com/caolan/async#readme +Copyright 2010-2014 Caolan McMahon +Copyright (c) 2010-2014 Caolan McMahon + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +async-limiter 1.0.0 - MIT +https://github.com/strml/async-limiter#readme +Copyright (c) 2017 Samuel Reed + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +backo2 1.0.2 - MIT +https://github.com/mokesmokes/backo + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +base64-arraybuffer 0.1.5 - MIT +https://github.com/niklasvh/base64-arraybuffer +Copyright (c) 2012 Niklas von Hertzen + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +base64id 1.0.0 - MIT +https://github.com/faeldt/base64id#readme +Copyright (c) 2012-2016 Kristian Faeldt + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +batch 0.6.1 - MIT +https://github.com/visionmedia/batch#readme +Copyright (c) 2013 TJ Holowaychuk + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +better-assert 1.0.2 - MIT +https://github.com/visionmedia/better-assert +Copyright (c) 2012 TJ Holowaychuk + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +blob 0.0.5 - MIT +https://github.com/webmodules/blob +Copyright (c) 2014 Rase + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +callsite 1.0.0 - MIT + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +compare-versions 3.5.1 - MIT +https://github.com/omichelsen/compare-versions#readme +Copyright (c) 2015-2017 Ole Michelsen + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +component-bind 1.0.0 - MIT +https://github.com/component/bind + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +component-emitter 1.2.1 - MIT +https://github.com/component/emitter#readme +Copyright (c) 2014 Component + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +component-inherit 0.0.3 - MIT +https://github.com/component/inherit + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +cookie 0.3.1 - MIT +https://github.com/jshttp/cookie +Copyright (c) 2012-2014 Roman Shtylman +Copyright (c) 2015 Douglas Christopher Wilson +Copyright (c) 2012-2014 Roman Shtylman +Copyright (c) 2015 Douglas Christopher Wilson + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +debug 3.1.0 - MIT +https://github.com/visionmedia/debug#readme +Copyright (c) 2014 TJ Holowaychuk +Copyright (c) 2014-2017 TJ Holowaychuk + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +debug 4.1.1 - MIT +https://github.com/visionmedia/debug#readme +Copyright (c) 2014 TJ Holowaychuk +Copyright (c) 2014-2017 TJ Holowaychuk + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +define-properties 1.1.3 - MIT +https://github.com/ljharb/define-properties#readme +Copyright (c) 2015 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +diagnostic-channel 0.2.0 - MIT +https://github.com/Microsoft/node-diagnostic-channel +Copyright (c) Microsoft Corporation. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +diagnostic-channel-publishers 0.2.1 - MIT +https://github.com/Microsoft/node-diagnostic-channel +Copyright (c) Microsoft Corporation. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +engine.io 3.3.2 - MIT +https://github.com/socketio/engine.io +Copyright (c) 2014 Guillermo Rauch + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +engine.io-client 3.3.2 - MIT +https://github.com/socketio/engine.io-client +Copyright (c) 2014 Automattic, Inc. +Copyright (c) 2012 Niklas von Hertzen +Copyright (c) 2014-2015 Automattic + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +engine.io-parser 2.1.3 - MIT +https://github.com/socketio/engine.io-parser +Copyright (c) 2016 Guillermo Rauch + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +es-abstract 1.13.0 - MIT +https://github.com/ljharb/es-abstract#readme +Copyright (c) 2015 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +es-to-primitive 1.2.0 - MIT +https://github.com/ljharb/es-to-primitive#readme +Copyright (c) 2015 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +eventemitter2 5.0.1 - MIT +https://github.com/hij1nx/EventEmitter2#readme +Copyright (c) 2013 +Copyright (c) 2016 Paolo Fragomeni and Contributors + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +eventsource 1.0.7 - MIT +http://github.com/EventSource/eventsource +Copyright (c) EventSource GitHub +Copyright Joyent, Inc. and other Node contributors. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +faye-websocket 0.11.1 - MIT +https://github.com/faye/faye-websocket-node +Copyright (c) 2010-2017 James Coglan + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +faye-websocket 0.10.0 - MIT +http://github.com/faye/faye-websocket-node +Copyright (c) 2010-2015 James Coglan + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +function-bind 1.1.1 - MIT +https://github.com/Raynos/function-bind +Copyright (c) 2013 Raynos. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +handle-thing 2.0.0 - MIT +https://github.com/spdy-http2/handle-thing#readme +Copyright Fedor Indutny, 2015. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +has 1.0.3 - MIT +https://github.com/tarruda/has +Copyright (c) 2013 Thiago de Arruda + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +has-binary2 1.0.3 - MIT +Copyright (c) 2014 Kevin Roark + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +has-cors 1.1.0 - MIT +https://github.com/component/has-cors + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +has-symbols 1.0.0 - MIT +https://github.com/ljharb/has-symbols#readme +Copyright (c) 2016 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +hpack.js 2.1.6 - MIT +https://github.com/indutny/hpack.js#readme +Copyright Fedor Indutny, 2015. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +http-deceiver 1.2.7 - MIT +https://github.com/indutny/http-deceiver#readme +Copyright Fedor Indutny, 2015. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +http-errors 1.6.3 - MIT +https://github.com/jshttp/http-errors#readme +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2016 Douglas Christopher Wilson +Copyright (c) 2014 Jonathan Ong me@jongleberry.com +Copyright (c) 2016 Douglas Christopher Wilson doug@somethingdoug.com + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +http-parser-js 0.5.0 - MIT +https://github.com/creationix/http-parser-js#readme +Copyright Joyent, Inc. and other Node contributors. +Copyright (c) 2015 Tim Caswell (https://github.com/creationix) and other contributors. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +indexof 0.0.1 - MIT + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +is-arguments 1.0.4 - MIT +https://github.com/ljharb/is-arguments +Copyright (c) 2014 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +is-callable 1.1.4 - MIT +https://github.com/ljharb/is-callable#readme +Copyright (c) 2015 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +is-date-object 1.0.1 - MIT +https://github.com/ljharb/is-date-object#readme +Copyright (c) 2015 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +is-generator-function 1.0.7 - MIT +https://github.com/ljharb/is-generator-function#readme +Copyright (c) 2014 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +is-regex 1.0.4 - MIT +https://github.com/ljharb/is-regex +Copyright (c) 2014 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +is-symbol 1.0.2 - MIT +https://github.com/ljharb/is-symbol#readme +Copyright (c) 2015 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +is-wsl 1.1.0 - MIT +https://github.com/sindresorhus/is-wsl#readme +(c) Sindre Sorhus (https://sindresorhus.com) +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +isarray 2.0.1 - MIT +https://github.com/juliangruber/isarray +Copyright (c) 2013 Julian Gruber + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +js-tokens 4.0.0 - MIT +https://github.com/lydell/js-tokens#readme +Copyright 2014, 2015, 2016, 2017, 2018 Simon Lydell +Copyright (c) 2014, 2015, 2016, 2017, 2018 Simon Lydell + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +json3 3.3.3 - MIT +https://bestiejs.github.io/json3 +Copyright (c) 2013-2015 Benjamin Tan. https://d10.github.io +Copyright (c) 2012-2015 Kit Cambridge. http://kitcambridge.be +Copyright 2012-2015, Kit Cambridge, Benjamin Tan http://kit.mit-license.org +(c) 2012-2015 http://kitcambridge.be/' Kit Cambridge, https://d10.github.io/' Benjamin Tan + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +loose-envify 1.4.0 - MIT +https://github.com/zertosh/loose-envify +Copyright (c) 2015 Andres Suarez + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +mime-db 1.40.0 - MIT +https://github.com/jshttp/mime-db#readme +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +mime-types 2.1.24 - MIT +https://github.com/jshttp/mime-types#readme +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +ms 2.1.1 - MIT +https://github.com/zeit/ms#readme +Copyright (c) 2016 Zeit, Inc. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +ms 2.0.0 - MIT +https://github.com/zeit/ms#readme +Copyright (c) 2016 Zeit, Inc. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +negotiator 0.6.2 - MIT +https://github.com/jshttp/negotiator#readme +Copyright (c) 2012 Federico Romero +Copyright (c) 2014 Federico Romero +Copyright (c) 2012 Isaac Z. Schlueter +Copyright (c) 2012-2014 Federico Romero +Copyright (c) 2012-2014 Isaac Z. Schlueter +Copyright (c) 2015 Douglas Christopher Wilson +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +object-assign 4.1.1 - MIT +https://github.com/sindresorhus/object-assign#readme +(c) Sindre Sorhus +(c) Sindre Sorhus (https://sindresorhus.com) +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +object-component 0.0.3 - MIT + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +object-keys 1.1.1 - MIT +https://github.com/ljharb/object-keys#readme +Copyright (c) 2013 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +object.entries 1.1.0 - MIT +https://github.com/es-shims/Object.entries#readme +Copyright (c) 2015 Jordan Harband + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +obuf 1.1.2 - MIT +https://github.com/indutny/offset-buffer +Copyright Fedor Indutny, 2015. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +open 6.4.0 - MIT +https://github.com/sindresorhus/open#readme +Copyright 2006, Kevin Krammer +Copyright 2006, Jeremy White +Copyright 2009-2010, Fathi Boudra +Copyright 2009-2010, Rex Dieter +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +original 1.0.2 - MIT +https://github.com/unshiftio/original#readme +Copyright (c) 2015 Unshift.io, Arnout Kazemier + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +os 0.1.1 - MIT +https://github.com/DiegoRBaquero/node-os#readme +Copyright (c) 2016 Diego Rodriguez Baquero +Copyright (c) Diego Rodriguez Baquero (https://diegorbaquero.com). + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +parseqs 0.0.5 - MIT +https://github.com/get/querystring +Copyright (c) 2015 Gal Koren + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +parseuri 0.0.5 - MIT +https://github.com/get/parseuri +Copyright (c) 2014 Gal Koren + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +pkg-up 2.0.0 - MIT +https://github.com/sindresorhus/pkg-up#readme +(c) Sindre Sorhus (https://sindresorhus.com) +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +portfinder 1.0.20 - MIT +https://github.com/indexzero/node-portfinder#readme +(c) 2011, Charlie Robbins +Copyright (c) 2012 Charlie Robbins + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +prop-types 15.7.2 - MIT +https://facebook.github.io/react/ +(c) Sindre Sorhus +Copyright (c) 2013-present, Facebook, Inc. +Copyright (c) Facebook, Inc. and its affiliates. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +querystringify 2.1.1 - MIT +https://github.com/unshiftio/querystringify +Copyright (c) 2015 Unshift.io, Arnout Kazemier + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +react 16.8.6 - MIT +https://reactjs.org/ +(c) Sindre Sorhus +Copyright (c) 2013-present, Facebook, Inc. +Copyright (c) Facebook, Inc. and its affiliates. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +react-dom 16.8.6 - MIT +https://reactjs.org/ +Copyright (c) 2013-present, Facebook, Inc. +Copyright (c) Facebook, Inc. and its affiliates. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +react-error-overlay 5.1.6 - MIT +https://github.com/facebook/create-react-app#readme +(c) 2019 Denis Pushkarev +Copyright (c) 2013-present, Facebook, Inc. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +react-is 16.8.6 - MIT +https://reactjs.org/ +Copyright (c) Facebook, Inc. and its affiliates. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +recursive-readdir 2.2.2 - MIT +https://github.com/jergason/recursive-readdir#readme + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +safe-buffer 5.1.2 - MIT +https://github.com/feross/safe-buffer +Copyright (c) Feross Aboukhadijeh +Copyright (c) Feross Aboukhadijeh (http://feross.org) + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +scheduler 0.13.6 - MIT +https://reactjs.org/ +Copyright (c) Facebook, Inc. and its affiliates. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +select-hose 2.0.0 - MIT +https://github.com/indutny/select-hose#readme +Copyright Fedor Indutny, 2015. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +selfsigned 1.10.4 - MIT +https://github.com/jfromaniello/selfsigned#readme +Copyright (c) 2013 Jose F. Romaniello + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +serve-index 1.9.1 - MIT +https://github.com/expressjs/serve-index#readme +Copyright (c) 2011 LearnBoost +Copyright (c) 2010 Sencha Inc. +Copyright (c) 2011 Sencha Inc. +Copyright (c) 2011 TJ Holowaychuk +Copyright (c) 2014-2015 Douglas Christopher Wilson + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +socket.io 2.2.0 - MIT +https://github.com/socketio/socket.io#readme +Copyright (c) 2014-2018 Automattic + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +socket.io-adapter 1.1.1 - MIT +https://github.com/socketio/socket.io-adapter#readme +Copyright (c) 2014 Guillermo Rauch + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +socket.io-client 2.2.0 - MIT +https://github.com/Automattic/socket.io-client#readme +(c) 2014-2018 Guillermo Rauch +Copyright (c) 2014 Guillermo Rauch +Copyright (c) 2012 Niklas von Hertzen + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +socket.io-parser 3.3.0 - MIT +https://github.com/Automattic/socket.io-parser#readme +Copyright (c) 2014 Guillermo Rauch + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +sockjs 0.3.19 - MIT +https://github.com/sockjs/sockjs-node +Copyright (c) 2011 VMware, Inc. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +sockjs-client 1.3.0 - MIT +http://sockjs.org +Copyright (c) 2011-2012 VMware, Inc. +Copyright 2012-2014, Kit Cambridge http://kit.mit-license.org + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +spdy 4.0.0 - MIT +https://github.com/indutny/node-spdy +Copyright Fedor Indutny, 2015. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +spdy-transport 3.0.0 - MIT +https://github.com/spdy-http2/spdy-transport +Copyright Fedor Indutny, 2015. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +svg-inline-react 3.1.0 - MIT +https://github.com/sairion/svg-inline-react +Copyright 2015-2017 Jaeho Lee + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +to-array 0.1.4 - MIT +https://github.com/Raynos/to-array +Copyright (c) 2012 Raynos. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +url-parse 1.4.7 - MIT +https://github.com/unshiftio/url-parse#readme +Copyright (c) 2015 Unshift.io, Arnout Kazemier + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +util 0.12.1 - MIT +https://github.com/browserify/node-util +Copyright Joyent, Inc. and other Node contributors. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +vscode-extension-telemetry 0.1.1 - MIT +https://github.com/Microsoft/vscode-extension-telemetry#readme +Copyright (c) Microsoft Corporation. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +vscode-nls 4.1.1 - MIT +https://github.com/Microsoft/vscode-nls#readme +Copyright (c) Microsoft Corporation. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +wbuf 1.7.3 - MIT +https://github.com/indutny/wbuf +Copyright Fedor Indutny, 2014. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +webpack-dev-middleware 3.7.0 - MIT +https://github.com/webpack/webpack-dev-middleware +Copyright JS Foundation and other contributors + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +webpack-log 2.0.0 - MIT +https://github.com/webpack-contrib/webpack-log#readme +Copyright (c) 2017 + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +websocket-driver 0.7.0 - MIT +https://github.com/faye/websocket-driver-node +Copyright (c) 2010-2017 James Coglan + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +websocket-extensions 0.1.3 - MIT +http://github.com/faye/websocket-extensions-node +Copyright (c) 2014-2017 James Coglan + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- -THIRD-PARTY SOFTWARE NOTICES AND INFORMATION -Do Not Translate or Localize +------------------------------------------------------------------- + +ws 6.1.4 - MIT +https://github.com/websockets/ws +Copyright (c) 2011 Einar Otto Stangvik + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +xmlhttprequest-ssl 1.5.5 - MIT +https://github.com/mjwwit/node-XMLHttpRequest#readme +Copyright (c) 2010 passive.ly LLC + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +yeast 0.1.2 - MIT +https://github.com/unshiftio/yeast +Copyright (c) 2015 Unshift.io, Arnout Kazemier + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +zone.js 0.7.6 - MIT +https://github.com/angular/zone.js#readme +Copyright Google Inc. +Copyright (c) 2016 Google, Inc. + +Copyright (c) 2012-2015 Kit Cambridge. +http://kitcambridge.be/ + +Copyright (c) 2013-2015 Benjamin Tan. +https://d10.github.io/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: -This project incorporates components from the projects listed below. The original copyright notices and the licenses under which Microsoft received such components are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. - - -1. Files from the Python Project (https://www.python.org/) -2. Python extension for Visual Studio Code (https://github.com/microsoft/vscode-python) -3. Python Language Server (https://github.com/Microsoft/python-language-server) -4. Visual Studio Code (https://github.com/microsoft/vscode) -5. TypeScript (https://github.com/microsoft/TypeScript/) -6. VS Code NLS (https://github.com/Microsoft/vscode-nls) -7. VS Code extension telemetry (https://github.com/Microsoft/vscode-extension-telemetry) -8. React (https://github.com/facebook/react) -9. Webpack (https://github.com/webpack/webpack) -10. Node.js (https://github.com/nodejs/node) -11. Gulp (https://github.com/gulpjs/gulp) -12. Microsoft MakeCode (https://github.com/microsoft/pxt) -13. MakeCode for Adafruit Circuit Playground Express (https://github.com/microsoft/pxt-adafruit) -14. Python for Win32 (https://github.com/mhammond/pywin32) -15. Playsound (https://github.com/TaylorSMarks/playsound) -16. pytest (https://docs.pytest.org/en/latest/) - - -%% Files from the Python Project NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= -PYTHON SOFTWARE FOUNDATION LICENSE LICENSE AGREEMENT FOR PYTHON 3.7.4rc2 --------------------------------------------- +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +Additional - + +Python 3.7.4 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using Python - 3.7.4rc2 software in source or binary form and its associated documentation. + 3.7.4 software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, - distribute, and otherwise use Python 3.7.4rc2 alone or in any derivative + distribute, and otherwise use Python 3.7.4 alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2019 Python Software Foundation; All Rights - Reserved" are retained in Python 3.7.4rc2 alone or in any derivative version + Reserved" are retained in Python 3.7.4 alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or - incorporates Python 3.7.4rc2 or any part thereof, and wants to make the + incorporates Python 3.7.4 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python - 3.7.4rc2. + 3.7.4. -4. PSF is making Python 3.7.4rc2 available to Licensee on an "AS IS" basis. +4. PSF is making Python 3.7.4 available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE - USE OF PYTHON 3.7.4rc2 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + USE OF PYTHON 3.7.4 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7.4rc2 +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7.4 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF - MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7.4rc2, OR ANY DERIVATIVE + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7.4, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of @@ -66,156 +3694,11 @@ PYTHON SOFTWARE FOUNDATION LICENSE LICENSE AGREEMENT FOR PYTHON 3.7.4rc2 trademark sense to endorse or promote products or services of Licensee, or any third party. -8. By copying, installing or otherwise using Python 3.7.4rc2, Licensee agrees +8. By copying, installing or otherwise using Python 3.7.4, Licensee agrees to be bound by the terms and conditions of this License Agreement. -BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 -------------------------------------------- - -BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 - -1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an -office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the -Individual or Organization ("Licensee") accessing and otherwise using -this software in source or binary form and its associated -documentation ("the Software"). - -2. Subject to the terms and conditions of this BeOpen Python License -Agreement, BeOpen hereby grants Licensee a non-exclusive, -royalty-free, world-wide license to reproduce, analyze, test, perform -and/or display publicly, prepare derivative works, distribute, and -otherwise use the Software alone or in any derivative version, -provided, however, that the BeOpen Python License is retained in the -Software, alone or in any derivative version prepared by Licensee. - -3. BeOpen is making the Software available to Licensee on an "AS IS" -basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE -SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS -AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY -DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -5. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -6. This License Agreement shall be governed by and interpreted in all -respects by the law of the State of California, excluding conflict of -law provisions. Nothing in this License Agreement shall be deemed to -create any relationship of agency, partnership, or joint venture -between BeOpen and Licensee. This License Agreement does not grant -permission to use BeOpen trademarks or trade names in a trademark -sense to endorse or promote products or services of Licensee, or any -third party. As an exception, the "BeOpen Python" logos available at -http://www.pythonlabs.com/logos.html may be used according to the -permissions granted on that web page. - -7. By copying, installing or otherwise using the software, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 ---------------------------------------- - -1. This LICENSE AGREEMENT is between the Corporation for National -Research Initiatives, having an office at 1895 Preston White Drive, -Reston, VA 20191 ("CNRI"), and the Individual or Organization -("Licensee") accessing and otherwise using Python 1.6.1 software in -source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, CNRI -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python 1.6.1 -alone or in any derivative version, provided, however, that CNRI's -License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) -1995-2001 Corporation for National Research Initiatives; All Rights -Reserved" are retained in Python 1.6.1 alone or in any derivative -version prepared by Licensee. Alternately, in lieu of CNRI's License -Agreement, Licensee may substitute the following text (omitting the -quotes): "Python 1.6.1 is made available subject to the terms and -conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the Internet using the following -unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the Internet -using the following URL: http://hdl.handle.net/1895.22/1013". - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python 1.6.1 or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python 1.6.1. - -4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" -basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. This License Agreement shall be governed by the federal -intellectual property law of the United States, including without -limitation the federal copyright law, and, to the extent such -U.S. federal law does not apply, by the law of the Commonwealth of -Virginia, excluding Virginia's conflict of law provisions. -Notwithstanding the foregoing, with regard to derivative works based -on Python 1.6.1 that incorporate non-separable material that was -previously distributed under the GNU General Public License (GPL), the -law of the Commonwealth of Virginia shall govern this License -Agreement only as to issues arising under or with respect to -Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this -License Agreement shall be deemed to create any relationship of -agency, partnership, or joint venture between CNRI and Licensee. This -License Agreement does not grant permission to use CNRI trademarks or -trade name in a trademark sense to endorse or promote products or -services of Licensee, or any third party. - -8. By clicking on the "ACCEPT" button where indicated, or by copying, -installing or otherwise using Python 1.6.1, Licensee agrees to be -bound by the terms and conditions of this License Agreement. - - -CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 --------------------------------------------------- - -Copyright (C) 2006-2010 Python Software Foundation - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the name of Stichting Mathematisch -Centrum or CWI not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE -FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -============================================= -END OF Files from the Python Project NOTICES, INFORMATION, AND LICENSE - - -%% Python extension for Visual Studio Code NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= +Python extension for Visual Studio Code Copyright (c) Microsoft Corporation. All rights reserved. MIT License @@ -237,12 +3720,9 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -============================================= -END OF Python extension for Visual Studio Code NOTICES, INFORMATION, AND LICENSE -%% Microsoft Python Language Server NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= +Microsoft Python Language Server Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -444,12 +3924,9 @@ Apache License WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. -============================================= -END OF Microsoft Python Language Server NOTICES, INFORMATION, AND LICENSE -%% Visual Studio Code NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= +Visual Studio Code MIT License Copyright (c) 2015 - present Microsoft Corporation @@ -473,12 +3950,9 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -============================================= -END OF Visual Studio Code NOTICES, INFORMATION, AND LICENSE -%% TypeScript NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= +TypeScript Apache License Version 2.0, January 2004 @@ -534,117 +4008,358 @@ If the Work includes a "NOTICE" text file as part of its distribution, then any 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS -============================================= -END OF TypeScript NOTICES, INFORMATION, AND LICENSE - - -%% VS Code NLS NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= -The MIT License (MIT) - -Copyright (c) Microsoft Corporation - -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, -modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT -OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -============================================= -END OF VS Code NLS NOTICES, INFORMATION, AND LICENSE - - -%% VS Code extension telemetry NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= -vscode-extension-telemetry - -The MIT License (MIT) - -Copyright (c) Microsoft Corporation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -============================================= -END OF VS Code extension telemetry NOTICES, INFORMATION, AND LICENSE - - -%% React NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= -MIT License - -Copyright (c) Facebook, Inc. and its affiliates. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -============================================= -END OF React NOTICES, INFORMATION, AND LICENSE - - -%% Webpack NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= -Copyright JS Foundation and other contributors - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -============================================= -END OF Webpack NOTICES, INFORMATION, AND LICENSE - - -%% Node.js NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= +TypeScript is authored by: +* Aaron Holmes +* Abubaker Bashir +* Adam Freidin +* Adi Dahiya +* Aditya Daflapurkar +* Adnan Chowdhury +* Adrian Leonhard +* Adrien Gibrat +* Ahmad Farid +* Akshar Patel +* Alan Agius +* Alex Chugaev +* Alex Eagle +* Alex Khomchenko +* Alex Ryan +* Alexander Kuvaev +* Alexander Rusakov +* Alexander Tarasyuk +* Ali Sabzevari +* Aliaksandr Radzivanovich +* Aluan Haddad +* Anatoly Ressin +* Anders Hejlsberg +* Andreas Martin +* Andrej Baran +* Andrew Casey +* Andrew Faulkner +* Andrew Ochsner +* Andrew Stegmaier +* Andrew Z Allen +* András Parditka +* Andy Hanson +* Anil Anar +* Anton Khlynovskiy +* Anton Tolmachev +* Anubha Mathur +* Armando Aguirre +* Arnaud Tournier +* Arnav Singh +* Artem Tyurin +* Arthur Ozga +* Asad Saeeduddin +* Avery Morin +* Basarat Ali Syed +* @begincalendar +* Ben Duffield +* Ben Mosher +* Benjamin Bock +* Benjamin Lichtman +* Benny Neugebauer +* Bill Ticehurst +* Blaine Bublitz +* Blake Embrey +* @bluelovers +* @bootstraponline +* Bowden Kelly +* Bowden Kenny +* Brandon Slade +* Brett Mayen +* Bryan Forbes +* Caitlin Potter +* Cameron Taggart +* @cedvdb +* Charles Pierce +* Charly POLY +* Chris Bubernak +* Christophe Vidal +* Chuck Jazdzewski +* Colby Russell +* Colin Snover +* Cotton Hou +* Cyrus Najmabadi +* Dafrok Zhang +* Dahan Gong +* Dan Corder +* Dan Freeman +* Dan Quirk +* Daniel Gooss +* Daniel Hollocher +* Daniel Król +* Daniel Lehenbauer +* Daniel Rosenwasser +* David Kmenta +* David Li +* David Sheldrick +* David Sherret +* David Souther +* David Staheli +* Denis Nedelyaev +* Derek P Sifford +* Dhruv Rajvanshi +* Dick van den Brink +* Diogo Franco (Kovensky) +* Dirk Bäumer +* Dirk Holtwick +* Dom Chen +* Donald Pipowitch +* Doug Ilijev +* @e-cloud +* Ecole Keine +* Elisée Maurer +* Elizabeth Dinella +* Emilio García-Pumarino +* Eric Grube +* Eric Tsang +* Erik Edrosa +* Erik McClenney +* Esakki Raj +* Ethan Resnick +* Ethan Rubio +* Eugene Timokhov +* Evan Martin +* Evan Sebastian +* Eyas Sharaiha +* Fabian Cook +* @falsandtru +* Filipe Silva +* @flowmemo +* Francois Wouts +* Frank Wallis +* Franklin Tse +* František Žiacik +* Gabe Moothart +* Gabriel Isenberg +* Gilad Peleg +* Godfrey Chan +* Graeme Wicksted +* Guilherme Oenning +* Guillaume Salles +* Guy Bedford +* Halasi Tamás +* Harald Niesche +* Hendrik Liebau +* Henry Mercer +* Herrington Darkholme +* Holger Jeromin +* Homa Wong +* Iain Monro +* @IdeaHunter +* Igor Novozhilov +* Ika +* Ingvar Stepanyan +* Isiah Meadows +* Ivan Enderlin +* Ivo Gabe de Wolff +* Iwata Hidetaka +* Jack Williams +* Jakub Korzeniowski +* Jakub Młokosiewicz +* James Henry +* James Whitney +* Jan Melcher +* Jason Freeman +* Jason Jarrett +* Jason Killian +* Jason Ramsay +* JBerger +* Jed Mao +* Jeffrey Morlan +* Jesse Schalken +* Jing Ma +* Jiri Tobisek +* Joe Calzaretta +* Joe Chung +* Joel Day +* Joey Wilson +* Johannes Rieken +* John Doe +* John Vilk +* Jonathan Bond-Caron +* Jonathan Park +* Jonathan Toland +* Jonathan Turner +* Jonathon Smith +* Jordi Oliveras Rovira +* Joscha Feth +* Josh Abernathy +* Josh Goldberg +* Josh Kalderimis +* Josh Soref +* Juan Luis Boya García +* Julian Williams +* Justin Bay +* Justin Johansson +* K. Preißer +* Kagami Sascha Rosylight +* Kanchalai Tanglertsampan +* Kate Miháliková +* Keith Mashinter +* Ken Howard +* Kenji Imamula +* Kerem Kat +* Kevin Donnelly +* Kevin Gibbons +* Kevin Lang +* Khải +* Kitson Kelly +* Klaus Meinhardt +* Kris Zyp +* Kyle Kelley +* Kārlis Gaņģis +* Lorant Pinter +* Lucien Greathouse +* Lukas Elmer +* Maarten Sijm +* Magnus Hiie +* Magnus Kulke +* Manish Giri +* Marin Marinov +* Marius Schulz +* Markus Johnsson +* Martin Hiller +* Martin Probst +* Martin Vseticka +* Martyn Janes +* Masahiro Wakame +* Mateusz Burzyński +* Matt Bierner +* Matt McCutchen +* Matt Mitchell +* Mattias Buelens +* Mattias Buelens +* Max Deepfield +* Maxwell Paul Brickner +* @meyer +* Micah Zoltu +* @micbou +* Michael +* Michael Bromley +* Mike Busyrev +* Mike Morearty +* Mine Starks +* Mohamed Hegazy +* Mohsen Azimi +* Myles Megyesi +* Nathan Shively-Sanders +* Nathan Yee +* Nicolas Henry +* Nicu Micleușanu +* @nieltg +* Nima Zahedi +* Noah Chen +* Noel Varanda +* Noj Vek +* Oleg Mihailik +* Oleksandr Chekhovskyi +* Omer Sheikh +* Orta Therox +* Oskar Segersva¨rd +* Oussama Ben Brahim +* Patrick Zhong +* Paul Jolly +* Paul Koerbitz +* Paul van Brenk +* @pcbro +* Pedro Maltez +* Perry Jiang +* Peter Burns +* Philip Bulley +* Philippe Voinov +* Pi Lanningham +* Piero Cangianiello +* @piloopin +* Prayag Verma +* Priyantha Lankapura +* @progre +* Punya Biswal +* Rado Kirov +* Raj Dosanjh +* Reiner Dolp +* Remo H. Jansen +* @rhysd +* Ricardo N Feliciano +* Richard Karmazín +* Richard Knoll +* Richard Sentino +* Robert Coie +* Rohit Verma +* Ron Buckton +* Rostislav Galimsky +* Rowan Wyborn +* Ryan Cavanaugh +* Ryohei Ikegami +* Sam Bostock +* Sam El-Husseini +* Sarangan Rajamanickam +* Sean Barag +* Sergey Rubanov +* Sergey Shandar +* Sergii Bezliudnyi +* Sharon Rolel +* Sheetal Nandi +* Shengping Zhong +* Shyyko Serhiy +* Simon Hürlimann +* Slawomir Sadziak +* Solal Pirelli +* Soo Jae Hwang +* Stan Thomas +* Stanislav Iliev +* Stanislav Sysoev +* Stas Vilchik +* Stephan Ginthör +* Steve Lucco +* @styfle +* Sudheesh Singanamalla +* Sébastien Arod +* @T18970237136 +* @t_ +* Taras Mankovski +* Tarik Ozket +* Tetsuharu Ohzeki +* Thomas den Hollander +* Thomas Loubiou +* Tien Hoanhtien +* Tim Lancina +* Tim Perry +* Tim Viiding-Spader +* Tingan Ho +* Todd Thomson +* togru +* Tomas Grubliauskas +* Torben Fitschen +* @TravCav +* TruongSinh Tran-Nguyen +* Tycho Grouwstra +* Vadi Taslim +* Vakhurin Sergey +* Vidar Tonaas Fauske +* Viktor Zozulyak +* Vilic Vane +* Vimal Raghubir +* Vladimir Kurchatkin +* Vladimir Matveev +* Vyacheslav Pukhanov +* Wenlu Wang +* Wesley Wigham +* William Orr +* Wilson Hobbs +* York Yao +* @yortus +* Yuichi Nukiyama +* Yuval Greenfield +* Zeeshan Ahmed +* Zev Spitz +* Zhengbo Li +* @Zzzen + + +Node Node.js is licensed for use as follows: """ @@ -821,387 +4536,7 @@ The externally maintained libraries used by Node.js are: written authorization of the copyright holder. --------------------- - - Third-Party Software Licenses - - This section contains third-party software notices and/or additional - terms for licensed third-party software components included within ICU - libraries. - - 1. ICU License - ICU 1.8.1 to ICU 57.1 - - COPYRIGHT AND PERMISSION NOTICE - - Copyright (c) 1995-2016 International Business Machines Corporation and others - All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, and/or sell copies of the Software, and to permit persons - to whom the Software is furnished to do so, provided that the above - copyright notice(s) and this permission notice appear in all copies of - the Software and that both the above copyright notice(s) and this - permission notice appear in supporting documentation. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY - SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER - RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - - Except as contained in this notice, the name of a copyright holder - shall not be used in advertising or otherwise to promote the sale, use - or other dealings in this Software without prior written authorization - of the copyright holder. - - All trademarks and registered trademarks mentioned herein are the - property of their respective owners. - - 2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt) - - # The Google Chrome software developed by Google is licensed under - # the BSD license. Other software included in this distribution is - # provided under other licenses, as set forth below. - # - # The BSD License - # http://opensource.org/licenses/bsd-license.php - # Copyright (C) 2006-2008, Google Inc. - # - # All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions are met: - # - # Redistributions of source code must retain the above copyright notice, - # this list of conditions and the following disclaimer. - # Redistributions in binary form must reproduce the above - # copyright notice, this list of conditions and the following - # disclaimer in the documentation and/or other materials provided with - # the distribution. - # Neither the name of Google Inc. nor the names of its - # contributors may be used to endorse or promote products derived from - # this software without specific prior written permission. - # - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - # - # - # The word list in cjdict.txt are generated by combining three word lists - # listed below with further processing for compound word breaking. The - # frequency is generated with an iterative training against Google web - # corpora. - # - # * Libtabe (Chinese) - # - https://sourceforge.net/project/?group_id=1519 - # - Its license terms and conditions are shown below. - # - # * IPADIC (Japanese) - # - http://chasen.aist-nara.ac.jp/chasen/distribution.html - # - Its license terms and conditions are shown below. - # - # ---------COPYING.libtabe ---- BEGIN-------------------- - # - # /* - # * Copyright (c) 1999 TaBE Project. - # * Copyright (c) 1999 Pai-Hsiang Hsiao. - # * All rights reserved. - # * - # * Redistribution and use in source and binary forms, with or without - # * modification, are permitted provided that the following conditions - # * are met: - # * - # * . Redistributions of source code must retain the above copyright - # * notice, this list of conditions and the following disclaimer. - # * . Redistributions in binary form must reproduce the above copyright - # * notice, this list of conditions and the following disclaimer in - # * the documentation and/or other materials provided with the - # * distribution. - # * . Neither the name of the TaBE Project nor the names of its - # * contributors may be used to endorse or promote products derived - # * from this software without specific prior written permission. - # * - # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - # * OF THE POSSIBILITY OF SUCH DAMAGE. - # */ - # - # /* - # * Copyright (c) 1999 Computer Systems and Communication Lab, - # * Institute of Information Science, Academia - # * Sinica. All rights reserved. - # * - # * Redistribution and use in source and binary forms, with or without - # * modification, are permitted provided that the following conditions - # * are met: - # * - # * . Redistributions of source code must retain the above copyright - # * notice, this list of conditions and the following disclaimer. - # * . Redistributions in binary form must reproduce the above copyright - # * notice, this list of conditions and the following disclaimer in - # * the documentation and/or other materials provided with the - # * distribution. - # * . Neither the name of the Computer Systems and Communication Lab - # * nor the names of its contributors may be used to endorse or - # * promote products derived from this software without specific - # * prior written permission. - # * - # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - # * OF THE POSSIBILITY OF SUCH DAMAGE. - # */ - # - # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, - # University of Illinois - # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 - # - # ---------------COPYING.libtabe-----END-------------------------------- - # - # - # ---------------COPYING.ipadic-----BEGIN------------------------------- - # - # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science - # and Technology. All Rights Reserved. - # - # Use, reproduction, and distribution of this software is permitted. - # Any copy of this software, whether in its original form or modified, - # must include both the above copyright notice and the following - # paragraphs. - # - # Nara Institute of Science and Technology (NAIST), - # the copyright holders, disclaims all warranties with regard to this - # software, including all implied warranties of merchantability and - # fitness, in no event shall NAIST be liable for - # any special, indirect or consequential damages or any damages - # whatsoever resulting from loss of use, data or profits, whether in an - # action of contract, negligence or other tortuous action, arising out - # of or in connection with the use or performance of this software. - # - # A large portion of the dictionary entries - # originate from ICOT Free Software. The following conditions for ICOT - # Free Software applies to the current dictionary as well. - # - # Each User may also freely distribute the Program, whether in its - # original form or modified, to any third party or parties, PROVIDED - # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear - # on, or be attached to, the Program, which is distributed substantially - # in the same form as set out herein and that such intended - # distribution, if actually made, will neither violate or otherwise - # contravene any of the laws and regulations of the countries having - # jurisdiction over the User or the intended distribution itself. - # - # NO WARRANTY - # - # The program was produced on an experimental basis in the course of the - # research and development conducted during the project and is provided - # to users as so produced on an experimental basis. Accordingly, the - # program is provided without any warranty whatsoever, whether express, - # implied, statutory or otherwise. The term "warranty" used herein - # includes, but is not limited to, any warranty of the quality, - # performance, merchantability and fitness for a particular purpose of - # the program and the nonexistence of any infringement or violation of - # any right of any third party. - # - # Each user of the program will agree and understand, and be deemed to - # have agreed and understood, that there is no warranty whatsoever for - # the program and, accordingly, the entire risk arising from or - # otherwise connected with the program is assumed by the user. - # - # Therefore, neither ICOT, the copyright holder, or any other - # organization that participated in or was otherwise related to the - # development of the program and their respective officials, directors, - # officers and other employees shall be held liable for any and all - # damages, including, without limitation, general, special, incidental - # and consequential damages, arising out of or otherwise in connection - # with the use or inability to use the program or any product, material - # or result produced or otherwise obtained by using the program, - # regardless of whether they have been advised of, or otherwise had - # knowledge of, the possibility of such damages at any time during the - # project or thereafter. Each user will be deemed to have agreed to the - # foregoing by his or her commencement of use of the program. The term - # "use" as used herein includes, but is not limited to, the use, - # modification, copying and distribution of the program and the - # production of secondary products from the program. - # - # In the case where the program, whether in its original form or - # modified, was distributed or delivered to or received by a user from - # any person, organization or entity other than ICOT, unless it makes or - # grants independently of ICOT any specific warranty to the user in - # writing, such person, organization or entity, will also be exempted - # from and not be held liable to the user for any such damages as noted - # above as far as the program is concerned. - # - # ---------------COPYING.ipadic-----END---------------------------------- - - 3. Lao Word Break Dictionary Data (laodict.txt) - - # Copyright (c) 2013 International Business Machines Corporation - # and others. All Rights Reserved. - # - # Project: http://code.google.com/p/lao-dictionary/ - # Dictionary: http://lao-dictionary.googlecode.com/git/Lao-Dictionary.txt - # License: http://lao-dictionary.googlecode.com/git/Lao-Dictionary-LICENSE.txt - # (copied below) - # - # This file is derived from the above dictionary, with slight - # modifications. - # ---------------------------------------------------------------------- - # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. - # All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, - # are permitted provided that the following conditions are met: - # - # - # Redistributions of source code must retain the above copyright notice, this - # list of conditions and the following disclaimer. Redistributions in - # binary form must reproduce the above copyright notice, this list of - # conditions and the following disclaimer in the documentation and/or - # other materials provided with the distribution. - # - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - # OF THE POSSIBILITY OF SUCH DAMAGE. - # -------------------------------------------------------------------------- - - 4. Burmese Word Break Dictionary Data (burmesedict.txt) - - # Copyright (c) 2014 International Business Machines Corporation - # and others. All Rights Reserved. - # - # This list is part of a project hosted at: - # github.com/kanyawtech/myanmar-karen-word-lists - # - # -------------------------------------------------------------------------- - # Copyright (c) 2013, LeRoy Benjamin Sharon - # All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: Redistributions of source code must retain the above - # copyright notice, this list of conditions and the following - # disclaimer. Redistributions in binary form must reproduce the - # above copyright notice, this list of conditions and the following - # disclaimer in the documentation and/or other materials provided - # with the distribution. - # - # Neither the name Myanmar Karen Word Lists, nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS - # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - # SUCH DAMAGE. - # -------------------------------------------------------------------------- - - 5. Time Zone Database - - ICU uses the public domain data and code derived from Time Zone - Database for its time zone support. The ownership of the TZ database - is explained in BCP 175: Procedure for Maintaining the Time Zone - Database section 7. - - # 7. Database Ownership - # - # The TZ database itself is not an IETF Contribution or an IETF - # document. Rather it is a pre-existing and regularly updated work - # that is in the public domain, and is intended to remain in the - # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do - # not apply to the TZ Database or contributions that individuals make - # to it. Should any claims be made and substantiated against the TZ - # Database, the organization that is providing the IANA - # Considerations defined in this RFC, under the memorandum of - # understanding with the IETF, currently ICANN, may act in accordance - # with all competent court orders. No ownership claims will be made - # by ICANN or the IETF Trust on the database or the code. Any person - # making a contribution to the database or code waives all rights to - # future claims in that contribution or in the TZ Database. - - 6. Google double-conversion - - Copyright 2006-2011, the V8 project authors. All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - """ - + - libuv, located at deps/uv, is licensed as follows: """ libuv is licensed for use as follows: @@ -1358,7 +4693,7 @@ The externally maintained libraries used by Node.js are: - Punycode.js, located at lib/punycode.js, is licensed as follows: """ - Copyright Mathias Bynens + Copyright Mathias Bynens Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -1450,7 +4785,7 @@ The externally maintained libraries used by Node.js are: """ SipHash reference C implementation - Copyright (c) 2016 Jean-Philippe Aumasson + Copyright (c) 2016 Jean-Philippe Aumasson To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public @@ -1903,7 +5238,7 @@ The externally maintained libraries used by Node.js are: - babel-eslint, located at tools/node_modules/babel-eslint, is licensed as follows: """ - Copyright (c) 2014-2016 Sebastian McKenzie + Copyright (c) 2014-2016 Sebastian McKenzie MIT License @@ -2122,7 +5457,7 @@ The externally maintained libraries used by Node.js are: """ ISC License - Copyright (c) 2012, Ben Noordhuis + Copyright (c) 2012, Ben Noordhuis Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -2154,15 +5489,12 @@ The externally maintained libraries used by Node.js are: ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ -============================================= -END OF Node.js NOTICES, INFORMATION, AND LICENSE -%% Gulp NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= +gulp The MIT License (MIT) -Copyright (c) 2013-2018 Blaine Bublitz , Eric Schoffstall and other contributors +Copyright (c) 2013-2018 Blaine Bublitz , Eric Schoffstall and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -2181,12 +5513,9 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -============================================= -END OF Gulp NOTICES, INFORMATION, AND LICENSE -%% Microsoft MakeCode NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= +Microsoft MakeCode (PXT - Programming eXperience Toolkit) The MIT License (MIT) Copyright (c) Microsoft Corporation @@ -2210,12 +5539,9 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -============================================= -END OF Microsoft MakeCode NOTICES, INFORMATION, AND LICENSE -%% MakeCode for Adafruit Circuit Playground Express NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= +MakeCode for Adafruit Circuit Playground Express PXT-Adafruit The MIT License (MIT) @@ -2241,63 +5567,107 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -============================================= -END OF MakeCode for Adafruit Circuit Playground Express NOTICES, INFORMATION, AND LICENSE -%% Python for Win32 NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= -PSF License -1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and - the Individual or Organization ("Licensee") accessing and otherwise using Python - 3.7.4rc2 software in source or binary form and its associated documentation. +pywin32 +Unless stated in the specfic source file, this work is +Copyright (c) 1996-2008, Greg Stein and Mark Hammond. +All rights reserved. -2. Subject to the terms and conditions of this License Agreement, PSF hereby - grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, - analyze, test, perform and/or display publicly, prepare derivative works, - distribute, and otherwise use Python 3.7.4rc2 alone or in any derivative - version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright © 2001-2019 Python Software Foundation; All Rights - Reserved" are retained in Python 3.7.4rc2 alone or in any derivative version - prepared by Licensee. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the distribution. + +Neither names of Greg Stein, Mark Hammond nor the name of contributors may be used +to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Unless stated in the specfic source file, this work is +Copyright (c) 1994-2008, Mark Hammond +All rights reserved. -3. In the event Licensee prepares a derivative work that is based on or - incorporates Python 3.7.4rc2 or any part thereof, and wants to make the - derivative work available to others as provided herein, then Licensee hereby - agrees to include in any such work a brief summary of the changes made to Python - 3.7.4rc2. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the distribution. + +Neither name of Mark Hammond nor the name of contributors may be used +to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Unless stated in the specfic source file, this work is +Copyright (c) 1994-2008, Mark Hammond +All rights reserved. -4. PSF is making Python 3.7.4rc2 available to Licensee on an "AS IS" basis. - PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF - EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR - WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE - USE OF PYTHON 3.7.4rc2 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.7.4rc2 - FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF - MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.7.4rc2, OR ANY DERIVATIVE - THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. -6. This License Agreement will automatically terminate upon a material breach of - its terms and conditions. +Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in +the documentation and/or other materials provided with the distribution. -7. Nothing in this License Agreement shall be deemed to create any relationship - of agency, partnership, or joint venture between PSF and Licensee. This License - Agreement does not grant permission to use PSF trademarks or trade name in a - trademark sense to endorse or promote products or services of Licensee, or any - third party. +Neither name of Mark Hammond nor the name of contributors may be used +to endorse or promote products derived from this software without +specific prior written permission. -8. By copying, installing or otherwise using Python 3.7.4rc2, Licensee agrees - to be bound by the terms and conditions of this License Agreement. -============================================= -END OF Python for Win32 NOTICES, INFORMATION, AND LICENSE +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -%% Playsound NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= +playsound The MIT License (MIT) -Copyright (c) 2016 Taylor Marks +Copyright (c) 2016 Taylor Marks Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -2316,12 +5686,9 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -============================================= -END OF Playsound NOTICES, INFORMATION, AND LICENSE -%% pytest NOTICES, INFORMATION, AND LICENSE BEGIN HERE -============================================= +pytest The MIT License (MIT) Copyright (c) 2004-2019 Holger Krekel and others @@ -2343,5 +5710,438 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -============================================= -END OF pytest NOTICES, INFORMATION, AND LICENSE + + +Visual Studio Code extension for Arduino +------------------------------------------ START OF LICENSE ----------------------------------------- + +vscode-arduino + +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +----------------------------------------------- END OF LICENSE ------------------------------------------ + + +Python-Socketio +The MIT License (MIT) + +Copyright (c) 2015 Miguel Grinberg + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Requests +Copyright 2018 Kenneth Reitz + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +node-forge 0.7.5 - BSD-3-Clause OR GPL-2.0 +https://github.com/digitalbazaar/forge +(c) 2016 +Copyright (c) 2005 Tom Wu +Copyright (c) 2003-2005 Tom Wu +Copyright (c) 2005-2009 Tom Wu +Copyright (c) 2012 Kenji Urushima +Copyright (c) 2013 Digital Bazaar, Inc. +Copyright (c) 2014 Digital Bazaar, Inc. +Copyright (c) 2010, Digital Bazaar, Inc. +Copyright 2008-2013 Digital Bazaar, Inc. +Copyright 2011-2016 Digital Bazaar, Inc. +Copyright 2011-2017 Digital Bazaar, Inc. +copyrighted by the Free Software Foundation +Copyright (c) 2008-2013 Digital Bazaar, Inc. +Copyright (c) 2009-2012 Digital Bazaar, Inc. +Copyright (c) 2009-2013 Digital Bazaar, Inc. +Copyright (c) 2009-2014 Digital Bazaar, Inc. +Copyright (c) 2009-2015 Digital Bazaar, Inc. +Copyright (c) 2010-2012 Digital Bazaar, Inc. +Copyright (c) 2010-2013 Digital Bazaar, Inc. +Copyright (c) 2010-2014 Digital Bazaar, Inc. +Copyright (c) 2010-2015 Digital Bazaar, Inc. +Copyright (c) 2010-2018 Digital Bazaar, Inc. +Copyright (c) 2011-2014 Digital Bazaar, Inc. +Copyright (c) 2012-2014 Digital Bazaar, Inc. +Copyright (c) 2012-2015 Digital Bazaar, Inc. +Copyright (c) 2013-2014 Digital Bazaar, Inc. +Copyright (c) 2014-2015 Digital Bazaar, Inc. +Copyright (c) 2017-2018 Digital Bazaar, Inc. +Copyright 2012 Stefan Siegl +Copyright (c) 2012 Stefan Siegl +Copyright (c) 1989, 1991 Free Software Foundation, Inc. +Copyright (c) Ellis Pritchard, Guardian Unlimited 2003. +Copyright (c) 2014 Lautaro Cozzani + +You may use the Forge project under the terms of either the BSD License or the +GNU General Public License (GPL) Version 2. + +The BSD License is recommended for most projects. It is simple and easy to +understand and it places almost no restrictions on what you can do with the +Forge project. + +If the GPL suits your project better you are also free to use Forge under +that license. + +You don't have to do anything special to choose one license or the other and +you don't have to notify anyone which license you are using. You are free to +use this project in commercial projects as long as the copyright header is +left intact. + +If you are a commercial entity and use this set of libraries in your +commercial software then reasonable payment to Digital Bazaar, if you can +afford it, is not required but is expected and would be appreciated. If this +library saves you time, then it's saving you money. The cost of developing +the Forge software was on the order of several hundred hours and tens of +thousands of dollars. We are attempting to strike a balance between helping +the development community while not being taken advantage of by lucrative +commercial entities for our efforts. + +------------------------------------------------------------------------------- +New BSD License (3-clause) +Copyright (c) 2010, Digital Bazaar, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Digital Bazaar, Inc. nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DIGITAL BAZAAR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + + +------------------------------------------------------------------- diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 000000000..d5cf4a87a Binary files /dev/null and b/assets/icon.png differ diff --git a/assets/readmeFiles/cpx.jpg b/assets/readmeFiles/cpx.jpg new file mode 100644 index 000000000..28765f4e5 Binary files /dev/null and b/assets/readmeFiles/cpx.jpg differ diff --git a/assets/readmeFiles/deployToBoard.png b/assets/readmeFiles/deployToBoard.png new file mode 100644 index 000000000..b4e18f8a4 Binary files /dev/null and b/assets/readmeFiles/deployToBoard.png differ diff --git a/assets/readmeFiles/findExamples.jpg b/assets/readmeFiles/findExamples.jpg new file mode 100644 index 000000000..ac1997ee7 Binary files /dev/null and b/assets/readmeFiles/findExamples.jpg differ diff --git a/assets/readmeFiles/newFile.gif b/assets/readmeFiles/newFile.gif new file mode 100644 index 000000000..76f7bbc99 Binary files /dev/null and b/assets/readmeFiles/newFile.gif differ diff --git a/assets/readmeFiles/otherSensors.gif b/assets/readmeFiles/otherSensors.gif new file mode 100644 index 000000000..76b2aa8f7 Binary files /dev/null and b/assets/readmeFiles/otherSensors.gif differ diff --git a/assets/readmeFiles/run.gif b/assets/readmeFiles/run.gif new file mode 100644 index 000000000..fb1fa4c96 Binary files /dev/null and b/assets/readmeFiles/run.gif differ diff --git a/assets/readmeFiles/slider_basedSensor.gif b/assets/readmeFiles/slider_basedSensor.gif new file mode 100644 index 000000000..5e1b8b3b0 Binary files /dev/null and b/assets/readmeFiles/slider_basedSensor.gif differ diff --git a/docs/developers-setup.md b/docs/developers-setup.md index 8decc495b..d9eb3a2b1 100644 --- a/docs/developers-setup.md +++ b/docs/developers-setup.md @@ -8,27 +8,32 @@ - Python 3.7.4 (or latest) - - Download link : https://www.python.org/downloads/ - - **NOTE :** Make sure Python is in your path under an environment variable named `python` (during installation or insert it manually afterwards) - - **NOTE :** Make sure pip is added to your environment variables as well - (for example it could be found at : `c:\users\<...>\appdata\local\programs\python\python37\lib\site-packages\pip`) - - Run in a console `python -m pip install --upgrade pip` - -- Playsound - - - Run the command in a console : `pip install playsound` - -- pytest - - - Run the command in a console : `pip install pytest` - -- Pywin32 - - - Run the command in a console : `pip install pywin32` - -- VS Code - -- Python extension for VS Code (download from VS Code market place) +- Download link : https://www.python.org/downloads/ +- **NOTE :** Make sure Python is in your path under an environment variable named `python` or `python3.7` (during installation or insert it manually afterwards) +- **NOTE :** Make sure pip is added to your environment variables as well + (for example it could be found at : `c:\users\<...>\appdata\local\programs\python\python37\lib\site-packages\pip`) +- Run in a console `python -m pip install --upgrade pip` + +* Python Modules + + - **Note:** On extension activation you will be prompted a popup asking if you want the modules to be automatically installed for you, **except** `pywin32` which is needed only on Windows platform. + - Playsound + - Run the command in a console : `pip install playsound` + - pytest + - Run the command in a console : `pip install pytest` + - Pywin32 + - **Note:** This is only needed for Windows computers. You must install it manually with the above command! + - Run the command in a console : `pip install pywin32` + - Python-Socketio + - Run the command in a console : `pip install python-socketio` + - Requests + - Run the command in a console : `pip install requests` + - Application Insights + - Run the command in a console : `pip install applicationinsights` + +* VS Code + +* Python extension for VS Code (download from VS Code market place) ## Steps to Get Started Running the Extension in Debug Mode @@ -50,7 +55,7 @@ - Debugging the extension opens a new VS Code window with the extension installed - From the original VS Code window (opened in our repository) you can see outputs in the Debug Console - In the new VS Code window, you can access the commands provided by the extension from the Commands Palette (Ctrl+Shift+P) - listed as 'Pacifica : ...' + listed as 'Device Simulator Express : ...' - If you change some files you'll need to run the 'npm run compile' command again and restart debugging ## Repository Structure (important files) @@ -62,3 +67,5 @@ - `view` : React side - `components/` - `cpx/` and `Simulator.tsx` : contain the React components and objects to display and handle the simulator webview + - `toolbar/` : contains the React components used in the toolbar and the modal. + - `translation/en.json`: contains the constants that should be localized. To internationalize the extension you can add additional files with constants sharing the same id found in _en.json_, but with the translated values. diff --git a/docs/how-to-use.md b/docs/how-to-use.md index a44526f36..6f6892ab4 100644 --- a/docs/how-to-use.md +++ b/docs/how-to-use.md @@ -2,8 +2,8 @@ Commands are accessible through : -- **The command palette** (`Ctrl+shift+P` or `View->Command Palette`) and type 'Pacifica : `command_name`' -- **The extension buttons** available on the top right of the Text Editor Panel when you have a Python file open +- **The command palette** (`Ctrl+shift+P` or `View->Command Palette`) and type 'Device Simulator Express : `command_name`' +- **The extension buttons** available on the top right of the Text Editor Panel when you have a Python file open (1) ## Available commands @@ -18,12 +18,24 @@ Commands are accessible through : - **Deploy to Device** : saves the code to a Circuit Playground Express. _(**Note :** the board needs to be correctly formatted to a `CIRCUITPY` drive first if it's not the case : [Installing CircuitPython](https://learn.adafruit.com/welcome-to-circuitpython/installing-circuitpython))_. +* **Select Serial Port** : selects the serial port of the board you want the serial monitor to interact with. (2) + _(**Note :** USB detection must be enabled in the extension settings.)_ + +* **Open Serial Monitor** : opens the serial monitor. (2) + _(**Note :** A serial port must have been selected already)_. + +* **Change Baud Rate** : changes the baud rate of the serial monitor. (2) + +* **Close Serial Montitor** : closes the serial monitor. (2) + ## Available features - We currently support the [Adafruit Circuit Playground Express board](https://www.adafruit.com/product/3333) - Access to auto-completion and Python error flagging - Output panel for the simulator - Deploy to the physical device (if correctly formatted) +- Debugger for the simulator +- Serial Monitor (available on Windows and Mac only) - Device's features : - NeoPixels - Buttons (A & B) @@ -41,15 +53,29 @@ Commands are accessible through : ## Not supported yet - Auto-detect/format the device -- Serial monitor for the device -- Debugger for the simulator - Device's features - Sound sensor - - Tones - - Sound detection\* - - IR transmitter\* - - Motion sensors - - Tap detection + - Tones +- Sound detection (3) +- IR transmitter (3) +- Motion sensors + - Tap detection + +## Device Simulator Express configuration + +Here are the settings you can change in the Device Simulator Express configuration: + +- **Debugger Server Port:** allows you to change the port used to communicate with the debugger. Default value is _5577_. (4) + +- **Enable USB Detection:** when disabled, prevents the serial monitor from listening to messages from the serial port. + +- **Show Device Icon In Editor Title Menu:** allows you to choose whether the _`Deploy to Device`_ button should be in the editor title. + +- **Show Open Icon In Editor Title Menu:** allows you to choose whether the _`Open Simulator`_ button should be in the editor title. + +- **Show Simulator Icon In Editor Title Menu:** allows you to choose whether the _`Run Simulator`_ button should be in the editor title. + +- **Show Dependency Install:** allows you to choose whether you want to be prompted to install the Python dependencies. ## Troubleshooting Tips @@ -58,7 +84,11 @@ Commands are accessible through : - To open the output panel again after closing it go to VS Code menu : `View->Output`. - If you have pylint enabled, it might underline the import of the adafruit_circuitplayground library, but it will work correctly. - If you try to deploy to the device while it's plugged in but you still get an error saying it cannot find the board, make sure your Circuit Playground Express is formatted correctly and that its name matches `CIRCUITPY`. +- If you can't get the Simulator communication working while debugging, try to open you `Settings` and check the port used under `'Device Simulator Express: Debugger Server Port'`. You can either change it (usually ports above 5000 could work) or try to free it, then start debugging again. -### Note +### Notes -\* Sensors currently not supported by the official adafruit_circuit_playground Express library (v2.1.2). +(1) Can be changed in settings. +(2) To use the Serial Monitor commands, you'll need to open a folder because this saves the configuration file for the serial communication. You can still use the rest of the extension without opening a folder. +(3) Sensors currently not supported by the official adafruit_circuit_playground.express library (v2.1.2). +(4) The regular communication is using the stdout and stdin of the Python process. But when you debug your code, it will communicate over sockets on port 5577. This is the default port that you can change in your `Settings` : `'Device Simulator Express: Debugger Server Port'`. diff --git a/docs/install.md b/docs/install.md index d6fd79e09..b0d782d2d 100644 --- a/docs/install.md +++ b/docs/install.md @@ -17,15 +17,19 @@ _Note: You need to install all the dependencies in order to use the extension._ - [VS Code](https://code.visualstudio.com/Download) - [Node](https://nodejs.org/en/download/) -- [Python 3.7.4 (or latest)](https://www.python.org/downloads/) - - **Warning :** Make sure you've included `python` and `pip` to your `PATH` in your **environment variables**. - _(Note: the easiest way to do it might be when you install Python, you can select the "Add to PATH" option directly. Otherwise you can search how to insert it manually, but make sure that when you type `python` in a terminal, the command is recognized.)_ -- Python VS Code extension (downloaded from VS Code Marketplace) +- [Python 3.7.4](https://www.python.org/downloads/) + - **Warning :** Make sure you've included `python` (or `python3.7`) and `pip` to your `PATH` in your **environment variables**. + _(Note: the easiest way to do it might be when you install Python, you can select the "Add to PATH" option directly. Otherwise you can search how to insert it manually, but make sure that when you type `python` (or `python3.7`) in a terminal, the command is recognized.)_ +- [Python VS Code extension](https://marketplace.visualstudio.com/items?itemName=ms-python.python) - **Note:** This extension is installed automatically from the marketplace when you install our extension -- Playsound : - - `python -m pip install --upgrade pip` - - `pip install playsound` -- Pywin32 : `pip install pywin32` +- Python Modules + - **Note:** On extension activation you will be prompted a popup asking if you want the modules to be automatically installed for you, **except** `pywin32` which is needed only on Windows platform. + - Playsound : `pip install playsound` + - Pywin32 : `pip install pywin32` + - **Note:** This is only needed for Windows computers. You must install it manually with the above command! + - Python-Socketio : `pip install python-socketio` + - Requests : `pip install requests` + - Application Insights: `pip install applicationinsights` ## How to use the Extension diff --git a/docs/telemetry.md b/docs/telemetry.md index de9268d1e..e9a22941e 100644 --- a/docs/telemetry.md +++ b/docs/telemetry.md @@ -1,6 +1,6 @@ -# Pacifica Telemetry +# Device Simulator Express Telemetry -Pacifica logs usage data and diagnostics telemetry through [Application Insights](https://azure.microsoft.com/en-us/services/monitor/). +The Device Simulator Express logs usage data and diagnostics telemetry through [Application Insights](https://azure.microsoft.com/en-us/services/monitor/). ## Telemetry Gathered @@ -11,15 +11,15 @@ This extension collects basic diagnostics telemetry and usage data: ## Usage Telemetry -Through the Application Insights API, telemetry events are collected on Pacifica extension usage. The follow table describes the Telemetry events we collect: +Through the Application Insights API, telemetry events are collected on The Device Simulator Express extension usage. The follow table describes the Telemetry events we collect: -| **Property** | **Note** | -| :-------------------: | ---------------------------------------------------------------------------------------------------- | -| **Event Name** | Unique event name/descriptor for the event. For ex: Pacifica/COMMAND_NEW_FILE | -| **VS Code Session ID** | A unique identifier for the current session (changes each time the editor is started) | -| **VS Code Machine ID** | A unique identifier for the computer | -| **VS Code Version** | VS Code version being used by the user | -| **Extension Version** | Pacifica extension version being used | -| **OS** | User's operating system | -| **Performance** | A number indicating how long the command or API call took to execute | -| **Result** | If the event succeeded or not | +| **Property** | **Note** | +| :--------------------: | --------------------------------------------------------------------------------------------- | +| **Event Name** | Unique event name/descriptor for the event. For ex: Device Simulator Express/COMMAND_NEW_FILE | +| **VS Code Session ID** | A unique identifier for the current session (changes each time the editor is started) | +| **VS Code Machine ID** | A unique identifier for the computer | +| **VS Code Version** | VS Code version being used by the user | +| **Extension Version** | The Device Simulator Express extension version being used | +| **OS** | User's operating system | +| **Performance** | A number indicating how long the command or API call took to execute | +| **Result** | If the event succeeded or not | diff --git a/gulpfile.js b/gulpfile.js index b26d4ca16..fdc6fcac2 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -24,12 +24,20 @@ const languages = [{ folderName: "en", id: "en" }]; gulp.task("clean", () => { return del( - ["out/**", "package.nls.*.json", "../../dist/*0.0.0-UNTRACKEDVERSION.vsix"], + [ + "out/!(python_libs)", + "package.nls.*.json", + "../../dist/*0.0.0-UNTRACKEDVERSION.vsix" + ], { force: true } ); }); -const pythonToMove = ["./src/adafruit_circuitplayground/*.*", "./src/*.py"]; +const pythonToMove = [ + "./src/adafruit_circuitplayground/*.*", + "./src/*.py", + "./src/requirements.txt" +]; gulp.task("python-compile", () => { // the base option sets the relative root for the set of files, @@ -58,7 +66,7 @@ gulp.task("vsce:publish", () => { gulp.task("vsce:package", () => { return vsce.createVSIX({ - packagePath: "../../dist/pacifica-0.0.0-UNTRACKEDVERSION.vsix" + packagePath: "../../dist/deviceSimulatorExpress-0.0.0-UNTRACKEDVERSION.vsix" }); }); diff --git a/locales/en/out/constants.i18n.json b/locales/en/out/constants.i18n.json index 88b62c10b..40744b520 100644 --- a/locales/en/out/constants.i18n.json +++ b/locales/en/out/constants.i18n.json @@ -6,13 +6,19 @@ "dialogResponses.exampleCode": "Example Code on GitHub", "dialogResponses.help": "I need help", "dialogResponses.installPython": "Install from python.org", + "dialogResponses.installNow": "Install Now", + "dialogResponses.dontInstall": "Don't Install", "dialogResponses.tutorials": "Tutorials on Adafruit", - " error.incorrectFileNameForDevice": "[ERROR] Can\\'t deploy to your Circuit Playground Express device, please rename your file to \"code.py\" or \"main.py\". \n", + "error.debuggerServerInitFailed": "Warning : The Debugger Server cannot be opened. Please try to free the port {0} if it's already in use or select another one in your Settings 'Device Simulator Express: Debugger Server Port' and start another debug session.\n You can still debug your code but you won't be able to use the Simulator.", + "error.debuggingSessionInProgress": "[ERROR] A debugging session is currently in progress, please stop it before running your code. \n", + "error.incorrectFileNameForDevice": "[ERROR] Can\\'t deploy to your Circuit Playground Express device, please rename your file to \"code.py\" or \"main.py\". \n", "error.incorrectFileNameForDevicePopup": "Seems like you have a different file name than what the CPX requires, please rename it to \"code.py\" or \"main.py\".", "error.incorrectFileNameForSimulatorPopup": "We want your code to work on your actual board as well. Make sure you name your file \"code.py\" or \"main.py\" to be able to run your code on an actual physical device.", "error.invalidFileNameDebug": "The file you tried to debug isn\\'t named \"code.py\" or \"main.py\\. Rename your file if you want your code to work on your actual device.", "error.noDevice": "No plugged in boards detected. Please double check if your board is connected and/or properly formatted", "error.noFileToRun": "\n[ERROR] We can't find the .py file to run on simulator. Open up a new .py file, or browse through some examples\n", + "error.noFolderCreated": "In order to use the Serial Monitor, you need to open a folder and reload VS Code.", + "error.noProgramFoundDebug": "Cannot find a program to debug.", "error.noPythonPath": "We found that you don't have Python 3 installed on your computer, please install the latest version, add it to your PATH and try again.", "error.stderr": "\n[ERROR] {0} \n", "error.unexpectedMessage": "Webview sent an unexpected message", @@ -21,14 +27,17 @@ "info.deploySuccess": "\n[INFO] Code successfully deployed\n", "info.extensionActivated": "Congratulations, your extension Adafruit_Simulator is now active!", "info.firstTimeWebview": "To reopen the simulator click on the \"Open Simulator\" button on the upper right corner of the text editor, or select the command \"Open Simulator\" from command palette.", + "info.installPythonDependencies": "Do you want us to try and install this extensions dependencies for you?", "error.invalidFileExtensionDebug": "The file you tried to run isn\\'t a Python file.", "info.newFile": "New to Python or the Circuit Playground Express? We are here to help!", "info.redirect": "You are being redirected.", "info.runningCode": "Running user code", "info.privacyStatement": "Privacy Statement", + "info.successfulInstall": "Successfully installed Python dependencies.", "info.thirdPartyWebsite": "By clicking \"Agree and Proceed\" you will be redirected to adafruit.com, a third party website not managed by Microsoft. Please note that your activity on adafruit.com is subject to Adafruit's privacy policy", "info.welcomeOutputTab": "Welcome to the Adafruit Simulator output tab !\n\n", - "label.webviewPanel": "Adafruit CPX", - "name": "Pacifica Simulator", + "label.webviewPanel": "Device Simulator Express", + "name": "Device Simulator Express", + "warning.agreeAndRun": "By selecting ‘Agree and Run’, you understand the extension executes Python code on your local computer, which may be a potential security risk." -} \ No newline at end of file +} diff --git a/locales/en/package.i18n.json b/locales/en/package.i18n.json index 4439331c7..49d2906a0 100644 --- a/locales/en/package.i18n.json +++ b/locales/en/package.i18n.json @@ -1,11 +1,16 @@ { - "pacificaExtension.commands.label": "Pacifica", - "pacificaExtension.commands.openSimulator": "Open Simulator", - "pacificaExtension.commands.runSimulator": "Run Simulator", - "pacificaExtension.commands.newFile": "New File", - "pacificaExtension.commands.runDevice": "Deploy to Device", - "pacificaExtension.configuration.title": "Pacfica configuration", - "pacificaExtension.configuration.properties.open": "Whether to show 'Open Simulator' icon in editor title menu.", - "pacificaExtension.configuration.properties.device": "Whether to show 'Run Device' icon in editor title menu.", - "pacificaExtension.configuration.properties.simulator": "Whether to show 'Run Simulator' icon in editor title menu." -} \ No newline at end of file + "deviceSimulatorExpressExtension.commands.changeBaudRate": "Change Baud Rate", + "deviceSimulatorExpressExtension.commands.closeSerialMonitor": "Close Serial Monitor", + "deviceSimulatorExpressExtension.commands.label": "Device Simulator Express", + "deviceSimulatorExpressExtension.commands.openSerialMonitor": "Open Serial Monitor", + "deviceSimulatorExpressExtension.commands.openSimulator": "Open Simulator", + "deviceSimulatorExpressExtension.commands.runSimulator": "Run Simulator", + "deviceSimulatorExpressExtension.commands.newFile": "New File", + "deviceSimulatorExpressExtension.commands.runDevice": "Deploy to Device", + "deviceSimulatorExpressExtension.commands.selectSerialPort": "Select Serial Port", + "deviceSimulatorExpressExtension.configuration.title": "Device Simulator Express configuration", + "deviceSimulatorExpressExtension.configuration.properties.open": "Whether to show 'Open Simulator' icon in editor title menu.", + "deviceSimulatorExpressExtension.configuration.properties.device": "Whether to show 'Run Device' icon in editor title menu.", + "deviceSimulatorExpressExtension.configuration.properties.simulator": "Whether to show 'Run Simulator' icon in editor title menu.", + "deviceSimulatorExpressExtension.configuration.properties.debuggerPort": "The port the Server will listen on for communication with the debugger." +} diff --git a/misc/usbmapping.json b/misc/usbmapping.json new file mode 100644 index 000000000..e3d9519a5 --- /dev/null +++ b/misc/usbmapping.json @@ -0,0 +1,18 @@ +[ + { + "index_file": "https://adafruit.github.io/arduino-board-index/package_adafruit_index.json", + "boards": [ + { + "vid": "239a", + "pid": [ + "8019" + ], + "name": "Adafruit Circuit Playground Express", + "package": "adafruit", + "architecture": "samd", + "id": "cpx" + } + + ] + } +] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 98e1f69a7..b0c2c98d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1137,6 +1137,11 @@ "integrity": "sha512-6It2EVfGskxZCQhuykrfnALg7oVeiI6KclWSmGDqB0AiInVrTGB9Jp9i4/Ad21u9Jde/voVQz6eFX/eSg/UsPA==", "dev": true }, + "@formatjs/intl-relativetimeformat": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-2.8.0.tgz", + "integrity": "sha512-5T3m5hJSxXrbwtnFHyYBSbTjOXPXu+4NJ0MUu1LAf4fPEdd+pJZfWKuMJSWgFQPVMbLYq9NLvDWQda3hVe99sg==" + }, "@gulp-sourcemaps/identity-map": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", @@ -1303,6 +1308,15 @@ "supports-color": "^5.3.0" } }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", @@ -1725,6 +1739,20 @@ "@types/node": "*" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, + "@types/invariant": { + "version": "2.2.30", + "resolved": "https://registry.npmjs.org/@types/invariant/-/invariant-2.2.30.tgz", + "integrity": "sha512-98fB+yo7imSD2F7PF7GIpELNgtLNgo5wjivu0W5V4jx+KVVJxo6p/qN4zdzSTBWy4/sN3pPyXwnhRSD28QX+ag==" + }, "@types/istanbul-lib-coverage": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", @@ -1778,8 +1806,7 @@ "@types/prop-types": { "version": "15.7.1", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz", - "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==", - "dev": true + "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==" }, "@types/q": { "version": "1.5.2", @@ -1791,7 +1818,6 @@ "version": "16.8.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.18.tgz", "integrity": "sha512-lUXdKzRqWR4FebR5tGHkLCqnvQJS4fdXKCBrNGGbglqZg2gpU+J82pMONevQODUotATs9fc9k66bx3/St8vReg==", - "dev": true, "requires": { "@types/prop-types": "*", "csstype": "^2.2.0" @@ -1806,6 +1832,14 @@ "@types/react": "*" } }, + "@types/socket.io": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/socket.io/-/socket.io-2.1.2.tgz", + "integrity": "sha512-Ind+4qMNfQ62llyB4IMs1D8znMEBsMKohZBPqfBUIXqLQ9bdtWIbNTBWwtdcBWJKnokMZGcmWOOKslatni5vtA==", + "requires": { + "@types/node": "*" + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -2090,7 +2124,6 @@ "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", - "dev": true, "requires": { "mime-types": "~2.1.24", "negotiator": "0.6.2" @@ -2136,6 +2169,11 @@ "integrity": "sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg==", "dev": true }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" + }, "agent-base": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", @@ -2467,6 +2505,11 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" + }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -2577,8 +2620,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", - "dev": true + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" }, "async-settle": { "version": "1.0.0", @@ -3057,6 +3099,11 @@ "now-and-later": "^2.0.0" } }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" + }, "bail": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.4.tgz", @@ -3066,8 +3113,7 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "base": { "version": "0.11.2", @@ -3130,12 +3176,22 @@ } } }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" + }, "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", "dev": true }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" + }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -3151,6 +3207,14 @@ "tweetnacl": "^0.14.3" } }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "requires": { + "callsite": "1.0.0" + } + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -3163,6 +3227,11 @@ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", "dev": true }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" + }, "bluebird": { "version": "3.5.5", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", @@ -3246,7 +3315,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3486,6 +3554,17 @@ "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "cache-base": { @@ -3529,6 +3608,11 @@ "caller-callsite": "^2.0.0" } }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" + }, "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", @@ -4003,12 +4087,22 @@ "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.5.1.tgz", "integrity": "sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg==" }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "dev": true }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" + }, "compressible": { "version": "2.0.17", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", @@ -4053,8 +4147,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", @@ -4149,6 +4242,17 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "copy-descriptor": { @@ -4948,8 +5052,7 @@ "csstype": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.5.tgz", - "integrity": "sha512-JsTaiksRsel5n7XwqPAfB0l3TFKdpjW/kgAELf9vrb5adGA7UCPLajKK5s3nFrcFm3Rkyp/Qkgl73ENc1UY3cA==", - "dev": true + "integrity": "sha512-JsTaiksRsel5n7XwqPAfB0l3TFKdpjW/kgAELf9vrb5adGA7UCPLajKK5s3nFrcFm3Rkyp/Qkgl73ENc1UY3cA==" }, "cyclist": { "version": "0.2.2", @@ -5016,7 +5119,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, "requires": { "ms": "^2.1.1" } @@ -5196,6 +5298,15 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } } } }, @@ -5530,6 +5641,105 @@ "once": "^1.4.0" } }, + "engine.io": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.3.2.tgz", + "integrity": "sha512-AsaA9KG7cWPXWHp5FvHdDWY3AMWeZ8x+2pUVLcn71qE5AtAzgGbxuclOytygskw8XGmiQafTmnI9Bix3uihu2w==", + "requires": { + "accepts": "~1.3.4", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "ws": "~6.1.0" + }, + "dependencies": { + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "engine.io-client": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.2.tgz", + "integrity": "sha512-y0CPINnhMvPuwtqXfsGuWE8BB66+B6wTtCofQDRecMQPYX3MYUZXFNKDhdrSe3EVjgOu4V3rxdeqN/Tr91IgbQ==", + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~6.1.0", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "ws": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", + "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, "enhanced-resolve": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", @@ -5833,6 +6043,17 @@ "object-assign": "^4.0.1", "object-hash": "^1.1.4", "rimraf": "^2.6.1" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "eslint-module-utils": { @@ -6216,6 +6437,11 @@ "through": "^2.3.8" } }, + "eventemitter2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", + "integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI=" + }, "eventemitter3": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", @@ -6792,6 +7018,17 @@ "flatted": "^2.0.0", "rimraf": "2.6.3", "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "flatted": { @@ -6988,8 +7225,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { "version": "1.2.9", @@ -7567,11 +7803,29 @@ "assert-plus": "^1.0.0" } }, + "git-rev-sync": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/git-rev-sync/-/git-rev-sync-1.12.0.tgz", + "integrity": "sha1-RGhAbH5sO6TPRYeZnhrbKNnRr1U=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5", + "graceful-fs": "4.1.11", + "shelljs": "0.7.7" + }, + "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + } + } + }, "glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -8144,6 +8398,26 @@ "ansi-regex": "^2.0.0" } }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -8267,6 +8541,14 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "hoist-non-react-statics": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz", + "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==", + "requires": { + "react-is": "^16.7.0" + } + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -8621,14 +8903,12 @@ "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -8728,11 +9008,34 @@ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", "dev": true }, + "intl-format-cache": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/intl-format-cache/-/intl-format-cache-4.1.13.tgz", + "integrity": "sha512-d/1NI8ctPZsPJU3G6k0pIuydnebeck7hG+wIHJOJUJm3eZ2+3bcZCD34o/Mc18M5ZiEZei02xsMtp8g1IQc23A==" + }, + "intl-locales-supported": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/intl-locales-supported/-/intl-locales-supported-1.4.5.tgz", + "integrity": "sha512-D7oriM5x46rd7kNlSW0f9noIBegFr3ReIM6xlMpwH4lfIPD/zvBelPlCjR10IK16boGJG9lKccOvRAM8wzpbrA==" + }, + "intl-messageformat": { + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-6.1.9.tgz", + "integrity": "sha512-vnPujdP+gpWK/+GxWUo3nWRfrUj67/NAZoBNAyCvZ0fnfWh5jvIkKNr5ZZYewdZ7J3cEFLU6hYYgWztiCEFZGg==", + "requires": { + "intl-format-cache": "^4.1.13", + "intl-messageformat-parser": "^3.0.7" + } + }, + "intl-messageformat-parser": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-3.0.7.tgz", + "integrity": "sha512-L16VbbV3NFaiZV65XwOIH9fBe52TS2EkOR0k8Y4ratsgTE7KPEbcUCUrz/iEQwJo7BcWY4ohkZbeYZRgAiPR1Q==" + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, "requires": { "loose-envify": "^1.0.0" } @@ -9171,6 +9474,17 @@ "make-dir": "^2.1.0", "rimraf": "^2.6.3", "source-map": "^0.6.1" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "istanbul-reports": { @@ -11079,14 +11393,12 @@ "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", - "dev": true + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" }, "mime-types": { "version": "2.1.24", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "dev": true, "requires": { "mime-db": "1.40.0" } @@ -11124,7 +11436,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -11353,13 +11664,23 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "multicast-dns": { "version": "6.2.3", @@ -11444,8 +11765,7 @@ "negotiator": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", - "dev": true + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, "neo-async": { "version": "2.6.1", @@ -11709,6 +12029,11 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" + }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -11888,6 +12213,12 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "on-error-resume-next": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-error-resume-next/-/on-error-resume-next-1.1.0.tgz", + "integrity": "sha512-XhWMbmKV0+W95yLJjT1Z9zdkKiPUjDn63YYsji1pdvKqaa7pq4coeHaHEXPsa36SFlffOyOlPK/0rn6Njfb+LA==", + "dev": true + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -11907,7 +12238,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, "requires": { "wrappy": "1" } @@ -12207,6 +12537,22 @@ "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", "dev": true }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "requires": { + "better-assert": "~1.0.0" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -12240,8 +12586,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", @@ -17203,6 +17548,23 @@ "integrity": "sha512-X1Y+0jR47ImDVr54Ab6V9eGk0Hnu7fVWGeHQSOXHf/C2pF9c6uy3gef8QUeuUiWlNb0i08InPSE5a/KJzNzw1Q==", "dev": true }, + "react-intl": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-3.1.9.tgz", + "integrity": "sha512-mQxhcFsYsj7O+k2Gsf1p+QaGhEdr+g7v5hYaa88dqar1m1xdWldEJjZ4JK/waHTp4zhVPoco4NQ1JdPkhs8HkA==", + "requires": { + "@formatjs/intl-relativetimeformat": "^2.6.3", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/invariant": "^2.2.30", + "hoist-non-react-statics": "^3.3.0", + "intl-format-cache": "^4.1.10", + "intl-locales-supported": "^1.4.5", + "intl-messageformat": "^6.1.6", + "intl-messageformat-parser": "^3.0.7", + "invariant": "^2.1.1", + "shallow-equal": "^1.1.0" + } + }, "react-is": { "version": "16.8.6", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", @@ -17920,15 +18282,6 @@ "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=", "dev": true }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, "ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -18329,6 +18682,11 @@ } } }, + "shallow-equal": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.0.tgz", + "integrity": "sha512-Z21pVxR4cXsfwpMKMhCEIO1PCi5sp7KEp+CmOpBQ+E8GpHwKOw2sEzk7sgblM3d/j4z4gakoWEoPcjK0VJQogA==" + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -18356,6 +18714,17 @@ "jsonify": "~0.0.0" } }, + "shelljs": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz", + "integrity": "sha1-svXHfvlxSPS09uImguELuoZnz/E=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, "shellwords": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", @@ -18542,6 +18911,100 @@ "kind-of": "^3.2.0" } }, + "socket.io": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.2.0.tgz", + "integrity": "sha512-wxXrIuZ8AILcn+f1B4ez4hJTPG24iNgxBBDaJfT6MsyOhVYiTXWexGoPkd87ktJG8kQEcL/NBvRi64+9k4Kc0w==", + "requires": { + "debug": "~4.1.0", + "engine.io": "~3.3.1", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.2.0", + "socket.io-parser": "~3.3.0" + } + }, + "socket.io-adapter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" + }, + "socket.io-client": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz", + "integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==", + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "engine.io-client": "~3.3.1", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "socket.io-parser": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", + "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "sockjs": { "version": "0.3.19", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", @@ -19371,6 +19834,11 @@ "is-negated-glob": "^1.0.0" } }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" + }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -19729,6 +20197,23 @@ "integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==", "dev": true }, + "typescript-react-intl": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/typescript-react-intl/-/typescript-react-intl-0.4.0.tgz", + "integrity": "sha512-sCQN2YdWSaWNAK9bBezmqZStswfh0MMWQnIlBOn+iFBN4fE1m36fv1zqUaw33jTbQUjIaZ4N+NFbMfS/cnyIew==", + "dev": true, + "requires": { + "typescript": "^2.6.2" + }, + "dependencies": { + "typescript": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", + "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==", + "dev": true + } + } + }, "uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", @@ -20148,6 +20633,51 @@ "extsprintf": "^1.2.0" } }, + "version-from-git": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/version-from-git/-/version-from-git-1.1.1.tgz", + "integrity": "sha512-R8kAZ+EFJcqG5HGrfjk7bSBAWKB3AE/Dnh21llvNg6CWJIPJAKsYNLR7lvYrCwu33A3TU4UljPLpp577UxgL3w==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "commander": "^2.15.1", + "cross-spawn": "^6.0.5", + "git-rev-sync": "^1.12.0", + "on-error-resume-next": "^1.0.0", + "semver": "^5.5.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "vfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.0.0.tgz", @@ -20820,6 +21350,17 @@ "p-map": "^1.1.1", "pify": "^3.0.0", "rimraf": "^2.2.8" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "globby": { @@ -21268,8 +21809,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { "version": "1.0.3", @@ -21334,6 +21874,11 @@ "integrity": "sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==", "dev": true }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" + }, "xregexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", @@ -21426,6 +21971,11 @@ "buffer-crc32": "~0.2.3" } }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" + }, "zone.js": { "version": "0.7.6", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz", diff --git a/package.json b/package.json index f7a997832..b08e76cf1 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "version": "0.0.0-UNTRACKEDVERSION", "publisher": "__PUBLISHER__", "instrumentationKey": "__AIKEY__", + "icon": "assets/icon.png", "engines": { "vscode": "^1.34.0" }, @@ -12,99 +13,164 @@ "Other" ], "preview": true, - "homepage": "https://github.com/microsoft/vscode-python-embedded/blob/master/README.md", - "bugs": { - "url": "https://github.com/microsoft/vscode-python-embedded/issues" - }, - "license": "SEE LICENSE IN LICENSE.md", - "repository": { - "type": "git", - "url": "https://github.com/microsoft/vscode-python-embedded", - "vsce": "^1.57.1", - "vscode-nls": "^4.0.0" - }, + "license": "MIT", "activationEvents": [ - "onCommand:pacifica.openSimulator", - "onCommand:pacifica.runSimulator", - "onCommand:pacifica.newFile", - "onCommand:pacifica.runDevice", + "onCommand:deviceSimulatorExpress.openSerialMonitor", + "onCommand:deviceSimulatorExpress.openSimulator", + "onCommand:deviceSimulatorExpress.runSimulator", + "onCommand:deviceSimulatorExpress.newFile", + "onCommand:deviceSimulatorExpress.runDevice", + "onCommand:deviceSimulatorExpress.runSimulatorEditorButton", + "onCommand:deviceSimulatorExpress.selectSerialPort", "onDebug" ], "main": "./out/extension.js", "contributes": { "commands": [ { - "command": "pacifica.openSimulator", - "title": "%pacificaExtension.commands.openSimulator%", - "category": "%pacificaExtension.commands.label%", + "command": "deviceSimulatorExpress.changeBaudRate", + "title": "%deviceSimulatorExpressExtension.commands.changeBaudRate%", + "category": "%deviceSimulatorExpressExtension.commands.label%" + }, + { + "command": "deviceSimulatorExpress.closeSerialMonitor", + "title": "%deviceSimulatorExpressExtension.commands.closeSerialMonitor%", + "category": "%deviceSimulatorExpressExtension.commands.label%" + }, + { + "command": "deviceSimulatorExpress.openSerialMonitor", + "title": "%deviceSimulatorExpressExtension.commands.openSerialMonitor%", + "category": "%deviceSimulatorExpressExtension.commands.label%" + }, + { + "command": "deviceSimulatorExpress.openSimulator", + "title": "%deviceSimulatorExpressExtension.commands.openSimulator%", + "category": "%deviceSimulatorExpressExtension.commands.label%", "icon": { "light": "./assets/light-theme/open-simulator.svg", "dark": "./assets/dark-theme/open-simulator.svg" } }, { - "command": "pacifica.runSimulator", - "title": "%pacificaExtension.commands.runSimulator%", - "category": "%pacificaExtension.commands.label%", + "command": "deviceSimulatorExpress.runSimulator", + "title": "%deviceSimulatorExpressExtension.commands.runSimulator%", + "category": "%deviceSimulatorExpressExtension.commands.label%" + }, + { + "command": "deviceSimulatorExpress.runSimulatorEditorButton", + "title": "%deviceSimulatorExpressExtension.commands.runSimulator%", + "category": "%deviceSimulatorExpressExtension.commands.label%", "icon": { "light": "./assets/light-theme/run-on-simulator.svg", "dark": "./assets/dark-theme/run-on-simulator.svg" } }, { - "command": "pacifica.newFile", - "title": "%pacificaExtension.commands.newFile%", - "category": "%pacificaExtension.commands.label%" + "command": "deviceSimulatorExpress.newFile", + "title": "%deviceSimulatorExpressExtension.commands.newFile%", + "category": "%deviceSimulatorExpressExtension.commands.label%" }, { - "command": "pacifica.runDevice", - "title": "%pacificaExtension.commands.runDevice%", - "category": "%pacificaExtension.commands.label%", + "command": "deviceSimulatorExpress.runDevice", + "title": "%deviceSimulatorExpressExtension.commands.runDevice%", + "category": "%deviceSimulatorExpressExtension.commands.label%", "icon": { "light": "./assets/light-theme/save-to-board.svg", "dark": "./assets/dark-theme/save-to-board.svg" } + }, + { + "command": "deviceSimulatorExpress.selectSerialPort", + "title": "%deviceSimulatorExpressExtension.commands.selectSerialPort%", + "category": "%deviceSimulatorExpressExtension.commands.label%" + } + ], + "colors": [ + { + "id": "highContrastButtonBorderOverride.color", + "description": "Color for the high contrast border updated", + "defaults": { + "dark": "debugToolBar.background", + "light": "debugToolBar.background", + "highContrast": "#6FC3DF" + } + }, + { + "id": "badgeForegroundOverride", + "description": "Color that fixes the issue with midnight blue ", + "defaults": { + "dark": "#FFFFFF", + "light": "badge.foreground", + "highContrast": "#FFFFFF" + } } ], "menus": { + "commandPalette": [ + { + "command": "deviceSimulatorExpress.runSimulatorEditorButton", + "when": "false" + } + ], "editor/title": [ { - "when": "editorLangId==python && config.pacifica.showOpenIconInEditorTitleMenu", - "command": "pacifica.openSimulator", + "when": "editorLangId==python && config.deviceSimulatorExpress.showOpenIconInEditorTitleMenu", + "command": "deviceSimulatorExpress.openSimulator", "group": "navigation@1" }, { - "when": "editorLangId==python && config.pacifica.showSimulatorIconInEditorTitleMenu", - "command": "pacifica.runSimulator", + "when": "editorLangId==python && config.deviceSimulatorExpress.showSimulatorIconInEditorTitleMenu", + "command": "deviceSimulatorExpress.runSimulatorEditorButton", "group": "navigation@2" }, { - "when": "editorLangId==python && config.pacifica.showDeviceIconInEditorTitleMenu", - "command": "pacifica.runDevice", + "when": "editorLangId==python && config.deviceSimulatorExpress.showDeviceIconInEditorTitleMenu", + "command": "deviceSimulatorExpress.runDevice", "group": "navigation@3" } ] }, "configuration": { "type": "object", - "title": "%pacificaExtension.configuration.title%", + "title": "%deviceSimulatorExpressExtension.configuration.title%", "properties": { - "pacifica.showOpenIconInEditorTitleMenu": { + "deviceSimulatorExpress.enableUSBDetection": { + "type": "boolean", + "default": true + }, + "deviceSimulatorExpress.showOpenIconInEditorTitleMenu": { + "type": "boolean", + "default": true, + "description": "%deviceSimulatorExpressExtension.configuration.properties.open%", + "scope": "resource" + }, + "deviceSimulatorExpress.showSimulatorIconInEditorTitleMenu": { "type": "boolean", "default": true, - "description": "%pacificaExtension.configuration.properties.open%", + "description": "%deviceSimulatorExpressExtension.configuration.properties.simulator%", "scope": "resource" }, - "pacifica.showSimulatorIconInEditorTitleMenu": { + "deviceSimulatorExpress.showDeviceIconInEditorTitleMenu": { "type": "boolean", "default": true, - "description": "%pacificaExtension.configuration.properties.simulator%", + "description": "%deviceSimulatorExpressExtension.configuration.properties.device%", "scope": "resource" }, - "pacifica.showDeviceIconInEditorTitleMenu": { + "deviceSimulatorExpress.showDependencyInstall": { "type": "boolean", "default": true, - "description": "%pacificaExtension.configuration.properties.device%", + "scope": "resource" + }, + "pacifica.showNewFilePopup": { + "type": "boolean", + "default": true, + "scope": "resource" + }, + "deviceSimulatorExpress.debuggerServerPort": { + + "type": "number", + "default": 5577, + "description": "%deviceSimulatorExpressExtension.configuration.properties.debuggerPort%", "scope": "resource" } } @@ -116,10 +182,11 @@ ], "debuggers": [ { - "type": "python", - "label": "Pacifica Simulator Debugger", - "program": "./out/debugAdapter.js", - "runtime": "node", + "type": "deviceSimulatorExpress", + "label": "Device Simulator Express Debugger", + "languages": [ + "python" + ], "configurationAttributes": { "launch": { "properties": { @@ -131,18 +198,24 @@ "stopOnEntry": { "type": "boolean", "description": "Automatically stop after launch.", - "default": false - }, - "justMyCode": { - "type": "boolean", "default": true }, + "console": { + "enum": [ + "internalConsole", + "integratedTerminal", + "externalTerminal" + ], + "description": "Where to launch the debug target: internal console, integrated terminal, or external terminal.", + "default": "integratedTerminal" + }, "args": { "type": "array", "description": "Command line arguments passed to the program.", "default": [], "items": { - "type": "string" + "filePath": "string", + "serverPort": "string" } }, "rules": { @@ -159,25 +232,21 @@ }, "initialConfigurations": [ { - "type": "python", + "type": "deviceSimulatorExpress", "request": "launch", - "name": "Pacifica Simulator Debugger", - "program": "${file}", - "stopOnEntry": false, - "justMyCode": true + "name": "Device Simulator Express Debugger", + "console": "integratedTerminal" } ], "configurationSnippets": [ { - "label": "Pacifica Simulator Debugger : Launch", - "description": "Pacifica Simulator Debugger - A configuration for debugging a python code file for the Pacifica simulator.", + "label": "Device Simulator Express Debugger : Launch", + "description": "Device Simulator Express Debugger - A configuration for debugging a python code file for the Device Simulator Express simulator.", "body": { - "type": "python", + "type": "deviceSimulatorExpress", "request": "launch", - "name": "Pacifica Simulator Debugger", - "program": "${file}", - "stopOnEntry": false, - "justMyCode": true + "name": "Device Simulator Express Debugger", + "console": "integratedTerminal" } } ] @@ -213,7 +282,6 @@ "css-loader": "^1.0.0", "del": "^4.0.0", "event-stream": "^4.0.1", - "glob": "^7.1.4", "gulp": "^4.0.2", "gulp-cli": "^2.1.0", "gulp-filter": "^5.1.0", @@ -233,6 +301,8 @@ "tslint-react": "^3.6.0", "tslint-react-hooks": "^2.0.0", "typescript": "^3.3.1", + "typescript-react-intl": "^0.4.0", + "version-from-git": "^1.1.1", "vsce": "^1.47.0", "vscode-nls-dev": "^3.2.6", "vscode-test": "^1.0.0", @@ -241,11 +311,16 @@ }, "dependencies": { "@types/open": "^6.1.0", + "@types/socket.io": "^2.1.2", "compare-versions": "^3.5.1", + "eventemitter2": "^5.0.1", "open": "^6.4.0", + "glob": "^7.1.4", "os": "^0.1.1", "react": "^16.8.6", "react-dom": "^16.8.6", + "react-intl": "^3.1.9", + "socket.io": "^2.2.0", "svg-inline-react": "^3.1.0", "util": "^0.12.1", "vscode-extension-telemetry": "^0.1.1", diff --git a/package.nls.json b/package.nls.json index 4439331c7..49d2906a0 100644 --- a/package.nls.json +++ b/package.nls.json @@ -1,11 +1,16 @@ { - "pacificaExtension.commands.label": "Pacifica", - "pacificaExtension.commands.openSimulator": "Open Simulator", - "pacificaExtension.commands.runSimulator": "Run Simulator", - "pacificaExtension.commands.newFile": "New File", - "pacificaExtension.commands.runDevice": "Deploy to Device", - "pacificaExtension.configuration.title": "Pacfica configuration", - "pacificaExtension.configuration.properties.open": "Whether to show 'Open Simulator' icon in editor title menu.", - "pacificaExtension.configuration.properties.device": "Whether to show 'Run Device' icon in editor title menu.", - "pacificaExtension.configuration.properties.simulator": "Whether to show 'Run Simulator' icon in editor title menu." -} \ No newline at end of file + "deviceSimulatorExpressExtension.commands.changeBaudRate": "Change Baud Rate", + "deviceSimulatorExpressExtension.commands.closeSerialMonitor": "Close Serial Monitor", + "deviceSimulatorExpressExtension.commands.label": "Device Simulator Express", + "deviceSimulatorExpressExtension.commands.openSerialMonitor": "Open Serial Monitor", + "deviceSimulatorExpressExtension.commands.openSimulator": "Open Simulator", + "deviceSimulatorExpressExtension.commands.runSimulator": "Run Simulator", + "deviceSimulatorExpressExtension.commands.newFile": "New File", + "deviceSimulatorExpressExtension.commands.runDevice": "Deploy to Device", + "deviceSimulatorExpressExtension.commands.selectSerialPort": "Select Serial Port", + "deviceSimulatorExpressExtension.configuration.title": "Device Simulator Express configuration", + "deviceSimulatorExpressExtension.configuration.properties.open": "Whether to show 'Open Simulator' icon in editor title menu.", + "deviceSimulatorExpressExtension.configuration.properties.device": "Whether to show 'Run Device' icon in editor title menu.", + "deviceSimulatorExpressExtension.configuration.properties.simulator": "Whether to show 'Run Simulator' icon in editor title menu.", + "deviceSimulatorExpressExtension.configuration.properties.debuggerPort": "The port the Server will listen on for communication with the debugger." +} diff --git a/src/adafruit_circuitplayground/constants.py b/src/adafruit_circuitplayground/constants.py index e5bcb24ad..4474dc46b 100644 --- a/src/adafruit_circuitplayground/constants.py +++ b/src/adafruit_circuitplayground/constants.py @@ -7,6 +7,8 @@ INDEX_ERROR = "The index is not a valid number, you can access the Neopixels from 0 to 9." +MAC_OS = "darwin" + NOT_IMPLEMENTED_ERROR = "This method is not implemented by the simulator" NOT_SUITABLE_FILE_ERROR = "Your .wav file is not suitable for the Circuit Playground Express." @@ -14,3 +16,25 @@ PIXEL_RANGE_ERROR = "The pixel hexadicimal color value should be in range #000000 and #FFFFFF." VALID_PIXEL_ASSIGN_ERROR = "The pixel color value should be a tuple with three values between 0 and 255 or a hexadecimal color between 0x000000 and 0xFFFFFF." + +TELEMETRY_EVENT_NAMES = { + 'TAPPED': "API.TAPPED", + 'PLAY_FILE': "API.PLAY.FILE", + 'PLAY_TONE': "API.PLAY.TONE", + 'START_TONE': "API.START.TONE", + 'STOP_TONE': "API.STOP.TONE", + 'DETECT_TAPS': "API.DETECT.TAPS", + 'ADJUST_THRESHOLD': "API.ADJUST.THRESHOLD", + 'RED_LED': "API.RED.LED", + 'PIXELS': "API.PIXELS" +} +ERROR_SENDING_EVENT = "Error trying to send event to the process : " + +TIME_DELAY = 0.03 + +DEFAULT_PORT = "5577" + + +EVENTS_BUTTON_PRESS = ['button_a', 'button_b', 'switch'] +EVENTS_SENSOR_CHANGED = ['temperature', + 'light', 'motion_x', 'motion_y', 'motion_z'] diff --git a/src/adafruit_circuitplayground/debugger_communication_client.py b/src/adafruit_circuitplayground/debugger_communication_client.py new file mode 100644 index 000000000..b2867c53d --- /dev/null +++ b/src/adafruit_circuitplayground/debugger_communication_client.py @@ -0,0 +1,51 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import sys +import json +import socketio +from . import express +from . import constants as CONSTANTS + + +# Create Socket Client +sio = socketio.Client(reconnection_attempts=2) + +# TODO: Get port from process_user_code.py via childprocess communication + + +# Initialize connection +def init_connection(port=CONSTANTS.DEFAULT_PORT): + sio.connect('http://localhost:{}'.format(port)) + + +# Transfer the user's inputs to the API +def __update_api_state(data, expected_events): + try: + event_state = json.loads(data) + for event in expected_events: + express.cpx._Express__state[event] = event_state.get( + event, express.cpx._Express__state[event]) + except Exception as e: + print(CONSTANTS.ERROR_SENDING_EVENT, + e, file=sys.stderr, flush=True) + + +# Method : Update State +def update_state(state): + sio.emit('updateState', state) + + +## Events Handlers ## + + +# Event : Button pressed (A, B, A+B, Switch) +@sio.on('button_press') +def button_press(data): + __update_api_state(data, CONSTANTS.EVENTS_BUTTON_PRESS) + + +# Event : Sensor changed (Temperature, light, Motion) +@sio.on('sensor_changed') +def button_press(data): + __update_api_state(data, CONSTANTS.EVENTS_SENSOR_CHANGED) diff --git a/src/adafruit_circuitplayground/express.py b/src/adafruit_circuitplayground/express.py index eda8f6b57..9ceff5131 100644 --- a/src/adafruit_circuitplayground/express.py +++ b/src/adafruit_circuitplayground/express.py @@ -9,6 +9,8 @@ from . import utils from . import constants as CONSTANTS from collections import namedtuple +from applicationinsights import TelemetryClient +from .telemetry import telemetry_py Acceleration = namedtuple('acceleration', ['x', 'y', 'z']) @@ -40,13 +42,11 @@ def __init__(self): 'motion_y': 0, 'motion_z': 0, 'touch': [False]*7, - 'detect_taps': 1, - 'tapped': False, 'shake': False, } - - self.pixels = Pixel(self.__state) + self.__debug_mode = False self.__abs_path_to_code_file = '' + self.pixels = Pixel(self.__state, self.__debug_mode) @property def acceleration(self): @@ -62,6 +62,7 @@ def button_b(self): @property def detect_taps(self): + telemetry_py.send_telemetry("DETECT_TAPS") return self.__state['detect_taps'] @detect_taps.setter @@ -74,15 +75,17 @@ def detect_taps(self, value): def tapped(self): """ Not Implemented! """ - + telemetry_py.send_telemetry("TAPPED") raise NotImplementedError(CONSTANTS.NOT_IMPLEMENTED_ERROR) @property def red_led(self): + telemetry_py.send_telemetry("RED_LED") return self.__state['red_led'] @red_led.setter def red_led(self, value): + telemetry_py.send_telemetry("RED_LED") self.__state['red_led'] = bool(value) self.__show() @@ -99,7 +102,7 @@ def light(self): return self.__state['light'] def __show(self): - utils.show(self.__state) + utils.show(self.__state, self.__debug_mode) def __touch(self, i): return self.__state['touch'][i-1] @@ -134,20 +137,23 @@ def touch_A7(self): def adjust_touch_threshold(self, adjustement): """Not implemented! - The CPX Simulator doesn't use capacitive touch threshold. + The Pacifica Simulator doesn't use capacitive touch threshold. """ + telemetry_py.send_telemetry("ADJUST_THRESHOLD") raise NotImplementedError( - "this method is not supported by the simulator") + CONSTANTS.NOT_IMPLEMENTED_ERROR) def shake(self, shake_threshold=30): return self.__state['shake'] def play_file(self, file_name): + telemetry_py.send_telemetry("PLAY_FILE") file_name = utils.remove_leading_slashes(file_name) abs_path_parent_dir = os.path.abspath( os.path.join(self.__abs_path_to_code_file, os.pardir)) abs_path_wav_file = os.path.normpath( os.path.join(abs_path_parent_dir, file_name)) + abs_path_wav_file = utils.escape_if_OSX(abs_path_wav_file) if sys.implementation.version[0] >= 3: if file_name.endswith(".wav"): @@ -164,19 +170,21 @@ def play_file(self, file_name): def play_tone(self, frequency, duration): """ Not Implemented! """ + telemetry_py.send_telemetry("PLAY_TONE") raise NotImplementedError( CONSTANTS.NOT_IMPLEMENTED_ERROR) def start_tone(self, frequency): """ Not Implemented! """ + telemetry_py.send_telemetry("START_TONE") raise NotImplementedError( CONSTANTS.NOT_IMPLEMENTED_ERROR) def stop_tone(self): """ Not Implemented! """ - # Stop playing any tones. + telemetry_py.send_telemetry("STOP_TONE") raise NotImplementedError( CONSTANTS.NOT_IMPLEMENTED_ERROR) diff --git a/src/adafruit_circuitplayground/locale/en/LC_MESSAGES/express.po b/src/adafruit_circuitplayground/locale/en/LC_MESSAGES/express.po new file mode 100644 index 000000000..50579af3a --- /dev/null +++ b/src/adafruit_circuitplayground/locale/en/LC_MESSAGES/express.po @@ -0,0 +1,26 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-08-21 13:35-0700\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: express.py:190 +msgid " is not a path to a .wav file." +msgstr "" + +#: express.py:192 +msgid "Please use Python 3 or higher." +msgstr "" diff --git a/src/adafruit_circuitplayground/locale/express.pot b/src/adafruit_circuitplayground/locale/express.pot new file mode 100644 index 000000000..50579af3a --- /dev/null +++ b/src/adafruit_circuitplayground/locale/express.pot @@ -0,0 +1,26 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-08-21 13:35-0700\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: express.py:190 +msgid " is not a path to a .wav file." +msgstr "" + +#: express.py:192 +msgid "Please use Python 3 or higher." +msgstr "" diff --git a/src/adafruit_circuitplayground/pixel.py b/src/adafruit_circuitplayground/pixel.py index 27fb7021e..530dbd48b 100644 --- a/src/adafruit_circuitplayground/pixel.py +++ b/src/adafruit_circuitplayground/pixel.py @@ -5,28 +5,38 @@ import sys from . import constants as CONSTANTS from . import utils +from applicationinsights import TelemetryClient +from . import constants as CONSTANTS +from .telemetry import telemetry_py class Pixel: - def __init__(self, state): + def __init__(self, state, debug_mode=False): self.__state = state self.auto_write = True + self.__debug_mode = debug_mode + self.telemetry_state = False def show(self): # Send the state to the extension so that React re-renders the Webview - utils.show(self.__state) + utils.show(self.__state, self.__debug_mode) def __show_if_auto_write(self): if self.auto_write: self.show() + def __set_debug_mode(self, debug_mode): + self.__debug_mode = debug_mode + def __getitem__(self, index): if type(index) is not slice: if not self.__valid_index(index): raise IndexError(CONSTANTS.INDEX_ERROR) + telemetry_py.send_telemetry("PIXELS") return self.__state['pixels'][index] def __setitem__(self, index, val): + telemetry_py.send_telemetry("PIXELS") is_slice = False if type(index) is slice: is_slice = True @@ -76,7 +86,6 @@ def __extract_pixel_value(self, val, is_slice=False): if len(rgb_value) != 3 or any(not self.__valid_rgb_value(pix) for pix in rgb_value): raise ValueError(CONSTANTS.VALID_PIXEL_ASSIGN_ERROR) extracted_values.append(rgb_value) - return rgb_value if not is_slice else extracted_values def __hex_to_rgb(self, hexValue): @@ -86,7 +95,6 @@ def __hex_to_rgb(self, hexValue): hexToRgbValue[0] = int(hexColor[0:2], 16) # R hexToRgbValue[1] = int(hexColor[2:4], 16) # G hexToRgbValue[2] = int(hexColor[4:6], 16) # B - return tuple(hexToRgbValue) else: raise ValueError(CONSTANTS.PIXEL_RANGE_ERROR) diff --git a/src/adafruit_circuitplayground/telemetry.py b/src/adafruit_circuitplayground/telemetry.py new file mode 100644 index 000000000..98156469c --- /dev/null +++ b/src/adafruit_circuitplayground/telemetry.py @@ -0,0 +1,34 @@ +from . import constants as CONSTANTS +from applicationinsights import TelemetryClient + + +class Telemetry: + def __init__(self): + # State of the telemetry + self.__enable_telemetry = True + self.telemetry_state = { + "DETECT_TAPS": False, + "TAPPED": False, + "RED_LED": False, + "ADJUST_THRESHOLD": False, + "PLAY_FILE": False, + "PLAY_TONE": False, + "START_TONE": False, + "STOP_TONE": False, + "PIXELS": False + } + self.telemetry_client = TelemetryClient('__AIKEY__') + self.extension_name = '__EXTENSIONNAME__' + + def send_telemetry(self, event_name): + if self.__enable_telemetry and self.telemetry_available() and not self.telemetry_state[event_name]: + self.telemetry_client.track_event( + '{}/{}'.format(self.extension_name, CONSTANTS.TELEMETRY_EVENT_NAMES[event_name])) + self.telemetry_client.flush() + self.telemetry_state[event_name] = True + + def telemetry_available(self): + return self.telemetry_client.context.instrumentation_key != '__AIKEY__' + + +telemetry_py = Telemetry() diff --git a/src/adafruit_circuitplayground/utils.py b/src/adafruit_circuitplayground/utils.py index 0504628d1..83a595d3b 100644 --- a/src/adafruit_circuitplayground/utils.py +++ b/src/adafruit_circuitplayground/utils.py @@ -5,22 +5,32 @@ import json import copy import time +from . import constants as CONSTANTS +from . import debugger_communication_client +from applicationinsights import TelemetryClient +previous_state = {} -previousState = {} -TIME_DELAY = 0.03 - -def show(state): - global previousState - if state != previousState: +def show(state, debug_mode=False): + global previous_state + if state != previous_state: + previous_state = copy.deepcopy(state) message = {'type': 'state', 'data': json.dumps(state)} - print(json.dumps(message) + '\0', end='', - file=sys.__stdout__, flush=True) - previousState = copy.deepcopy(state) - time.sleep(TIME_DELAY) + if debug_mode: + debugger_communication_client.update_state(json.dumps(message)) + else: + print(json.dumps(message) + '\0', end='', + file=sys.__stdout__, flush=True) + time.sleep(CONSTANTS.TIME_DELAY) def remove_leading_slashes(string): string = string.lstrip('\\/') return string + + +def escape_if_OSX(file_name): + if sys.platform.startswith(CONSTANTS.MAC_OS): + file_name = file_name.replace(" ", "%20") + return file_name \ No newline at end of file diff --git a/src/constants.ts b/src/constants.ts index 0813e8983..692d13f55 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,20 +2,61 @@ // Licensed under the MIT license. import * as nls from "vscode-nls"; +import * as path from "path"; import { MessageItem } from "vscode"; +// Debugger Server +export const SERVER_INFO = { + DEFAULT_SERVER_PORT: 5577, + ERROR_CODE_INIT_SERVER: "ERROR_INIT_SERVER", + SERVER_PORT_CONFIGURATION: "deviceSimulatorExpress.debuggerServerPort" +}; + const localize: nls.LocalizeFunc = nls.config({ messageFormat: nls.MessageFormat.file })(); +export const CONFIG = { + SHOW_DEPENDENCY_INSTALL: "deviceSimulatorExpress.showDependencyInstall", + SHOW_NEW_FILE_POPUP: "pacifica.showNewFilePopup" +}; + export const CONSTANTS = { - DEBUG_CONFIGURATION_NAME: "Pacifica Simulator Debugger", + DEBUG_CONFIGURATION_TYPE: "deviceSimulatorExpress", DEPENDENCY_CHECKER: { - PYTHON: 'python', - PYTHON3: 'python3', - PYTHON_LAUNCHER: 'py -3' + PIP3: "pip3", + PYTHON: "python", + PYTHON3: "python3.7", }, ERROR: { + COMPORT_UNKNOWN_ERROR: + "Writing to COM port (GetOverlappedResult): Unknown error code 121", + CPX_FILE_ERROR: localize( + "error.cpxFileFormat", + "The cpx.json file format is not correct." + ), + DEBUGGER_SERVER_INIT_FAILED: (port: number) => { + return localize( + "error.debuggerServerInitFailed", + `Warning : The Debugger Server cannot be opened. Please try to free the port ${port} if it's already in use or select another one in your Settings 'Device Simulator Express: Debugger Server Port' and start another debug session.\n You can still debug your code but you won't be able to use the Simulator.` + ); + }, + DEBUGGING_SESSION_IN_PROGESS: localize( + "error.debuggingSessionInProgress", + "[ERROR] A debugging session is currently in progress, please stop it before running your code. \n" + ), + FAILED_TO_OPEN_SERIAL_PORT: (port: string): string => { + return localize( + "error.failedToOpenSerialPort", + `[ERROR] Failed to open serial port ${port}.` + ); + }, + FAILED_TO_OPEN_SERIAL_PORT_DUE_TO: (port: string, error: any) => { + return localize( + "error.failedToOpenSerialPortDueTo", + `[ERROR] Failed to open serial port ${port} due to error: ${error}. \n` + ); + }, INCORRECT_FILE_NAME_FOR_DEVICE: localize( "error.incorrectFileNameForDevice", '[ERROR] Can\'t deploy to your Circuit Playground Express device, please rename your file to "code.py" or "main.py". \n' @@ -36,11 +77,22 @@ export const CONSTANTS = { "error.noFileToRun", '[ERROR] We can\'t find a Python file to run. Please make sure you select or open a new ".py" code file, or use the "New File" command to get started and see useful links.\n' ), + NO_FOLDER_OPENED: localize( + "error.noFolderCreated", + "In order to use the Serial Monitor, you need to open a folder and reload VS Code." + ), NO_PROGRAM_FOUND_DEBUG: localize( "error.noProgramFoundDebug", "Cannot find a program to debug." ), - NO_PYTHON_PATH: localize("error.noPythonPath", "We found that you don't have Python 3 installed on your computer, please install the latest version, add it to your PATH and try again."), + NO_PYTHON_PATH: localize( + "error.noPythonPath", + "We found that you don't have Python 3 installed on your computer, please install the latest version, add it to your PATH and try again." + ), + RECONNECT_DEVICE: localize( + "error.reconnectDevice", + "Please disconnect your Circuit Playground Express and try again." + ), STDERR: (data: string) => { return localize("error.stderr", `\n[ERROR] ${data} \n`); }, @@ -50,7 +102,21 @@ export const CONSTANTS = { ) }, INFO: { + ARE_YOU_SURE: localize( + "info.areYouSure", + "Are you sure you don't want to install the dependencies? The extension can't run without installing it" + ), + CLOSED_SERIAL_PORT: (port: string) => { + return localize( + "info.closedSerialPort", + `[DONE] Closed the serial port - ${port} \n` + ); + }, COMPLETED_MESSAGE: "Completed", + CPX_JSON_ALREADY_GENERATED: localize( + "info.cpxJsonAlreadyGenerated", + "cpx.json has already been generated." + ), DEPLOY_DEVICE: localize( "info.deployDevice", "\n[INFO] Deploying code to the device...\n" @@ -67,7 +133,6 @@ export const CONSTANTS = { "info.extensionActivated", "Congratulations, your extension Adafruit_Simulator is now active!" ), - FILE_SELECTED: (filePath: string) => { return localize( "info.fileSelected", @@ -82,6 +147,14 @@ export const CONSTANTS = { "info.incorrectFileNameForSimulatorPopup", 'We want your code to work on your actual board as well. Make sure you name your file "code.py" or "main.py" to be able to run your code on an actual physical device' ), + INSTALLING_PYTHON_DEPENDENCIES: localize( + "info.installingPythonDependencies", + "The Python packages are currently being installed. You will be prompt a message telling you when the installation is done." + ), + INSTALL_PYTHON_DEPENDENCIES: localize( + "info.installPythonDependencies", + "Do you want us to try and install this extensions dependencies for you?" + ), INVALID_FILE_NAME_DEBUG: localize( "info.invalidFileNameDebug", 'The file you tried to debug isn\'t named "code.py" or "main.py". Rename your file if you want your code to work on your actual device.' @@ -90,11 +163,31 @@ export const CONSTANTS = { "info.newFile", "New to Python or the Circuit Playground Express? We are here to help!" ), + OPENED_SERIAL_PORT: (port: string) => { + return localize( + "info.openedSerialPort", + `[INFO] Opened the serial port - ${port} \n` + ); + }, + OPENING_SERIAL_PORT: (port: string) => { + return localize( + "info.openingSerialPort", + `[STARTING] Opening the serial port - ${port} \n` + ); + }, + PLEASE_OPEN_FOLDER: localize( + "info.pleaseOpenFolder", + "Please open a folder first." + ), REDIRECT: localize("info.redirect", "You are being redirected."), RUNNING_CODE: localize("info.runningCode", "Running user code"), + SUCCESSFUL_INSTALL: localize( + "info.successfulInstall", + "Successfully installed Python dependencies." + ), THIRD_PARTY_WEBSITE: localize( "info.thirdPartyWebsite", - "By clicking \"Agree and Proceed\" you will be redirected to adafruit.com, a third party website not managed by Microsoft. Please note that your activity on adafruit.com is subject to Adafruit's privacy policy", + 'By clicking "Agree and Proceed" you will be redirected to adafruit.com, a third party website not managed by Microsoft. Please note that your activity on adafruit.com is subject to Adafruit\'s privacy policy' ), WELCOME_OUTPUT_TAB: localize( "info.welcomeOutputTab", @@ -102,7 +195,7 @@ export const CONSTANTS = { ) }, LABEL: { - WEBVIEW_PANEL: localize("label.webviewPanel", "Adafruit CPX") + WEBVIEW_PANEL: localize("label.webviewPanel", "Device Simulator Express") }, LINKS: { DOWNLOAD_PYTHON: "https://www.python.org/downloads/", @@ -114,13 +207,59 @@ export const CONSTANTS = { TUTORIALS: "https://learn.adafruit.com/circuitpython-made-easy-on-circuit-playground-express/circuit-playground-express-library" }, - NAME: localize("name", "Pacifica Simulator"), + MISC: { + SELECT_PORT_PLACEHOLDER: localize( + "misc.selectPortPlaceholder", + "Select a serial port" + ), + SERIAL_MONITOR_NAME: localize( + "misc.serialMonitorName", + "Device Simulator Express Serial Monitor" + ), + SERIAL_MONITOR_TEST_IF_OPEN: localize( + "misc.testIfPortOpen", + "Test if serial port is open" + ) + }, + NAME: localize("name", "Device Simulator Express"), WARNING: { - ACCEPT_AND_RUN: localize("warning.agreeAndRun", "By selecting ‘Agree and Run’, you understand the extension executes Python code on your local computer, which may be a potential security risk."), + ACCEPT_AND_RUN: localize( + "warning.agreeAndRun", + "By selecting ‘Agree and Run’, you understand the extension executes Python code on your local computer, which may be a potential security risk." + ), + INVALID_BAUD_RATE: localize( + "warning.invalidBaudRate", + "Invalid baud rate, keep baud rate unchanged." + ), + NO_RATE_SELECTED: localize( + "warning.noRateSelected", + "No rate is selected, keep baud rate unchanged." + ), + NO_SERIAL_PORT_SELECTED: localize( + "warning.noSerialPortSelected", + "No serial port was selected, please select a serial port first" + ), + SERIAL_MONITOR_ALREADY_OPENED: (port: string) => { + return localize( + "warning.serialMonitorAlreadyOpened", + `Serial monitor is already opened for ${port} \n` + ); + }, + SERIAL_MONITOR_NOT_STARTED: localize( + "warning.serialMonitorNotStarted", + "Serial monitor has not been started." + ), + SERIAL_PORT_NOT_STARTED: localize( + "warning.serialPortNotStarted", + "Serial port has not been started.\n" + ) } }; -// Need the different events we want to track and the name of it +export enum CONFIG_KEYS { + ENABLE_USB_DETECTION = "deviceSimulatorExpress.enableUSBDetection" +} + export enum TelemetryEventName { FAILED_TO_OPEN_SIMULATOR = "SIMULATOR.FAILED_TO_OPEN", @@ -128,7 +267,13 @@ export enum TelemetryEventName { COMMAND_DEPLOY_DEVICE = "COMMAND.DEPLOY.DEVICE", COMMAND_NEW_FILE = "COMMAND.NEW.FILE", COMMAND_OPEN_SIMULATOR = "COMMAND.OPEN.SIMULATOR", - COMMAND_RUN_SIMULATOR = "COMMAND.RUN.SIMULATOR", + COMMAND_RUN_SIMULATOR_BUTTON = "COMMAND.RUN.SIMULATOR_BUTTON", + COMMAND_RUN_PALETTE = "COMMAND.RUN.PALETTE", + COMMAND_RUN_EDITOR_ICON = "COMMAND.RUN.EDITOR_ICON", + COMMAND_SERIAL_MONITOR_CHOOSE_PORT = "COMMAND.SERIAL_MONITOR.CHOOSE_PORT", + COMMAND_SERIAL_MONITOR_OPEN = "COMMAND.SERIAL_MONITOR.OPEN", + COMMAND_SERIAL_MONITOR_BAUD_RATE = "COMMAND.SERIAL_MONITOR.BAUD_RATE", + COMMAND_SERIAL_MONITOR_CLOSE = "COMMAND.SERIAL_MONITOR.CLOSE", // Simulator interaction SIMULATOR_BUTTON_A = "SIMULATOR.BUTTON.A", @@ -136,6 +281,13 @@ export enum TelemetryEventName { SIMULATOR_BUTTON_AB = "SIMULATOR.BUTTON.AB", SIMULATOR_SWITCH = "SIMULATOR.SWITCH", + //Sensors + SIMULATOR_TEMPERATURE_SENSOR = "SIMULATOR.TEMPERATURE", + SIMULATOR_LIGHT_SENSOR = " SIMULATOR.LIGHT", + SIMULATOR_MOTION_SENSOR = "SIMULATOR.MOTION", + SIMULATOR_SHAKE = "SIMULATOR.SHAKE", + SIMULATOR_CAPACITIVE_TOUCH = "SIMULATOR.CAPACITIVE.TOUCH", + // Pop-up dialog CLICK_DIALOG_DONT_SHOW = "CLICK.DIALOG.DONT.SHOW", CLICK_DIALOG_EXAMPLE_CODE = "CLICK.DIALOG.EXAMPLE.CODE", @@ -159,7 +311,8 @@ export enum WebviewMessages { BUTTON_PRESS = "button-press", PLAY_SIMULATOR = "play-simulator", SENSOR_CHANGED = "sensor-changed", - REFRESH_SIMULATOR = "refresh-simulator" + REFRESH_SIMULATOR = "refresh-simulator", + SLIDER_TELEMETRY = "slider-telemetry" } // tslint:disable-next-line: no-namespace @@ -172,13 +325,22 @@ export namespace DialogResponses { }; export const CANCEL: MessageItem = { title: localize("dialogResponses.cancel", "Cancel") - } + }; export const HELP: MessageItem = { title: localize("dialogResponses.help", "I need help") }; export const DONT_SHOW: MessageItem = { title: localize("dialogResponses.dontShowAgain", "Don't Show Again") }; + export const NO: MessageItem = { + title: localize("dialogResponses.No", "No") + }; + export const INSTALL_NOW: MessageItem = { + title: localize("dialogResponses.installNow", "Install Now") + }; + export const DONT_INSTALL: MessageItem = { + title: localize("dialogResponses.dontInstall", "Don't Install") + }; export const PRIVACY_STATEMENT: MessageItem = { title: localize("info.privacyStatement", "Privacy Statement") }; @@ -193,12 +355,23 @@ export namespace DialogResponses { }; export const INSTALL_PYTHON: MessageItem = { title: localize("dialogResponses.installPython", "Install from python.org") - } + }; + export const YES: MessageItem = { + title: localize("dialogResponses.Yes", "Yes") + }; } +export const CPX_CONFIG_FILE = path.join(".vscode", "cpx.json"); + export const USER_CODE_NAMES = { CODE_PY: "code.py", MAIN_PY: "main.py" }; +export const STATUS_BAR_PRIORITY = { + PORT: 20, + OPEN_PORT: 30, + BAUD_RATE: 40 +}; + export default CONSTANTS; diff --git a/src/cpxWorkspace.ts b/src/cpxWorkspace.ts new file mode 100644 index 000000000..b1276a35b --- /dev/null +++ b/src/cpxWorkspace.ts @@ -0,0 +1,22 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as vscode from "vscode"; + +export class CPXWorkspace { + static get rootPath(): string|undefined { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length === 0) { + return undefined; + } + + for (const workspaceFolder of workspaceFolders) { + const workspaceFolderPath = workspaceFolder.uri.fsPath; + const cpxConfigPath = path.join(workspaceFolderPath, ".vscode", "cpx.json"); + if (fs.existsSync(cpxConfigPath)) { + return workspaceFolderPath; + } + } + + return workspaceFolders[0].uri.fsPath; + } +} diff --git a/src/debug_user_code.py b/src/debug_user_code.py new file mode 100644 index 000000000..3a5297c21 --- /dev/null +++ b/src/debug_user_code.py @@ -0,0 +1,67 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT license. + +import os +import sys +import traceback +from pathlib import Path +import python_constants as CONSTANTS + + + +# Insert absolute path to Adafruit library into sys.path +abs_path_to_parent_dir = os.path.dirname(os.path.abspath(__file__)) +abs_path_to_lib = os.path.join(abs_path_to_parent_dir, CONSTANTS.LIBRARY_NAME) +sys.path.insert(0, abs_path_to_lib) + +# Insert absolute path to python libraries into sys.path +abs_path_to_parent_dir = os.path.dirname(os.path.abspath(__file__)) +abs_path_to_lib = os.path.join( + abs_path_to_parent_dir, CONSTANTS.PYTHON_LIBS_DIR) +sys.path.insert(0, abs_path_to_lib) + +# This import must happen after the sys.path is modified +from adafruit_circuitplayground.express import cpx +from adafruit_circuitplayground import debugger_communication_client + + +## Execute User Code ## + + +# Get user's code path +abs_path_to_code_file = '' +if len(sys.argv) > 1 and sys.argv[1]: + abs_path_to_code_file = sys.argv[1] +else: + raise FileNotFoundError(CONSTANTS.ERROR_NO_FILE) + +# Get Debugger Server Port +server_port = CONSTANTS.DEFAULT_PORT +if len(sys.argv) > 2: + server_port = sys.argv[2] + +# Init Communication +debugger_communication_client.init_connection(server_port) + +# Init API variables +cpx._Express__abs_path_to_code_file = abs_path_to_code_file +cpx._Express__debug_mode = True +cpx.pixels._Pixel__set_debug_mode(True) + +# Execute the user's code file +with open(abs_path_to_code_file) as user_code_file: + user_code = user_code_file.read() + try: + codeObj = compile(user_code, abs_path_to_code_file, + CONSTANTS.EXEC_COMMAND) + exec(codeObj, {}) + sys.stdout.flush() + except Exception as e: + exc_type, exc_value, exc_traceback = sys.exc_info() + errorMessage = CONSTANTS.ERROR_TRACEBACK + stackTrace = traceback.format_exception( + exc_type, exc_value, exc_traceback) + + for frameIndex in range(2, len(stackTrace) - 1): + errorMessage += '\t' + str(stackTrace[frameIndex]) + print(e, errorMessage, file=sys.stderr, flush=True) diff --git a/src/debuggerCommunicationServer.ts b/src/debuggerCommunicationServer.ts new file mode 100644 index 000000000..23d429475 --- /dev/null +++ b/src/debuggerCommunicationServer.ts @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as http from "http"; +import * as socketio from "socket.io"; +import { WebviewPanel } from "vscode"; +import { SERVER_INFO } from "./constants"; + +export class DebuggerCommunicationServer { + private port: number; + private serverHttp: http.Server; + private serverIo: socketio.Server; + private simulatorWebview: WebviewPanel | undefined; + + constructor( + webviewPanel: WebviewPanel | undefined, + port = SERVER_INFO.DEFAULT_SERVER_PORT + ) { + this.port = port; + this.serverHttp = new http.Server(); + this.initHttpServer(); + + this.serverIo = socketio(this.serverHttp); + this.simulatorWebview = webviewPanel; + this.initEventsHandlers(); + console.info(`Server running on port ${this.port}`); + } + + public closeConnection(): void { + this.serverIo.close(); + this.serverHttp.close(); + console.info("Closing the server"); + } + + public setWebview(webviewPanel: WebviewPanel | undefined) { + this.simulatorWebview = webviewPanel; + } + + // Emit Buttons Inputs Events + public emitButtonPress(newState: string): void { + console.log(`Emit Button Press: ${newState} \n`); + this.serverIo.emit("button_press", newState); + } + + // Emit Sensors Inputs Events + public emitSensorChanged(newState: string): void { + console.log(`Emit Sensor Changed: ${newState} \n`); + this.serverIo.emit("sensor_changed", newState); + } + + private initHttpServer(): void { + this.serverHttp.listen(this.port); + if (!this.serverHttp.listening) { + throw new Error(SERVER_INFO.ERROR_CODE_INIT_SERVER); + } + } + + private initEventsHandlers(): void { + this.serverIo.on("connection", (socket: any) => { + console.log("Connection received"); + + socket.on("updateState", (data: any) => { + this.handleState(data); + }); + + socket.on("disconnect", () => { + console.log("Socket disconnected"); + if (this.simulatorWebview) { + this.simulatorWebview.webview.postMessage({ command: "reset-state" }); + } + }); + }); + } + + private handleState(data: any): void { + try { + const messageToWebview = JSON.parse(data); + if (messageToWebview.type === "state") { + console.log(`State recieved: ${messageToWebview.data}`); + if (this.simulatorWebview) { + this.simulatorWebview.webview.postMessage({ + command: "set-state", + state: JSON.parse(messageToWebview.data) + }); + } + } + } catch (err) { + console.error(`Error: Non-JSON output from the process : ${data}`); + } + } +} diff --git a/src/device.py b/src/device.py index 767ac96bb..5eca629ee 100644 --- a/src/device.py +++ b/src/device.py @@ -12,7 +12,7 @@ import win32api -class Adafruit: +class Device: def __init__(self): self.connected = False self.error_message = None @@ -58,7 +58,7 @@ def find_device_directory(self): if __name__ == "__main__": import shutil - cpx = Adafruit() + cpx = Device() device_directory = cpx.find_device_directory() if cpx.error_message: print("{}:\t{}".format( diff --git a/src/deviceContext.ts b/src/deviceContext.ts new file mode 100644 index 000000000..ebcb25814 --- /dev/null +++ b/src/deviceContext.ts @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Credit: A majority of this code was taken from the Visual Studio Code Arduino extension with some modifications to suit our purposes. + +import * as fs from "fs"; +import * as path from "path"; +import * as utils from "./extension_utils/utils"; +import * as vscode from "vscode"; +import { CPXWorkspace } from "./cpxWorkspace"; +import CONSTANTS, { CPX_CONFIG_FILE } from "./constants"; + +export class DeviceContext implements vscode.Disposable { + public static getInstance(): DeviceContext { + return DeviceContext._deviceContext; + } + + private static _deviceContext: DeviceContext = new DeviceContext(); + + private _onDidChange = new vscode.EventEmitter(); + private _watcher: vscode.FileSystemWatcher; + private _vscodeWatcher: vscode.FileSystemWatcher; + private _port!: string; + + private constructor() { + if (vscode.workspace && CPXWorkspace.rootPath) { + this._watcher = vscode.workspace.createFileSystemWatcher(path.join(CPXWorkspace.rootPath, CPX_CONFIG_FILE)); + this._vscodeWatcher = vscode.workspace.createFileSystemWatcher(path.join(CPXWorkspace.rootPath, ".vscode"), true, true, false); + + // Reloads the config into the code if the cpx config file has changed + this._watcher.onDidCreate(() => this.loadContext()); + this._watcher.onDidChange(() => this.loadContext()); + this._watcher.onDidDelete(() => this.loadContext()); + + this._vscodeWatcher.onDidDelete(() => this.loadContext()); + } + } + + public loadContext(): Thenable { + return vscode.workspace.findFiles(CPX_CONFIG_FILE, null, 1) + .then((files) => { + let cpxConfigJson: any = {}; + if (files && files.length > 0) { + const configFile = files[0]; + cpxConfigJson = utils.tryParseJSON(fs.readFileSync(configFile.fsPath, "utf8")); + if (cpxConfigJson) { + this._port = cpxConfigJson.port; + this._onDidChange.fire(); + } else { + console.error(CONSTANTS.ERROR.CPX_FILE_ERROR); + } + } else { + this._port = null; + this._onDidChange.fire(); + } + return this; + }, (reason) => { + this._port = null; + this._onDidChange.fire(); + return this; + }); + } + + public saveContext() { + if (!CPXWorkspace.rootPath) { + return; + } + const cpxConfigFile = path.join(CPXWorkspace.rootPath, CPX_CONFIG_FILE); + let cpxConfigJson: any = {}; + if (utils.fileExistsSync(cpxConfigFile)) { + cpxConfigJson = utils.tryParseJSON(fs.readFileSync(cpxConfigFile, "utf8")); + } + if (!cpxConfigJson) { + // log and notify user error + return; + } + cpxConfigJson.port = this.port; + + utils.mkdirRecursivelySync(path.dirname(cpxConfigFile)); + fs.writeFileSync(cpxConfigFile, JSON.stringify(cpxConfigJson, (key, value) => { + if (value === null) { + return undefined; + } + return value; + }, 4)); + } + + public dispose() { + if (this._watcher) { + this._watcher.dispose(); + } + + if (this._vscodeWatcher) { + this._vscodeWatcher.dispose(); + } + } + + public async initialize() { + if (CPXWorkspace.rootPath && utils.fileExistsSync(path.join(CPXWorkspace.rootPath, CPX_CONFIG_FILE))) { + vscode.window.showInformationMessage(CONSTANTS.INFO.CPX_JSON_ALREADY_GENERATED); + return; + } else { + if (!CPXWorkspace.rootPath) { + vscode.window.showInformationMessage(CONSTANTS.INFO.PLEASE_OPEN_FOLDER); + return; + } + } + } + + public get onDidChange(): vscode.Event { + return this._onDidChange.event; + } + + public get port() { + return this._port; + } + + public set port(value: string) { + this._port = value; + this.saveContext(); + } +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 20f6ae396..0a523c3f8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,30 +1,40 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import * as vscode from "vscode"; -import * as path from "path"; import * as cp from "child_process"; import * as fs from "fs"; import * as open from "open"; -import TelemetryAI from "./telemetry/telemetryAI"; +import * as path from "path"; +import * as utils from "./extension_utils/utils"; +import * as vscode from "vscode"; import { + CONFIG, CONSTANTS, + CPX_CONFIG_FILE, DialogResponses, TelemetryEventName, - WebviewMessages + WebviewMessages, + SERVER_INFO } from "./constants"; +import { CPXWorkspace } from "./cpxWorkspace"; import { SimulatorDebugConfigurationProvider } from "./simulatorDebugConfigurationProvider"; -import * as utils from "./extension_utils/utils"; +import { SerialMonitor } from "./serialMonitor"; +import TelemetryAI from "./telemetry/telemetryAI"; +import { UsbDetector } from "./usbDetector"; +import { DebuggerCommunicationServer } from "./debuggerCommunicationServer"; let currentFileAbsPath: string = ""; let currentTextDocument: vscode.TextDocument; let telemetryAI: TelemetryAI; let pythonExecutableName: string = "python"; +let configFileCreated: boolean = false; +let inDebugMode: boolean = false; +let debuggerCommunicationHandler: DebuggerCommunicationServer; // Notification booleans let firstTimeClosed: boolean = true; -let shouldShowNewFile: boolean = true; let shouldShowInvalidFileNamePopup: boolean = true; let shouldShowRunCodePopup: boolean = true; +export let outChannel: vscode.OutputChannel | undefined; function loadScript(context: vscode.ExtensionContext, scriptPath: string) { return ``; } +const setPathAndSendMessage = ( + currentPanel: vscode.WebviewPanel, + newFilePath: string +) => { + currentFileAbsPath = newFilePath; + if (currentPanel) { + currentPanel.webview.postMessage({ + command: "current-file", + state: { running_file: newFilePath } + }); + } +}; + // Extension activation export async function activate(context: vscode.ExtensionContext) { console.info(CONSTANTS.INFO.EXTENSION_ACTIVATED); telemetryAI = new TelemetryAI(context); let currentPanel: vscode.WebviewPanel | undefined; - let outChannel: vscode.OutputChannel | undefined; let childProcess: cp.ChildProcess | undefined; let messageListener: vscode.Disposable; + let activeEditorListener: vscode.Disposable; // Add our library path to settings.json for autocomplete functionality updatePythonExtraPaths(); pythonExecutableName = await utils.setPythonExectuableName(); + await utils.checkPythonDependencies(context, pythonExecutableName) + + // Generate cpx.json + try { + utils.generateCPXConfig(); + configFileCreated = true; + } catch (err) { + console.info("Failed to create the CPX config file."); + configFileCreated = false; + } + + if (pythonExecutableName === "") { return; } if (outChannel === undefined) { outChannel = vscode.window.createOutputChannel(CONSTANTS.NAME); - logToOutputChannel(outChannel, CONSTANTS.INFO.WELCOME_OUTPUT_TAB, true); + utils.logToOutputChannel(outChannel, CONSTANTS.INFO.WELCOME_OUTPUT_TAB); } - vscode.workspace.onDidSaveTextDocument(async (document: vscode.TextDocument) => { - await updateCurrentFileIfPython(document); - }); + vscode.workspace.onDidSaveTextDocument( + async (document: vscode.TextDocument) => { + await updateCurrentFileIfPython(document, currentPanel); + } + ); const openWebview = () => { if (currentPanel) { - currentPanel.reveal(vscode.ViewColumn.Two); + currentPanel.reveal(vscode.ViewColumn.Beside); } else { currentPanel = vscode.window.createWebviewPanel( "adafruitSimulator", CONSTANTS.LABEL.WEBVIEW_PANEL, - { preserveFocus: true, viewColumn: vscode.ViewColumn.Two }, + { preserveFocus: true, viewColumn: vscode.ViewColumn.Beside }, { // Only allow the webview to access resources in our extension's media directory localResourceRoots: [ @@ -87,6 +124,14 @@ export async function activate(context: vscode.ExtensionContext) { } } + if (activeEditorListener !== undefined) { + activeEditorListener.dispose(); + const index = context.subscriptions.indexOf(activeEditorListener); + if (index > -1) { + context.subscriptions.splice(index, 1); + } + } + if (currentPanel) { // Handle messages from webview messageListener = currentPanel.webview.onDidReceiveMessage( @@ -96,25 +141,42 @@ export async function activate(context: vscode.ExtensionContext) { case WebviewMessages.BUTTON_PRESS: // Send input to the Python process handleButtonPressTelemetry(message.text); - console.log("About to write"); - console.log(messageJson + "\n"); - if (childProcess) { + console.log(`About to write ${messageJson} \n`); + if (inDebugMode && debuggerCommunicationHandler) { + debuggerCommunicationHandler.emitButtonPress(messageJson); + } else if (childProcess) { childProcess.stdin.write(messageJson + "\n"); } break; case WebviewMessages.PLAY_SIMULATOR: - console.log("Play button"); - console.log(messageJson + "\n"); - if (message.text as boolean) { + console.log(`Play button ${messageJson} \n`); + if (message.text.state as boolean) { + setPathAndSendMessage( + currentPanel, + message.text.selected_file + ); + if (currentFileAbsPath) { + const foundDocument = utils.getActiveEditorFromPath( + currentFileAbsPath + ); + if (foundDocument !== undefined) { + currentTextDocument = foundDocument; + } + } + telemetryAI.trackFeatureUsage( + TelemetryEventName.COMMAND_RUN_SIMULATOR_BUTTON + ); runSimulatorCommand(); } else { killProcessIfRunning(); } break; case WebviewMessages.SENSOR_CHANGED: - console.log("sensor changed"); - console.log(messageJson + "\n"); - if (childProcess) { + checkForTelemetry(message.text); + console.log(`Sensor changed ${messageJson} \n`); + if (inDebugMode && debuggerCommunicationHandler) { + debuggerCommunicationHandler.emitSensorChanged(messageJson); + } else if (childProcess) { childProcess.stdin.write(messageJson + "\n"); } break; @@ -122,6 +184,9 @@ export async function activate(context: vscode.ExtensionContext) { console.log("Refresh button"); runSimulatorCommand(); break; + case WebviewMessages.SLIDER_TELEMETRY: + handleSensorTelemetry(message.text); + break; default: vscode.window.showInformationMessage( CONSTANTS.ERROR.UNEXPECTED_MESSAGE @@ -132,11 +197,20 @@ export async function activate(context: vscode.ExtensionContext) { undefined, context.subscriptions ); + + activeEditorListener = utils.addVisibleTextEditorCallback( + currentPanel, + context + ); + console.log("sent"); } currentPanel.onDidDispose( () => { currentPanel = undefined; + if (debuggerCommunicationHandler) { + debuggerCommunicationHandler.setWebview(undefined); + } killProcessIfRunning(); if (firstTimeClosed) { vscode.window.showInformationMessage( @@ -153,7 +227,7 @@ export async function activate(context: vscode.ExtensionContext) { // Open Simulator on the webview const openSimulator: vscode.Disposable = vscode.commands.registerCommand( - "pacifica.openSimulator", + "deviceSimulatorExpress.openSimulator", () => { telemetryAI.trackFeatureUsage(TelemetryEventName.COMMAND_OPEN_SIMULATOR); telemetryAI.runWithLatencyMeasure( @@ -167,8 +241,9 @@ export async function activate(context: vscode.ExtensionContext) { const fileName = "template.py"; const filePath = __dirname + path.sep + fileName; const file = fs.readFileSync(filePath, "utf8"); + const showNewFilePopup: boolean = vscode.workspace.getConfiguration().get(CONFIG.SHOW_NEW_FILE_POPUP); - if (shouldShowNewFile) { + if (showNewFilePopup) { vscode.window .showInformationMessage( CONSTANTS.INFO.NEW_FILE, @@ -178,7 +253,7 @@ export async function activate(context: vscode.ExtensionContext) { ) .then((selection: vscode.MessageItem | undefined) => { if (selection === DialogResponses.DONT_SHOW) { - shouldShowNewFile = false; + vscode.workspace.getConfiguration().update(CONFIG.SHOW_NEW_FILE_POPUP, false); telemetryAI.trackFeatureUsage( TelemetryEventName.CLICK_DIALOG_DONT_SHOW ); @@ -217,7 +292,7 @@ export async function activate(context: vscode.ExtensionContext) { }; const newFile: vscode.Disposable = vscode.commands.registerCommand( - "pacifica.newFile", + "deviceSimulatorExpress.newFile", () => { telemetryAI.trackFeatureUsage(TelemetryEventName.COMMAND_NEW_FILE); telemetryAI.runWithLatencyMeasure( @@ -240,6 +315,13 @@ export async function activate(context: vscode.ExtensionContext) { }; const runSimulatorCommand = async () => { + // Prevent running new code if a debug session is active + if (inDebugMode) { + vscode.window.showErrorMessage( + CONSTANTS.ERROR.DEBUGGING_SESSION_IN_PROGESS + ); + return; + } if (shouldShowRunCodePopup) { const shouldExitCommand = await vscode.window .showWarningMessage( @@ -268,21 +350,39 @@ export async function activate(context: vscode.ExtensionContext) { } console.info(CONSTANTS.INFO.RUNNING_CODE); - telemetryAI.trackFeatureUsage(TelemetryEventName.COMMAND_RUN_SIMULATOR); - logToOutputChannel(outChannel, CONSTANTS.INFO.DEPLOY_SIMULATOR); + utils.logToOutputChannel(outChannel, CONSTANTS.INFO.DEPLOY_SIMULATOR, true); killProcessIfRunning(); - await updateCurrentFileIfPython(vscode.window.activeTextEditor!.document); + await updateCurrentFileIfPython( + vscode.window.activeTextEditor!.document, + currentPanel + ); if (currentFileAbsPath === "") { - logToOutputChannel(outChannel, CONSTANTS.ERROR.NO_FILE_TO_RUN, true); + utils.logToOutputChannel( + outChannel, + CONSTANTS.ERROR.NO_FILE_TO_RUN, + true + ); + vscode.window.showErrorMessage( + CONSTANTS.ERROR.NO_FILE_TO_RUN, + DialogResponses.MESSAGE_UNDERSTOOD + ); } else { // Save on run await currentTextDocument.save(); - logToOutputChannel( + if (!currentTextDocument.fileName.endsWith(".py")) { + utils.logToOutputChannel( + outChannel, + CONSTANTS.ERROR.NO_FILE_TO_RUN, + true + ); + return; + } + utils.logToOutputChannel( outChannel, CONSTANTS.INFO.FILE_SELECTED(currentFileAbsPath) ); @@ -308,9 +408,13 @@ export async function activate(context: vscode.ExtensionContext) { }); } + // Activate the run webview button + currentPanel.webview.postMessage({ command: "activate-play" }); + childProcess = cp.spawn(pythonExecutableName, [ utils.getPathToScript(context, "out", "process_user_code.py"), - currentFileAbsPath + currentFileAbsPath, + JSON.stringify({ enable_telemetry: utils.getTelemetryState() }) ]); let dataFromTheProcess = ""; @@ -343,10 +447,10 @@ export async function activate(context: vscode.ExtensionContext) { case "print": console.log( `Process print statement output = ${ - messageToWebview.data + messageToWebview.data }` ); - logToOutputChannel( + utils.logToOutputChannel( outChannel, `[PRINT] ${messageToWebview.data}` ); @@ -370,7 +474,11 @@ export async function activate(context: vscode.ExtensionContext) { childProcess.stderr.on("data", data => { console.error(`Error from the Python process through stderr: ${data}`); telemetryAI.trackFeatureUsage(TelemetryEventName.ERROR_PYTHON_PROCESS); - logToOutputChannel(outChannel, CONSTANTS.ERROR.STDERR(data), true); + utils.logToOutputChannel( + outChannel, + CONSTANTS.ERROR.STDERR(data), + true + ); if (currentPanel) { console.log("Sending clearing state command"); currentPanel.webview.postMessage({ command: "reset-state" }); @@ -384,10 +492,19 @@ export async function activate(context: vscode.ExtensionContext) { } }; + const runSimulatorEditorButton: vscode.Disposable = vscode.commands.registerCommand( + "deviceSimulatorExpress.runSimulatorEditorButton", + () => { + telemetryAI.trackFeatureUsage(TelemetryEventName.COMMAND_RUN_EDITOR_ICON); + runSimulatorCommand(); + } + ); + // Send message to the webview const runSimulator: vscode.Disposable = vscode.commands.registerCommand( - "pacifica.runSimulator", + "deviceSimulatorExpress.runSimulator", () => { + telemetryAI.trackFeatureUsage(TelemetryEventName.COMMAND_RUN_PALETTE); runSimulatorCommand(); } ); @@ -395,17 +512,28 @@ export async function activate(context: vscode.ExtensionContext) { const deployCodeToDevice = async () => { console.info("Sending code to device"); - logToOutputChannel(outChannel, CONSTANTS.INFO.DEPLOY_DEVICE); + utils.logToOutputChannel(outChannel, CONSTANTS.INFO.DEPLOY_DEVICE, true); - await updateCurrentFileIfPython(vscode.window.activeTextEditor!.document); + await updateCurrentFileIfPython( + vscode.window.activeTextEditor!.document, + currentPanel + ); if (currentFileAbsPath === "") { - logToOutputChannel(outChannel, CONSTANTS.ERROR.NO_FILE_TO_RUN, true); + utils.logToOutputChannel( + outChannel, + CONSTANTS.ERROR.NO_FILE_TO_RUN, + true + ); + vscode.window.showErrorMessage( + CONSTANTS.ERROR.NO_FILE_TO_RUN, + DialogResponses.MESSAGE_UNDERSTOOD + ); } else if (!utils.validCodeFileName(currentFileAbsPath)) { // Save on run await currentTextDocument.save(); // Output panel - logToOutputChannel( + utils.logToOutputChannel( outChannel, CONSTANTS.ERROR.INCORRECT_FILE_NAME_FOR_DEVICE, true @@ -415,7 +543,7 @@ export async function activate(context: vscode.ExtensionContext) { CONSTANTS.ERROR.INCORRECT_FILE_NAME_FOR_DEVICE_POPUP ); } else { - logToOutputChannel( + utils.logToOutputChannel( outChannel, CONSTANTS.INFO.FILE_SELECTED(currentFileAbsPath) ); @@ -440,7 +568,10 @@ export async function activate(context: vscode.ExtensionContext) { telemetryAI.trackFeatureUsage( TelemetryEventName.SUCCESS_COMMAND_DEPLOY_DEVICE ); - logToOutputChannel(outChannel, CONSTANTS.INFO.DEPLOY_SUCCESS); + utils.logToOutputChannel( + outChannel, + CONSTANTS.INFO.DEPLOY_SUCCESS + ); break; case "no-device": @@ -487,7 +618,7 @@ export async function activate(context: vscode.ExtensionContext) { console.error( `Error from the Python device process through stderr: ${data}` ); - logToOutputChannel(outChannel, `[ERROR] ${data} \n`, true); + utils.logToOutputChannel(outChannel, `[ERROR] ${data} \n`, true); }); // When the process is done @@ -498,7 +629,7 @@ export async function activate(context: vscode.ExtensionContext) { }; const runDevice: vscode.Disposable = vscode.commands.registerCommand( - "pacifica.runDevice", + "deviceSimulatorExpress.runDevice", () => { telemetryAI.trackFeatureUsage(TelemetryEventName.COMMAND_DEPLOY_DEVICE); telemetryAI.runWithLatencyMeasure( @@ -508,20 +639,161 @@ export async function activate(context: vscode.ExtensionContext) { } ); + let serialMonitor: SerialMonitor | undefined; + if (configFileCreated) { + serialMonitor = SerialMonitor.getInstance(); + context.subscriptions.push(serialMonitor); + } + + const selectSerialPort: vscode.Disposable = vscode.commands.registerCommand( + "deviceSimulatorExpress.selectSerialPort", + () => { + if (serialMonitor) { + telemetryAI.runWithLatencyMeasure(() => { + serialMonitor.selectSerialPort(null, null); + }, TelemetryEventName.COMMAND_SERIAL_MONITOR_CHOOSE_PORT); + } else { + vscode.window.showErrorMessage(CONSTANTS.ERROR.NO_FOLDER_OPENED); + console.info("Serial monitor is not defined."); + } + } + ); + + const openSerialMonitor: vscode.Disposable = vscode.commands.registerCommand( + "deviceSimulatorExpress.openSerialMonitor", + () => { + if (serialMonitor) { + telemetryAI.runWithLatencyMeasure( + serialMonitor.openSerialMonitor.bind(serialMonitor), + TelemetryEventName.COMMAND_SERIAL_MONITOR_OPEN + ); + } else { + vscode.window.showErrorMessage(CONSTANTS.ERROR.NO_FOLDER_OPENED); + console.info("Serial monitor is not defined."); + } + } + ); + + const changeBaudRate: vscode.Disposable = vscode.commands.registerCommand( + "deviceSimulatorExpress.changeBaudRate", + () => { + if (serialMonitor) { + telemetryAI.runWithLatencyMeasure( + serialMonitor.changeBaudRate.bind(serialMonitor), + TelemetryEventName.COMMAND_SERIAL_MONITOR_BAUD_RATE + ); + } else { + vscode.window.showErrorMessage(CONSTANTS.ERROR.NO_FOLDER_OPENED); + console.info("Serial monitor is not defined."); + } + } + ); + + const closeSerialMonitor: vscode.Disposable = vscode.commands.registerCommand( + "deviceSimulatorExpress.closeSerialMonitor", + (port, showWarning = true) => { + if (serialMonitor) { + telemetryAI.runWithLatencyMeasure(() => { + serialMonitor.closeSerialMonitor(port, showWarning); + }, TelemetryEventName.COMMAND_SERIAL_MONITOR_CLOSE); + } else { + vscode.window.showErrorMessage(CONSTANTS.ERROR.NO_FOLDER_OPENED); + console.info("Serial monitor is not defined."); + } + } + ); + + UsbDetector.getInstance().initialize(context.extensionPath); + UsbDetector.getInstance().startListening(); + + if ( + CPXWorkspace.rootPath && + (utils.fileExistsSync(path.join(CPXWorkspace.rootPath, CPX_CONFIG_FILE)) || + vscode.window.activeTextEditor) + ) { + (() => { + if (!SerialMonitor.getInstance().initialized) { + SerialMonitor.getInstance().initialize(); + } + })(); + } + // Debugger configuration const simulatorDebugConfiguration = new SimulatorDebugConfigurationProvider( - utils.getPathToScript(context, "out", "process_user_code.py") + utils.getPathToScript(context, "out/", "debug_user_code.py") ); + // On Debug Session Start: Init comunication + const debugSessionsStarted = vscode.debug.onDidStartDebugSession(() => { + if (simulatorDebugConfiguration.deviceSimulatorExpressDebug) { + // Reinitialize process + killProcessIfRunning(); + console.log("Debug Started"); + inDebugMode = true; + + try { + // Shut down existing server on debug restart + if (debuggerCommunicationHandler) { + debuggerCommunicationHandler.closeConnection(); + debuggerCommunicationHandler = undefined; + } + + debuggerCommunicationHandler = new DebuggerCommunicationServer( + currentPanel, + utils.getServerPortConfig() + ); + openWebview(); + if (currentPanel) { + debuggerCommunicationHandler.setWebview(currentPanel); + currentPanel.webview.postMessage({ command: "activate-play" }); + } + } catch (err) { + if (err.message === SERVER_INFO.ERROR_CODE_INIT_SERVER) { + console.error( + `Error trying to init the server on port ${utils.getServerPortConfig()}` + ); + vscode.window.showErrorMessage( + CONSTANTS.ERROR.DEBUGGER_SERVER_INIT_FAILED( + utils.getServerPortConfig() + ) + ); + } + } + } + }); + + // On Debug Session Stop: Stop communiation + const debugSessionStopped = vscode.debug.onDidTerminateDebugSession(() => { + if (simulatorDebugConfiguration.deviceSimulatorExpressDebug) { + console.log("Debug Stopped"); + inDebugMode = false; + simulatorDebugConfiguration.deviceSimulatorExpressDebug = false; + if (debuggerCommunicationHandler) { + debuggerCommunicationHandler.closeConnection(); + debuggerCommunicationHandler = undefined; + } + if (currentPanel) { + currentPanel.webview.postMessage({ command: "reset-state" }); + } + } + }); + context.subscriptions.push( + changeBaudRate, + closeSerialMonitor, + openSerialMonitor, openSimulator, + newFile, runSimulator, + runSimulatorEditorButton, runDevice, - newFile, + selectSerialPort, vscode.debug.registerDebugConfigurationProvider( - "python", + CONSTANTS.DEBUG_CONFIGURATION_TYPE, simulatorDebugConfiguration - ) + ), + debugSessionsStarted, + debugSessionStopped ); } @@ -531,41 +803,29 @@ const getActivePythonFile = () => { editor => editor.document.languageId === "python" ); if (activeEditor) { - currentTextDocument = activeEditor.document + currentTextDocument = activeEditor.document; } return activeEditor ? activeEditor.document.fileName : ""; }; -const getFileFromFilePicker = () => { - const options: vscode.OpenDialogOptions = { - canSelectMany: false, - filters: { - "All files": ["*"], - "Python files": ["py"] - }, - openLabel: "Run File" - }; - - return vscode.window.showOpenDialog(options).then(fileUri => { - if (fileUri && fileUri[0] && fileUri[0].fsPath.endsWith(".py")) { - console.log(`Selected file: ${fileUri[0].fsPath}`); - return fileUri[0].fsPath; - } - }); -}; - const updateCurrentFileIfPython = async ( - activeTextDocument: vscode.TextDocument | undefined + activeTextDocument: vscode.TextDocument | undefined, + currentPanel: vscode.WebviewPanel ) => { if (activeTextDocument && activeTextDocument.languageId === "python") { - currentFileAbsPath = activeTextDocument.fileName; + setPathAndSendMessage(currentPanel, activeTextDocument.fileName); currentTextDocument = activeTextDocument; } else if (currentFileAbsPath === "") { - currentFileAbsPath = - getActivePythonFile() || (await getFileFromFilePicker()) || ""; + setPathAndSendMessage(currentPanel, getActivePythonFile() || ""); } - if (currentFileAbsPath) { - await vscode.window.showTextDocument(currentTextDocument, vscode.ViewColumn.One); + if ( + currentTextDocument && + utils.getActiveEditorFromPath(currentTextDocument.fileName) === undefined + ) { + await vscode.window.showTextDocument( + currentTextDocument, + vscode.ViewColumn.One + ); } }; @@ -581,6 +841,44 @@ const handleButtonPressTelemetry = (buttonState: any) => { } }; +const handleSensorTelemetry = (sensor: string) => { + switch (sensor) { + case "temperature": + telemetryAI.trackFeatureUsage( + TelemetryEventName.SIMULATOR_TEMPERATURE_SENSOR + ); + break; + case "light": + telemetryAI.trackFeatureUsage(TelemetryEventName.SIMULATOR_LIGHT_SENSOR); + break; + case "motion_x": + telemetryAI.trackFeatureUsage(TelemetryEventName.SIMULATOR_MOTION_SENSOR); + break; + case "motion_y": + telemetryAI.trackFeatureUsage(TelemetryEventName.SIMULATOR_MOTION_SENSOR); + break; + case "motion_z": + telemetryAI.trackFeatureUsage(TelemetryEventName.SIMULATOR_MOTION_SENSOR); + break; + case "shake": + telemetryAI.trackFeatureUsage(TelemetryEventName.SIMULATOR_SHAKE); + break; + case "touch": + telemetryAI.trackFeatureUsage( + TelemetryEventName.SIMULATOR_CAPACITIVE_TOUCH + ); + break; + } +}; + +const checkForTelemetry = (sensorState: any) => { + if (sensorState["shake"]) { + handleSensorTelemetry("shake"); + } else if (sensorState["touch"]) { + handleSensorTelemetry("touch"); + } +}; + const updatePythonExtraPaths = () => { const pathToLib: string = __dirname; const currentExtraPaths: string[] = @@ -598,19 +896,6 @@ const updatePythonExtraPaths = () => { ); }; -const logToOutputChannel = ( - outChannel: vscode.OutputChannel | undefined, - message: string, - show: boolean = false -) => { - if (outChannel) { - if (show) { - outChannel.show(true); - } - outChannel.append(message); - } -}; - function getWebviewContent(context: vscode.ExtensionContext) { return ` @@ -632,4 +917,8 @@ function getWebviewContent(context: vscode.ExtensionContext) { } // this method is called when your extension is deactivated -export function deactivate() { } +export async function deactivate() { + const monitor: SerialMonitor = SerialMonitor.getInstance(); + await monitor.closeSerialMonitor(null, false); + UsbDetector.getInstance().stopListening(); +} diff --git a/src/extension_utils/dependencyChecker.ts b/src/extension_utils/dependencyChecker.ts index 363608568..4881dc8e8 100644 --- a/src/extension_utils/dependencyChecker.ts +++ b/src/extension_utils/dependencyChecker.ts @@ -16,6 +16,7 @@ interface IDependency { } const PYTHON3_REGEX = RegExp("^(Python )(3\\.[0-9]+\\.[0-9]+)"); +const MINIMUM_PYTHON_VERSION = "3.7.0" export class DependencyChecker { constructor() { } @@ -23,24 +24,14 @@ export class DependencyChecker { public async checkDependency(dependencyName: string): Promise { let state: boolean = false; if (dependencyName === CONSTANTS.DEPENDENCY_CHECKER.PYTHON) { - const userOS: string = os.platform(); - const userOnWin: boolean = userOS.indexOf("win") === 0; if ( - await this.runPythonVersionCommand(CONSTANTS.DEPENDENCY_CHECKER.PYTHON3) + await this.runCommandVersion(CONSTANTS.DEPENDENCY_CHECKER.PYTHON3, MINIMUM_PYTHON_VERSION) ) { state = true; dependencyName = CONSTANTS.DEPENDENCY_CHECKER.PYTHON3; } else if ( - await this.runPythonVersionCommand(CONSTANTS.DEPENDENCY_CHECKER.PYTHON) - ) { - state = true; - dependencyName = CONSTANTS.DEPENDENCY_CHECKER.PYTHON; - } else if ( - userOnWin && - (await this.runPythonVersionCommand( - CONSTANTS.DEPENDENCY_CHECKER.PYTHON_LAUNCHER - )) + await this.runCommandVersion(CONSTANTS.DEPENDENCY_CHECKER.PYTHON, MINIMUM_PYTHON_VERSION) ) { state = true; dependencyName = CONSTANTS.DEPENDENCY_CHECKER.PYTHON; @@ -56,12 +47,16 @@ export class DependencyChecker { }; } - private async runPythonVersionCommand(command: string) { - let installed: boolean; + private async runCommandVersion(command: string, versionDependency?: string) { + let installed: boolean = false; try { const { stdout } = await exec(command + " --version"); const matches = PYTHON3_REGEX.exec(stdout); - installed = matches ? compareVersions(matches[2], "3.5.0") >= 0 : false; + if (versionDependency) { + installed = matches ? compareVersions(matches[2], versionDependency) >= 0 : false; + } else { + installed = true + } } catch (err) { installed = false; } diff --git a/src/extension_utils/utils.ts b/src/extension_utils/utils.ts index 5cecd57d5..546bb8538 100644 --- a/src/extension_utils/utils.ts +++ b/src/extension_utils/utils.ts @@ -1,18 +1,32 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { ExtensionContext, MessageItem, Uri, window } from "vscode"; +import * as fs from "fs"; import * as path from "path"; -import { CONSTANTS, DialogResponses, USER_CODE_NAMES } from "../constants"; +import * as os from "os"; import { DependencyChecker } from "./dependencyChecker"; +import { DeviceContext } from "../deviceContext"; +import * as vscode from "vscode"; +import { + CONFIG, + CONSTANTS, + CPX_CONFIG_FILE, + DialogResponses, + USER_CODE_NAMES, + SERVER_INFO +} from "../constants"; +import { CPXWorkspace } from "../cpxWorkspace"; +import * as cp from "child_process"; +import * as util from "util"; +const exec = util.promisify(cp.exec); // tslint:disable-next-line: export-name export const getPathToScript = ( - context: ExtensionContext, + context: vscode.ExtensionContext, folderName: string, fileName: string ) => { - const onDiskPath = Uri.file( + const onDiskPath = vscode.Uri.file( path.join(context.extensionPath, folderName, fileName) ); const scriptPath = onDiskPath.with({ scheme: "vscode-resource" }); @@ -27,25 +41,138 @@ export const validCodeFileName = (filePath: string) => { }; export const showPrivacyModal = (okAction: () => void) => { - window.showInformationMessage( - `${CONSTANTS.INFO.THIRD_PARTY_WEBSITE}: ${CONSTANTS.LINKS.PRIVACY}`, - DialogResponses.AGREE_AND_PROCEED, - DialogResponses.CANCEL, - ) - .then((privacySelection: MessageItem | undefined) => { + vscode.window + .showInformationMessage( + `${CONSTANTS.INFO.THIRD_PARTY_WEBSITE}: ${CONSTANTS.LINKS.PRIVACY}`, + DialogResponses.AGREE_AND_PROCEED, + DialogResponses.CANCEL + ) + .then((privacySelection: vscode.MessageItem | undefined) => { if (privacySelection === DialogResponses.AGREE_AND_PROCEED) { okAction(); } else if (privacySelection === DialogResponses.CANCEL) { // do nothing } }); +}; + +export const logToOutputChannel = ( + outChannel: vscode.OutputChannel | undefined, + message: string, + show: boolean = false +): void => { + if (outChannel) { + if (show) { + outChannel.show(true); + } + outChannel.append(message); + } +}; + +export function tryParseJSON(jsonString: string): any | boolean { + try { + const jsonObj = JSON.parse(jsonString); + if (jsonObj && typeof jsonObj === "object") { + return jsonObj; + } + } catch (exception) {} + + return false; +} + +export function fileExistsSync(filePath: string): boolean { + try { + return fs.statSync(filePath).isFile(); + } catch (error) { + return false; + } +} + +export function mkdirRecursivelySync(dirPath: string): void { + if (directoryExistsSync(dirPath)) { + return; + } + const dirname = path.dirname(dirPath); + if (path.normalize(dirname) === path.normalize(dirPath)) { + fs.mkdirSync(dirPath); + } else if (directoryExistsSync(dirname)) { + fs.mkdirSync(dirPath); + } else { + mkdirRecursivelySync(dirname); + fs.mkdirSync(dirPath); + } +} + +export function directoryExistsSync(dirPath: string): boolean { + try { + return fs.statSync(dirPath).isDirectory(); + } catch (e) { + return false; + } +} + +/** + * This method pads the current string with another string (repeated, if needed) + * so that the resulting string reaches the given length. + * The padding is applied from the start (left) of the current string. + */ +export function padStart( + sourceString: string, + targetLength: number, + padString?: string +): string { + if (!sourceString) { + return sourceString; + } + + if (!(String.prototype as any).padStart) { + // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js + padString = String(padString || " "); + if (sourceString.length > targetLength) { + return sourceString; + } else { + targetLength = targetLength - sourceString.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); // append to original to ensure we are longer than needed + } + return padString.slice(0, targetLength) + sourceString; + } + } else { + return (sourceString as any).padStart(targetLength, padString); + } } +export function convertToHex(num: number, width = 0): string { + return padStart(num.toString(16), width, "0"); +} + +export function generateCPXConfig(): void { + const deviceContext: DeviceContext = DeviceContext.getInstance(); + const cpxJson = { + port: deviceContext.port + }; + const cpxConfigFilePath: string = path.join( + CPXWorkspace.rootPath, + CPX_CONFIG_FILE + ); + mkdirRecursivelySync(path.dirname(cpxConfigFilePath)); + fs.writeFileSync(cpxConfigFilePath, JSON.stringify(cpxJson, null, 4)); +} export const checkPythonDependency = async () => { const dependencyChecker: DependencyChecker = new DependencyChecker(); - const result = await dependencyChecker.checkDependency(CONSTANTS.DEPENDENCY_CHECKER.PYTHON); + const result = await dependencyChecker.checkDependency( + CONSTANTS.DEPENDENCY_CHECKER.PYTHON + ); return result.payload; -} +}; + +export const checkPipDependency = async () => { + const dependencyChecker: DependencyChecker = new DependencyChecker(); + const result = await dependencyChecker.checkDependency( + CONSTANTS.DEPENDENCY_CHECKER.PIP3 + ); + return result.payload; +}; export const setPythonExectuableName = async () => { // Find our what command is the PATH for python @@ -54,9 +181,12 @@ export const setPythonExectuableName = async () => { if (dependencyCheck.installed) { executableName = dependencyCheck.dependency; } else { - window.showErrorMessage(CONSTANTS.ERROR.NO_PYTHON_PATH, - DialogResponses.INSTALL_PYTHON) - .then((selection: MessageItem | undefined) => { + vscode.window + .showErrorMessage( + CONSTANTS.ERROR.NO_PYTHON_PATH, + DialogResponses.INSTALL_PYTHON + ) + .then((selection: vscode.MessageItem | undefined) => { if (selection === DialogResponses.INSTALL_PYTHON) { const okAction = () => { open(CONSTANTS.LINKS.DOWNLOAD_PYTHON); @@ -67,4 +197,124 @@ export const setPythonExectuableName = async () => { } return executableName; +}; + +export const addVisibleTextEditorCallback = ( + currentPanel: vscode.WebviewPanel, + context: vscode.ExtensionContext +): vscode.Disposable => { + const initialPythonEditors = filterForPythonFiles( + vscode.window.visibleTextEditors + ); + currentPanel.webview.postMessage({ + command: "visible-editors", + state: { activePythonEditors: initialPythonEditors } + }); + return vscode.window.onDidChangeVisibleTextEditors( + (textEditors: vscode.TextEditor[]) => { + const activePythonEditors = filterForPythonFiles(textEditors); + currentPanel.webview.postMessage({ + command: "visible-editors", + state: { activePythonEditors } + }); + }, + {}, + context.subscriptions + ); +}; + +export const filterForPythonFiles = (textEditors: vscode.TextEditor[]) => { + return textEditors + .filter(editor => editor.document.languageId === "python") + .map(editor => editor.document.fileName); +}; + +export const getActiveEditorFromPath = ( + filePath: string +): vscode.TextDocument => { + const activeEditor = vscode.window.visibleTextEditors.find( + (editor: vscode.TextEditor) => editor.document.fileName === filePath + ); + return activeEditor ? activeEditor.document : undefined; +}; + +export const getServerPortConfig = (): number => { + // tslint:disable: no-backbone-get-set-outside-model prefer-type-cast + if ( + vscode.workspace + .getConfiguration() + .has(SERVER_INFO.SERVER_PORT_CONFIGURATION) + ) { + return vscode.workspace + .getConfiguration() + .get(SERVER_INFO.SERVER_PORT_CONFIGURATION) as number; + } + return SERVER_INFO.DEFAULT_SERVER_PORT; +}; + +export const checkConfig = (configName: string): boolean => { + return vscode.workspace.getConfiguration().get(configName) === true; +}; + +export const checkPythonDependencies = async (context: vscode.ExtensionContext, pythonExecutable: string) => { + let hasInstalledDependencies: boolean = false; + if (checkPipDependency() && checkPythonDependency()) { + if (checkConfig(CONFIG.SHOW_DEPENDENCY_INSTALL)) { + hasInstalledDependencies = await promptInstallPythonDependencies(context, pythonExecutable); + if (hasInstalledDependencies) { + await vscode.workspace + .getConfiguration() + .update(CONFIG.SHOW_DEPENDENCY_INSTALL, false); + } + } + } else { + hasInstalledDependencies = false; + } + return hasInstalledDependencies; +}; + +export const promptInstallPythonDependencies = (context: vscode.ExtensionContext, pythonExecutable: string) => { + return vscode.window.showInformationMessage( + CONSTANTS.INFO.INSTALL_PYTHON_DEPENDENCIES, + DialogResponses.YES, + DialogResponses.NO) + .then((selection: vscode.MessageItem | undefined) => { + if (selection === DialogResponses.YES) { + return installPythonDependencies(context, pythonExecutable); + } else if (selection === DialogResponses.NO) { + return vscode.window.showInformationMessage( + CONSTANTS.INFO.ARE_YOU_SURE, + DialogResponses.INSTALL_NOW, + DialogResponses.DONT_INSTALL + ).then((installChoice: vscode.MessageItem | undefined) => { + if (installChoice === DialogResponses.INSTALL_NOW) { + return installPythonDependencies(context, pythonExecutable); + } else { + return false; + } + }) + } + }); +}; +export const getTelemetryState = () => { + return vscode.workspace + .getConfiguration() + .get("telemetry.enableTelemetry", true); +}; + +export const installPythonDependencies = async (context: vscode.ExtensionContext, pythonExecutable: string) => { + let installed: boolean = false; + try { + vscode.window.showInformationMessage(CONSTANTS.INFO.INSTALLING_PYTHON_DEPENDENCIES); + const requirementsPath: string = getPathToScript(context, "out", "requirements.txt"); + const pathToLibs: string = getPathToScript(context, "out", "python_libs"); + const { stdout } = await exec(`${pythonExecutable} -m pip install -r ${requirementsPath} -t ${pathToLibs}`); + console.info(stdout); + installed = true; + vscode.window.showInformationMessage(CONSTANTS.INFO.SUCCESSFUL_INSTALL); + } catch (err) { + console.error(err); + installed = false; + } + return installed } diff --git a/src/process_user_code.py b/src/process_user_code.py index 94b01f116..ad2fcac7b 100644 --- a/src/process_user_code.py +++ b/src/process_user_code.py @@ -10,7 +10,12 @@ import traceback import python_constants as CONSTANTS from pathlib import Path -from adafruit_circuitplayground.express import cpx + +# Insert absolute path to python libraries into sys.path +abs_path_to_parent_dir = os.path.dirname(os.path.abspath(__file__)) +abs_path_to_lib = os.path.join( + abs_path_to_parent_dir, CONSTANTS.PYTHON_LIBS_DIR) +sys.path.insert(0, abs_path_to_lib) read_val = "" threads = [] @@ -24,6 +29,11 @@ abs_path_to_parent_dir, CONSTANTS.LIBRARY_NAME) sys.path.insert(0, abs_path_to_lib) +# This import must happen after the sys.path is modified +from adafruit_circuitplayground.express import cpx +from adafruit_circuitplayground.telemetry import telemetry_py + + # Handle User Inputs Thread class UserInput(threading.Thread): @@ -32,7 +42,6 @@ def __init__(self): threading.Thread.__init__(self) def run(self): - from adafruit_circuitplayground.express import cpx while True: read_val = sys.stdin.readline() sys.stdin.flush() @@ -72,8 +81,8 @@ def handle_user_prints(): def execute_user_code(abs_path_to_code_file): cpx._Express__abs_path_to_code_file = abs_path_to_code_file # Execute the user's code.py file - with open(abs_path_to_code_file) as file: - user_code = file.read() + with open(abs_path_to_code_file) as user_code_file: + user_code = user_code_file.read() try: codeObj = compile(user_code, abs_path_to_code_file, CONSTANTS.EXEC_COMMAND) @@ -91,6 +100,9 @@ def execute_user_code(abs_path_to_code_file): user_code = threading.Thread(args=(sys.argv[1],), target=execute_user_code) +telemetry_state = json.loads(sys.argv[2]) +telemetry_py._Telemetry__enable_telemetry = telemetry_state.get( + CONSTANTS.ENABLE_TELEMETRY, True) threads.append(user_code) user_code.start() diff --git a/src/python_constants.py b/src/python_constants.py index 20f33cf3f..25837377b 100644 --- a/src/python_constants.py +++ b/src/python_constants.py @@ -3,7 +3,7 @@ CPX_DRIVE_NAME = "CIRCUITPY" - +ENABLE_TELEMETRY = 'enable_telemetry' EXPECTED_INPUT_EVENTS = [ "button_a", "button_b", @@ -13,12 +13,14 @@ "shake", "motion_x", "motion_y", - "motion_z" + "motion_z", + "touch" ] EXEC_COMMAND = "exec" ERROR_SENDING_EVENT = "Error trying to send event to the process : " ERROR_TRACEBACK = "\n\tTraceback of code execution : \n" +ERROR_NO_FILE = "Error : No file was passed to the process to execute.\n" LIBRARY_NAME = "adafruit_circuitplayground" LINUX_OS = "linux" @@ -31,6 +33,10 @@ NOT_SUPPORTED_OS = 'The OS "{}" not supported.' NOT_IMPLEMENTED_ERROR = "This method is not implemented by the simulator" +PYTHON_LIBS_DIR = "python_libs" + UTF_FORMAT = "utf-8" WINDOWS_OS = "win32" + +DEFAULT_PORT = "5577" diff --git a/src/requirements.txt b/src/requirements.txt index 1b57f28bf..b39b0bc1e 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,3 +1,6 @@ -playsound -pytest - +playsound==1.2.2 +pytest==5.0.1 +applicationinsights==0.11.9 +python-socketio==4.3.1 +requests==2.22.0 +pywin32==224 diff --git a/src/serialMonitor.ts b/src/serialMonitor.ts new file mode 100644 index 000000000..a022ddb1b --- /dev/null +++ b/src/serialMonitor.ts @@ -0,0 +1,230 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// Credit: A majority of this code was taken from the Visual Studio Code Arduino extension with some modifications to suit our purposes. + +import * as vscode from "vscode"; +import { outChannel } from "./extension"; +import { logToOutputChannel } from "./extension_utils/utils"; +import { DeviceContext } from "./deviceContext"; +import CONSTANTS, { STATUS_BAR_PRIORITY, DialogResponses } from "./constants"; +import { SerialPortControl } from "./serialPortControl"; + +export interface ISerialPortDetail { + comName: string; + manufacturer: string; + vendorId: string; + productId: string; +} + +export class SerialMonitor implements vscode.Disposable { + + public static DEFAULT_BAUD_RATE: number = 115200; + + public static listBaudRates(): number[] { + return [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 250000]; + } + + public static getInstance(): SerialMonitor { + if (SerialMonitor._serialMonitor === null) { + SerialMonitor._serialMonitor = new SerialMonitor(); + } + return SerialMonitor._serialMonitor; + } + + private static _serialMonitor: SerialMonitor | null = null; + + private _currentPort!: string; + private _currentBaudRate!: number; + private _outputChannel!: vscode.OutputChannel; + private _serialPortControl: SerialPortControl | null = null; + private _baudRateStatusBar!: vscode.StatusBarItem; + private _openPortStatusBar!: vscode.StatusBarItem; + private _portsStatusBar!: vscode.StatusBarItem; + + private constructor() { + const deviceContext = DeviceContext.getInstance(); + deviceContext.onDidChange(() => { + if (deviceContext.port) { + if (!this.initialized) { + this.initialize(); + } + this.updatePortListStatus(null); + } + }); + } + + public initialize() { + const defaultBaudRate: number = SerialMonitor.DEFAULT_BAUD_RATE; + this._outputChannel = vscode.window.createOutputChannel(CONSTANTS.MISC.SERIAL_MONITOR_NAME); + this._currentBaudRate = defaultBaudRate; + this._portsStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, STATUS_BAR_PRIORITY.PORT); + this._portsStatusBar.command = "deviceSimulatorExpress.selectSerialPort"; + this._portsStatusBar.tooltip = "Select Serial Port"; + this._portsStatusBar.show(); + + this._openPortStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, STATUS_BAR_PRIORITY.OPEN_PORT); + this._openPortStatusBar.command = "deviceSimulatorExpress.openSerialMonitor"; + this._openPortStatusBar.text = `$(plug)`; + this._openPortStatusBar.tooltip = "Open Serial Monitor"; + this._openPortStatusBar.show(); + + this._baudRateStatusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, STATUS_BAR_PRIORITY.BAUD_RATE); + this._baudRateStatusBar.command = "deviceSimulatorExpress.changeBaudRate"; + this._baudRateStatusBar.tooltip = "Baud Rate"; + this._baudRateStatusBar.text = defaultBaudRate.toString(); + this.updatePortListStatus(null); + } + + public async selectSerialPort(vid: string | null, pid: string | null) { + const lists = await SerialPortControl.list(); + if (!lists.length) { + vscode.window.showInformationMessage("No serial message is available."); + return; + } + + if (vid && pid) { + const valueOfVid = parseInt(vid, 16); + const valueOfPid = parseInt(pid, 16); + const foundPort = lists.find((port) => { + if (port.productId && port.vendorId) { + return parseInt(port.productId, 16) === valueOfPid && parseInt(port.vendorId, 16) === valueOfVid; + } + return false; + }); + if (foundPort && !(this._serialPortControl && this._serialPortControl.isActive)) { + this.updatePortListStatus(foundPort.comName); + } + } else { + const chosen = await vscode.window.showQuickPick(lists.map((port: ISerialPortDetail): vscode.QuickPickItem => { + return { + description: port.manufacturer, + label: port.comName + }; + }).sort((a, b): number => { + return a.label === b.label ? 0 : (a.label > b.label ? 1 : -1); + }) as vscode.QuickPickItem[], { placeHolder: CONSTANTS.MISC.SELECT_PORT_PLACEHOLDER}); + + if (chosen && chosen.label) { + this.updatePortListStatus(chosen.label); + } + } + } + + public async openSerialMonitor() { + if (!this._currentPort) { + const ans = await vscode.window.showInformationMessage( + CONSTANTS.WARNING.NO_SERIAL_PORT_SELECTED, + DialogResponses.YES, + DialogResponses.NO, + ); + if (ans === DialogResponses.YES) { + await this.selectSerialPort(null, null); + } + if (!this._currentPort) { + return; + } + } + + if (this._serialPortControl) { + if (this._currentPort !== this._serialPortControl.currentPort) { + await this._serialPortControl.changePort(this._currentPort); + } else if (this._serialPortControl.isActive) { + vscode.window.showWarningMessage(`Serial Monitor is already opened for ${this._currentPort}`); + return; + } + } else { + this._serialPortControl = new SerialPortControl(this._currentPort, this._currentBaudRate, this._outputChannel); + } + + if (!this._serialPortControl.currentPort) { + console.error(CONSTANTS.ERROR.FAILED_TO_OPEN_SERIAL_PORT(this._currentPort)); + return; + } + + try { + await this._serialPortControl.open(); + this.updatePortStatus(true); + } catch (error) { + logToOutputChannel(outChannel, CONSTANTS.ERROR.FAILED_TO_OPEN_SERIAL_PORT_DUE_TO(this._currentPort, error), true); + } + } + + public get initialized(): boolean { + return !!this._outputChannel; + } + + public dispose() { + if (this._serialPortControl && this._serialPortControl.isActive) { + return this._serialPortControl.stop(); + } + } + + public async changeBaudRate() { + const baudRates = SerialMonitor.listBaudRates(); + const chosen = await vscode.window.showQuickPick(baudRates.map((baudRate) => baudRate.toString())); + + if (!chosen) { + logToOutputChannel(outChannel, CONSTANTS.WARNING.NO_RATE_SELECTED, true); + return; + } + + if (!parseInt(chosen, 10)) { + logToOutputChannel(outChannel, CONSTANTS.WARNING.INVALID_BAUD_RATE, true); + return; + } + + if (!this._serialPortControl) { + logToOutputChannel(outChannel, CONSTANTS.WARNING.SERIAL_MONITOR_NOT_STARTED, true); + return; + } + + const selectedRate: number = parseInt(chosen, 10); + await this._serialPortControl.changeBaudRate(selectedRate); + this._currentBaudRate = selectedRate; + this._baudRateStatusBar.text = chosen; + } + + public async closeSerialMonitor(port: string, showWarning: boolean = true) { + if (this._serialPortControl) { + if (port && port !== this._serialPortControl.currentPort) { + // Port is not opened + return false; + } + const result = await this._serialPortControl.stop(); + this.updatePortStatus(false); + return result; + } else if (!port && showWarning) { + logToOutputChannel(outChannel, CONSTANTS.WARNING.SERIAL_PORT_NOT_STARTED, true); + return false; + } + } + + private updatePortStatus(isOpened: boolean) { + if (isOpened) { + this._openPortStatusBar.command = "deviceSimulatorExpress.closeSerialMonitor"; + this._openPortStatusBar.text = `$(x)`; + this._openPortStatusBar.tooltip = "Close Serial Monitor"; + this._baudRateStatusBar.show(); + } else { + this._openPortStatusBar.command = "deviceSimulatorExpress.openSerialMonitor"; + this._openPortStatusBar.text = `$(plug)`; + this._openPortStatusBar.tooltip = "Open Serial Monitor"; + this._baudRateStatusBar.hide(); + } + } + + private updatePortListStatus(port: string | null) { + const deviceContext = DeviceContext.getInstance(); + if (port) { + deviceContext.port = port; + } + this._currentPort = deviceContext.port; + + if (deviceContext.port) { + this._portsStatusBar.text = deviceContext.port; + } else { + this._portsStatusBar.text = " + + {renderOptions(props.textOptions)} + + + ); +}; + +const renderOptions = (options: string[]) => { + return options.map((name, index) => { + const key = `option-${index}`; + const parsedPath = parsePath(name); + return ( + + ); + }); +}; + +const parsePath = (filePath: string) => { + const lastSlash = + filePath.lastIndexOf("/") !== -1 + ? filePath.lastIndexOf("/") + : filePath.lastIndexOf("\\"); + return [filePath.slice(0, lastSlash), filePath.substr(lastSlash + 1)]; +}; + +export default Dropdown; diff --git a/src/view/components/Simulator.tsx b/src/view/components/Simulator.tsx index 6dd1c3814..29e25e5e5 100644 --- a/src/view/components/Simulator.tsx +++ b/src/view/components/Simulator.tsx @@ -5,6 +5,7 @@ import * as React from "react"; import { BUTTON_NEUTRAL, BUTTON_PRESSED } from "./cpx/Cpx_svg_style"; import Cpx, { updateSwitch, updatePinTouch } from "./cpx/Cpx"; import Button from "./Button"; +import Dropdown from "./Dropdown"; import { CONSTANTS } from "../constants"; import PlayLogo from "../svgs/play_svg"; import StopLogo from "../svgs/stop_svg"; @@ -20,9 +21,13 @@ interface ICpxState { button_b: boolean; switch: boolean; touch: boolean[]; + shake: boolean; } interface IState { + active_editors: string[]; + running_file: string; + selected_file: string; cpx: ICpxState; play_button: boolean; } @@ -48,11 +53,10 @@ const DEFAULT_CPX_STATE: ICpxState = { ], red_led: false, switch: false, - touch: [false, false, false, false, false, false, false] + touch: [false, false, false, false, false, false, false], + shake: false }; -const SIMULATOR_BUTTON_WIDTH = 60; - interface vscode { postMessage(message: any): void; } @@ -67,8 +71,11 @@ class Simulator extends React.Component { constructor(props: IMyProps) { super(props); this.state = { + active_editors: [], cpx: DEFAULT_CPX_STATE, - play_button: false + play_button: false, + running_file: "", + selected_file: "" }; this.handleClick = this.handleClick.bind(this); @@ -78,6 +85,7 @@ class Simulator extends React.Component { this.onMouseLeave = this.onMouseLeave.bind(this); this.togglePlayClick = this.togglePlayClick.bind(this); this.refreshSimulatorClick = this.refreshSimulatorClick.bind(this); + this.onSelectBlur = this.onSelectBlur.bind(this); } handleMessage = (event: any): void => { @@ -95,6 +103,26 @@ class Simulator extends React.Component { console.log("Setting the state: " + JSON.stringify(message.state)); this.setState({ ...this.state, cpx: message.state, play_button: true }); break; + case "activate-play": + this.setState({ ...this.state, play_button: !this.state.play_button }); + break; + case "visible-editors": + console.log( + "Setting active editors", + message.state.activePythonEditors + ); + this.setState({ + ...this.state, + active_editors: message.state.activePythonEditors + }); + break; + case "current-file": + console.log("Setting current file", message.state.running_file); + this.setState({ + ...this.state, + running_file: message.state.running_file + }); + break; default: console.log("Invalid message received from the extension."); this.setState({ ...this.state, cpx: DEFAULT_CPX_STATE }); @@ -116,7 +144,17 @@ class Simulator extends React.Component { const image = this.state.play_button ? StopLogo : PlayLogo; return (
-
+
+ +
+
{ image={image} styleLabel="play" label="play" - width={SIMULATOR_BUTTON_WIDTH} + width={CONSTANTS.SIMULATOR_BUTTON_WIDTH} />
@@ -152,8 +190,10 @@ class Simulator extends React.Component { } protected togglePlayClick() { - sendMessage("play-simulator", !this.state.play_button); - this.setState({ ...this.state, play_button: !this.state.play_button }); + sendMessage("play-simulator", { + selected_file: this.state.selected_file, + state: !this.state.play_button + }); const button = window.document.getElementById(CONSTANTS.ID_NAME.PLAY_BUTTON) || window.document.getElementById(CONSTANTS.ID_NAME.STOP_BUTTON); @@ -172,6 +212,9 @@ class Simulator extends React.Component { } } + protected onSelectBlur(event: React.FocusEvent) { + this.setState({ ...this.state, selected_file: event.currentTarget.value }); + } protected onKeyEvent(event: KeyboardEvent, active: boolean) { let element; const target = event.target as SVGElement; diff --git a/src/view/components/cpx/Cpx_svg.tsx b/src/view/components/cpx/Cpx_svg.tsx index ef295c1be..a1f8cf973 100644 --- a/src/view/components/cpx/Cpx_svg.tsx +++ b/src/view/components/cpx/Cpx_svg.tsx @@ -9,8 +9,7 @@ export const CPX_SVG = ( diff --git a/src/view/components/toolbar/InputSlider.tsx b/src/view/components/toolbar/InputSlider.tsx index 8aecc37ce..56eaba400 100644 --- a/src/view/components/toolbar/InputSlider.tsx +++ b/src/view/components/toolbar/InputSlider.tsx @@ -62,7 +62,7 @@ class InputSlider extends React.Component { onInput={this.handleOnChange} defaultValue={this.props.minValue.toLocaleString()} pattern="^-?[0-9]{0,3}$" - onKeyUp={this.validateRange} + onKeyUp={this.handleOnChange} aria-label={`${this.props.type} sensor input ${this.props.axisLabel}`} /> @@ -78,6 +78,8 @@ class InputSlider extends React.Component { min={this.props.minValue} max={this.props.maxValue} onChange={this.handleOnChange} + onKeyUp={this.sendTelemetry} + onMouseUp={this.sendTelemetry} aria-valuenow={this.state.value} value={this.state.value} aria-label={`${this.props.type} sensor slider`} @@ -92,36 +94,48 @@ class InputSlider extends React.Component { ); } - private handleOnChange(event: React.ChangeEvent) { - this.updateValue(event); - this.validateRange(); - const newSensorState = this.writeMessage(event); + private handleOnChange = (event: any) => { + const validatedValue = this.validateRange(this.updateValue(event)); + const newSensorState = this.writeMessage(validatedValue); if (newSensorState) { sendMessage(newSensorState); } - } + }; + + private writeMessage = (valueTowrite: number) => { + let value = valueTowrite; + if (value > this.props.maxValue || value < this.props.minValue) { + value = parseInt(this.state.value, 10); + } - private writeMessage(event: React.ChangeEvent) { - return this.props.type && this.state.value && event.target.value - ? { [this.props.type]: parseInt(event.target.value, 10) } + return this.props.type && this.state.value !== undefined + ? { [this.props.type]: value } : undefined; - } + }; - private updateValue(event: React.ChangeEvent) { + private updateValue = (event: any) => { const newValue = event.target.validity.valid ? event.target.value : this.state.value; this.setState({ value: newValue }); - } + return newValue; + }; - private validateRange() { - if (this.state.value < this.props.minValue) { - this.setState({ value: this.props.minValue }); - } - if (this.state.value > this.props.maxValue) { - this.setState({ value: this.props.maxValue }); + private sendTelemetry = () => { + vscode.postMessage({ command: "slider-telemetry", text: this.props.type }); + }; + + private validateRange = (valueString: string) => { + let valueInt = parseInt(valueString, 10); + if (valueInt < this.props.minValue) { + valueInt = this.props.minValue; + this.setState({ value: valueInt }); + } else if (valueInt > this.props.maxValue) { + valueInt = this.props.maxValue; + this.setState({ value: valueInt }); } - } + return valueInt; + }; } export default InputSlider; diff --git a/src/view/components/toolbar/MotionSensorBar.tsx b/src/view/components/toolbar/MotionSensorBar.tsx index 1ac5ae3d9..3dcdba145 100644 --- a/src/view/components/toolbar/MotionSensorBar.tsx +++ b/src/view/components/toolbar/MotionSensorBar.tsx @@ -28,22 +28,22 @@ const sendMessage = (state: any) => { const MOTION_SLIDER_PROPS_X: ISliderProps = { axisLabel: "X", maxLabel: "Right", - maxValue: 125, + maxValue: 78, minLabel: "Left", - minValue: -55, + minValue: -78, type: "motion_x" }; const MOTION_SLIDER_PROPS_Y: ISliderProps = { axisLabel: "Y", maxLabel: "Front", - maxValue: 125, + maxValue: 78, minLabel: "Back", - minValue: -55, + minValue: -78, type: "motion_y" }; const MOTION_SLIDER_PROPS_Z: ISliderProps = { - maxValue: 125, - minValue: -55, + maxValue: 78, + minValue: -78, minLabel: "Up", maxLabel: "Down", type: "motion_z", @@ -68,9 +68,6 @@ class MotionSensorBar extends React.Component { render() { return (
-
-
{MOTION_SENSOR_PROPERTIES.LABEL}
-
{ @@ -27,24 +24,14 @@ class ToolBar extends React.Component { constructor(props: any) { super(props); this.state = { - currentOpenedLabel: "", - doNotShowAgain: false, - showModal: false, - showRedirectModal: false + currentOpenedId: "", + showModal: false }; } render() { return (
-
-
- {INFO_SVG} - {CONSTANTS.TOOLBAR_INFO} - {this.getLearnLink()} -
- {this.getRedirectModal()} -
- {this.getIconModal()}
@@ -187,13 +184,13 @@ class ToolBar extends React.Component { ) => { if ( !this.state.showModal && - this.state.currentOpenedLabel === "" && - this.state.currentOpenedLabel !== label + this.state.currentOpenedId === "" && + this.state.currentOpenedId !== label ) { this.openModal(label); } else { this.closeCurrentModal(); - if (this.state.currentOpenedLabel !== label) { + if (this.state.currentOpenedId !== label) { this.openModal(label); } } @@ -208,16 +205,16 @@ class ToolBar extends React.Component { } } private closeCurrentModal = () => { - this.changePressedState(this.state.currentOpenedLabel, false); + this.changePressedState(this.state.currentOpenedId, false); this.setState({ - currentOpenedLabel: "", + currentOpenedId: "", showModal: false }); }; private openModal = (label: string) => { this.setState({ - currentOpenedLabel: label, + currentOpenedId: label, showModal: true }); this.changePressedState(label, true); @@ -226,13 +223,13 @@ class ToolBar extends React.Component { private getIconModal() { if ( !this.state.showModal || - !LABEL_TO_MODAL_CONTENT.get(this.state.currentOpenedLabel) + !LABEL_TO_MODAL_CONTENT.get(this.state.currentOpenedId) ) { return null; } const content = LABEL_TO_MODAL_CONTENT.get( - this.state.currentOpenedLabel + this.state.currentOpenedId ) as IModalContent; const component = content @@ -242,84 +239,27 @@ class ToolBar extends React.Component {
- {content["descriptionTitle"]} + {content["tagInput"]} {content["tagOutput"]}

-
{content["descriptionText"]}
+
+ +
-
{content["tryItTitle"]}

-
{content["tryItDescriptrion"]}
+ + + +
+
{component}
); } - - private getRedirectModal() { - if (this.state.doNotShowAgain || !this.state.showRedirectModal) { - return null; - } - return ( - -
-
{`${ - CONSTANTS.REDIRECT.DESCRIPTION - } : \n ${CONSTANTS.REDIRECT.PRIVACY}`}
- - {`Got it`} - - - {`close`} - - - {`Do Not Show Again`} - -
-
- ); - } - - private getLearnLink() { - const linkString = ( - - Learn More - - ); - const linkAnchor = ( - - Learn More - - ); - return this.state.doNotShowAgain ? linkAnchor : linkString; - } - - private handleOnClickButton = (event: React.MouseEvent) => { - this.setState({ showRedirectModal: false }); - }; - - private handleOnClickLink = (event: React.MouseEvent) => { - this.setState({ showRedirectModal: true }); - if (this.state.doNotShowAgain) { - const ref = window.document.getElementById("redirect"); - if (ref) { - window.location.assign(CONSTANTS.REDIRECT.LINK); - } - } - }; - - private handleDoNotShow = (event: React.MouseEvent) => { - this.setState({ doNotShowAgain: true }); - }; } -export default ToolBar; +export default injectIntl(ToolBar); diff --git a/src/view/components/toolbar/sensorModalUtils.tsx b/src/view/components/toolbar/sensorModalUtils.tsx index e5dbdd71e..cb4c60665 100644 --- a/src/view/components/toolbar/sensorModalUtils.tsx +++ b/src/view/components/toolbar/sensorModalUtils.tsx @@ -46,7 +46,7 @@ export const TOOLBAR_ICON_ID = { SOUND: "toolbar-sound-sensor", SPEAKER: "toolbar-speaker", SWITCH: "toolbar-slider-switch", - TEMPERATURE: "toolbar-temperatur-sensor" + TEMPERATURE: "toolbar-temperature-sensor" }; export interface IModalContent { @@ -56,7 +56,7 @@ export interface IModalContent { id: string; tagInput: any; tagOutput: any; - tryItDescriptrion: string; + tryItDescription: string; tryItTitle: string; } @@ -66,135 +66,119 @@ export const DEFAULT_MODAL_CONTENT: IModalContent = { tagOutput: undefined, descriptionText: "none", tryItTitle: "none", - tryItDescriptrion: "none", + tryItDescription: "none", component: undefined, id: "none" }; export const GPIO_MODAL_CONTENT: IModalContent = { - descriptionTitle: "GPIO", + descriptionTitle: "toolbar-gpio.title", tagInput: TAG_INPUT_SVG, tagOutput: TAG_OUTPUT_SVG, - descriptionText: - "8 GPIOs on CPX! Pin A1 - A7 can also be used as capacitive touch sensors, and A0 is a true analog output pin.", + descriptionText: "toolbar-gpio.description", tryItTitle: "Simulation Coming Soon!", - tryItDescriptrion: - "We’re working hard to support this sensor on the simulator in the Pacifica. You can try it on MakeCode!", - component: TRY_IT_MAKE_CODE, + tryItDescription: "toolbar-gpio.tryItDescription", + component: undefined, id: "GPIO" }; export const IR_MODAL_CONTENT: IModalContent = { - descriptionTitle: "IR Transmit & Receiver", + descriptionTitle: "toolbar-ir-sensor.title", tagInput: TAG_INPUT_SVG, tagOutput: TAG_OUTPUT_SVG, - descriptionText: - "Allows you to send commands to the CPX with a remote control, or even send messages between multiple CPXs! You can also do very simple proximity sensing since it reads the reflected light.", + descriptionText: "toolbar-ir-sensor.description", tryItTitle: "Simulation Coming Soon!", - tryItDescriptrion: - "We’re working hard to support this sensor on the simulator in the Pacifica. You can try it on MakeCode!", + tryItDescription: "toolbar-ir-sensor.tryItDescription", component: TRY_IT_MAKE_CODE, id: "IR" }; export const LIGHT_MODAL_CONTENT: IModalContent = { - descriptionTitle: "Light Sensor", + descriptionTitle: "toolbar-light-sensor.title", tagInput: TAG_INPUT_SVG, tagOutput: undefined, - descriptionText: - "An analog light sensor can be used to detect ambient light, with similar spectral response to the human eye.", + descriptionText: "toolbar-light-sensor.description", tryItTitle: "Try it on the Simulator!", - tryItDescriptrion: "Change the brightness from 0 - 255 here!", + tryItDescription: "toolbar-light-sensor.tryItDescription", component: , id: "light_sensor" }; export const MOTION_MODAL_CONTENT: IModalContent = { - descriptionTitle: "Motion Sensor", + descriptionTitle: "toolbar-motion-sensor.title", tagInput: TAG_INPUT_SVG, tagOutput: undefined, - descriptionText: - "Detects acceleration in XYZ orientations. And can also detect 'tap' and 'double tap' strikes on the board and when the board is shaken.", + descriptionText: "toolbar-motion-sensor.description", tryItTitle: "Try it on the Simulator!", - tryItDescriptrion: - "Change the acceleration here and click on the sensor on the board to simulate the “tap”!", + tryItDescription: "toolbar-motion-sensor.tryItDescription", component: , id: "motion_sensor" }; export const NEOP_MODAL_CONTENT: IModalContent = { - descriptionTitle: "NeoPixels", + descriptionTitle: "toolbar-neo-pixels.title", tagInput: undefined, tagOutput: TAG_OUTPUT_SVG, - descriptionText: - "The 10 full color RGB LEDs surrounding the outer edge of the boards can be set to any color. Great for beautiful lighting effects!", + descriptionText: "toolbar-neo-pixels.description", tryItTitle: "Try it on the Simulator!", - tryItDescriptrion: "Run your code and see the cool effects on the simulator!", - component: TRY_IT_MAKE_CODE, + tryItDescription: "toolbar-neo-pixels.tryItDescription", + component: undefined, id: "neon_pixel" }; export const PUSHB_MODAL_CONTENT: IModalContent = { - descriptionTitle: "Push Buttons", + descriptionTitle: "toolbar-push-button.title", tagInput: TAG_INPUT_SVG, tagOutput: undefined, - descriptionText: - "Two push buttons A and B are connected to digital pin #4 (Left) and #5 (Right) each.", + descriptionText: "toolbar-push-button.description", tryItTitle: "Try it on the Simulator!", - tryItDescriptrion: - "Click them with your mouse or pressing “A” “B” on your keyboard!", + tryItDescription: "toolbar-push-button.tryItDescription", component: undefined, id: "push_btn" }; export const RED_LED_MODAL_CONTENT: IModalContent = { - descriptionTitle: "Red LED", + descriptionTitle: "toolbar-red-led.title", tagInput: undefined, tagOutput: TAG_OUTPUT_SVG, - descriptionText: - "This Red LED is connected to the digital #13 GPIO pin. It can be very handy when you want an indicator LED.", + descriptionText: "toolbar-red-led.description", tryItTitle: "Try it on the Simulator!", - tryItDescriptrion: "Run your code and see the cool effects on the simulator!", + tryItDescription: "toolbar-red-led.tryItDescription", component: undefined, id: "red_LED" }; export const SOUND_MODAL_CONTENT: IModalContent = { - descriptionTitle: "Sound Sensor", + descriptionTitle: "toolbar-sound-sensor.title", tagInput: TAG_INPUT_SVG, tagOutput: undefined, - descriptionText: - "A digital microphone can detect audio volume and even perform basic FFT functions but cannot read it like an analog voltage.", + descriptionText: "toolbar-sound-sensor.description", tryItTitle: "Simulation Coming Soon!", - tryItDescriptrion: - "We’re working hard to support this sensor on the simulator in the Pacifica. You can try it on MakeCode!", + tryItDescription: "toolbar-sound-sensor.tryItDescription", component: TRY_IT_MAKE_CODE, id: "sound_sensor" }; export const SWITCH_MODAL_CONTENT: IModalContent = { - descriptionTitle: "Slide Switch ", + descriptionTitle: "toolbar-slider-switch.title", tagInput: TAG_INPUT_SVG, tagOutput: undefined, - descriptionText: - "This slide switch returns True or False depending on whether it's ON or OFF and can be used as a toggle switch in your code!", + descriptionText: "toolbar-slider-switch.description", tryItTitle: "Try it on the Simulator!", - tryItDescriptrion: "Click it with your mouse to switch it ON and OFF!", + tryItDescription: "toolbar-slider-switch.tryItDescription", component: undefined, id: "slider_switch" }; export const SPEAKER_MODAL_CONTENT: IModalContent = { - descriptionTitle: "Speaker ", + descriptionTitle: "toolbar-speaker.title", tagInput: undefined, tagOutput: TAG_OUTPUT_SVG, - descriptionText: - "This speaker can play .wav file and different tones and also has a class D amplifier that is connected to an output A0 pin built in! You can turn it off using the shutdown control on pin #11 on the physical device.", + descriptionText: "toolbar-speaker.description", tryItTitle: "Try it on the Simulator!", - tryItDescriptrion: "Run your code and you’ll hear music!", + tryItDescription: "toolbar-speaker.tryItDescription", component: undefined, id: "speaker" }; export const TEMPERATURE_MODAL_CONTENT: IModalContent = { component: , - descriptionText: - "This sensor uses an NTC thermistor to sense temperature an calculate it with the analog voltage on analog pin #A9.", - descriptionTitle: "Temperature Sensor", + descriptionText: "toolbar-temperature-sensor.description", + descriptionTitle: "toolbar-temperature-sensor.title", id: "temperature", tagInput: TAG_INPUT_SVG, tagOutput: undefined, - tryItDescriptrion: "You can set the temperature range from your code!", + tryItDescription: "toolbar-temperature-sensor.tryItDescription", tryItTitle: "Try it on the Simulator!" }; diff --git a/src/view/constants.ts b/src/view/constants.ts index 449911863..372d77cbf 100644 --- a/src/view/constants.ts +++ b/src/view/constants.ts @@ -3,6 +3,9 @@ // Key events export const CONSTANTS = { + CURRENTLY_RUNNING: (file: string) => { + return `Currently running: ${file}` + }, ID_NAME: { BUTTON_A: "BTN_A_OUTER", BUTTON_AB: "BTN_AB_OUTER", @@ -34,14 +37,9 @@ export const CONSTANTS = { NUMERIC_SIX: "Digit6", NUMERIC_SEVEN: "Digit7" }, - REDIRECT: { - DESCRIPTION: - 'By clicking "Agree and Proceed" you will be redirected to adafruit.com, a third party website not managed by Microsoft. Please note that your activity on adafruit.com is subject to Adafruit\'s privacy policy', - LINK: - "https://learn.adafruit.com/adafruit-circuit-playground-express/circuitpython-quickstart", - PRIVACY: "https://www.adafruit.com/privacy" - }, - TOOLBAR_INFO: `Explore what's on the board:` + NO_FILES_AVAILABLE: "Choose a .py file to run on the Simulator", + SIMULATOR_BUTTON_WIDTH: 60, + TOOLBAR_INFO: `Explore what's on the board:`, }; export default CONSTANTS; diff --git a/src/view/index.tsx b/src/view/index.tsx index 78a8cc933..d5a969aa8 100644 --- a/src/view/index.tsx +++ b/src/view/index.tsx @@ -1,13 +1,23 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import * as React from 'react'; -import * as ReactDOM from 'react-dom'; -import App from './App'; +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import App from "./App"; +import { IntlProvider } from "react-intl"; -import './index.css'; +import "./index.css"; + +const messageEn = require("./translations/en.json"); +const locale = "en"; + +const message = { + en: messageEn +}; ReactDOM.render( - , - document.getElementById('root') -); \ No newline at end of file + + + , + document.getElementById("root") +); diff --git a/src/view/styles/Button.css b/src/view/styles/Button.css index 5e77bc41b..bd2b1c8fe 100644 --- a/src/view/styles/Button.css +++ b/src/view/styles/Button.css @@ -5,15 +5,20 @@ } .button-icon { - fill: var(--vscode-badge-foreground); + fill: var(--vscode-badgeForegroundOverride); +} +.button-rectangle { + stroke: var(--vscode-badgeForegroundOverride); } .play-button { border-radius: 8px 0px 0px 8px; + border-color: var(--vscode-highContrastButtonBorderOverride-color); } .refresh-button { border-radius: 0px 8px 8px 0px; + border-color: var(--vscode-highContrastButtonBorderOverride-color); } .button:focus, @@ -29,7 +34,6 @@ border: none; } -.toolbar-button:focus, .toolbar-button:hover { outline: none; } diff --git a/src/view/styles/Dropdown.css b/src/view/styles/Dropdown.css new file mode 100644 index 000000000..c1b355d26 --- /dev/null +++ b/src/view/styles/Dropdown.css @@ -0,0 +1,27 @@ +.dropdown { + background: var(--vscode-debugToolBar-background); + border-color: var(--vscode-highContrastButtonBorderOverride-color); + border-radius: 2px; + max-width: 300px; + min-width: 240px; + box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.22); + color: var(--vscode-foreground); + height: 32px; + padding-left: 8px; + padding-right: 4px; +} + +select.dropdown:hover, +select.dropdown:focus, +select.dropdown:active { + outline: 1px solid var(--vscode-button-background); + outline-offset: 1px; +} + +option { + height: 32px; + background: var(--vscode-debugToolBar-background); + align-items: center; + font-size: 14px; + color: var(--vscode-foreground); +} diff --git a/src/view/styles/InputSlider.css b/src/view/styles/InputSlider.css index 523e70d77..8bd87a83c 100644 --- a/src/view/styles/InputSlider.css +++ b/src/view/styles/InputSlider.css @@ -13,16 +13,17 @@ width: 48px; height: 32px; background-color: var(--vscode-editor-background); - color: white; - border: 0; margin-right: 15px; margin-top: auto; margin-bottom: auto; margin-left: 5px; - color: var(--badge-foreground); + color: var(--badgeForegroundOverride); border-radius: 2px; font-size: 16px; font-weight: bold; + border-width: 1px; + border-radius: 2px; + border-color: var(--vscode-highContrastButtonBorderOverride-color); } .slider { diff --git a/src/view/styles/MotionSensorBar.css b/src/view/styles/MotionSensorBar.css index cc298dde0..552e1ddcd 100644 --- a/src/view/styles/MotionSensorBar.css +++ b/src/view/styles/MotionSensorBar.css @@ -10,7 +10,6 @@ } .MotionSensorBar { - margin-top: 10px; width: 440px; margin-left: auto; margin-right: auto; diff --git a/src/view/styles/SensorButton.css b/src/view/styles/SensorButton.css index c62bfdea4..df85bcef2 100644 --- a/src/view/styles/SensorButton.css +++ b/src/view/styles/SensorButton.css @@ -1,15 +1,17 @@ .sensor-button { - color: var(--vscode-badge-foreground); + color: var(--vscode-badgeForegroundOverride); text-align: center; background-color: var(--vscode-button-background); width: 320px; height: 32px; - border: none; font-weight: bolder; float: left; padding-left: 20px; margin-bottom: 20px; margin-top: 20px; + border-color: var(--vscode-highContrastButtonBorderOverride-color); + border-width: 1px; + border-style: solid; } .sensor-button:focus, diff --git a/src/view/styles/Simulator.css b/src/view/styles/Simulator.css index 0106b9a63..5a428910b 100644 --- a/src/view/styles/Simulator.css +++ b/src/view/styles/Simulator.css @@ -2,11 +2,19 @@ display: flex; flex-direction: column; justify-content: center; + max-width: 700px; + max-height: 700px; + margin-left: auto; + margin-right: auto; } .buttons { display: flex; flex-direction: row; - padding: 20px; + padding-top: 20px; justify-content: center; } + +.file-selector { + padding: 20px; +} diff --git a/src/view/styles/ToolBar.css b/src/view/styles/ToolBar.css index 333d73fe4..89e6c1d9f 100644 --- a/src/view/styles/ToolBar.css +++ b/src/view/styles/ToolBar.css @@ -5,13 +5,16 @@ height: fit-content; margin-left: auto; margin-right: auto; - margin-top: 53px; + margin-top: 24px; margin-bottom: 50px; } .toolbar, .toolbar-icon { box-shadow: 0px 0px 20px rgba(0, 0, 0, 30%); + border-color: var(--vscode-highContrastButtonBorderOverride-color); + border-width: 1px; + border-style: solid; } .tag, @@ -21,19 +24,23 @@ .sensor_modal { vertical-align: middle; - width: 320px; + width: 360px; + max-height: 240px; + overflow-y: scroll; + overflow-x: hidden; position: relative; height: fit-content; padding-left: 16px; box-shadow: none; background: var(--vscode-debugToolBar-background); + margin-left: 1px; } .title { -webkit-appearance: none; font-size: 16px; font-weight: bolder; - color: var(--vscode-badge-foreground); + color: var(--vscode-badgeForegroundOverride); text-align: left; margin-right: 40px; position: absolute; @@ -47,11 +54,6 @@ left: 200px; } -.close_icon { - position: absolute; - right: 0; -} - .description { -webkit-appearance: none; font-size: 14px; @@ -59,7 +61,6 @@ word-wrap: break-word; width: 320px; margin-top: 15px; - margin-bottom: 15px; text-align: left; line-height: 17px; font-weight: 100; @@ -75,32 +76,20 @@ .try_area { position: relative; padding-bottom: 30px; -} - -.info { - background: var(--vscode-editor-background); text-align: left; - margin-left: 0; - padding-bottom: 10px; - border: none; - box-shadow: none; -} - -.info-icon { - margin-right: 8px; -} - -.info-text { - font-weight: 500; - font-size: 16px; - padding-right: 13px; + line-height: 17px; + font-weight: 100; + opacity: 50%; + font-size: 14px; + word-wrap: break-word; } .link-parent { + padding-top: 12px; -webkit-appearance: none; padding-left: 150px; color: var(--vscode-textLink-activeForeground); - text-align: left; + text-align: right; text-decoration: none; font-weight: bold; } @@ -111,55 +100,6 @@ text-decoration: none; } -.info-icon-svg { - fill: var(--vscode-foreground); -} - -.redirect-link { - color: var(--vscode-foreground); -} -.redirect-modal { - background: var(--vscode-debugToolBar-background); - position: absolute; - right: 50px; - bottom: 50px; - color: var(--vscode-foreground); - /* word-wrap: break-word; */ - width: 450px; - box-shadow: 0px 0px 20px rgba(0, 0, 0, 30%); - text-align: center; - font-weight: 700; - font-optical-sizing: 16px; - height: 100px; -} - -.redirect-button { - text-decoration: none; - margin-left: auto; - margin-right: auto; - line-height: 17px; - color: var(--vscode-button-background); - padding-right: 15px; -} - -.redirect-description { - word-wrap: break-word; - margin-top: 15px; - margin-bottom: 15px; - text-align: center; - line-height: 17px; - opacity: 50%; - vertical-align: middle; -} - -.redirect-learn-link { - text-decoration: none; - color: var(--vscode-textLink-activeForeground); - font-size: 16px; - text-align: right; - padding-left: 16px; -} - .link { -webkit-appearance: none; text-decoration: none; diff --git a/src/view/svgs/info_svg.tsx b/src/view/svgs/info_svg.tsx deleted file mode 100644 index 769481d75..000000000 --- a/src/view/svgs/info_svg.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import * as React from "react"; -import "../styles/ToolBar.css"; - -export const INFO_SVG = ( - - - -); - -export default INFO_SVG; diff --git a/src/view/svgs/toolbar_svg.tsx b/src/view/svgs/toolbar_svg.tsx index 99aeaf196..d763724ad 100644 --- a/src/view/svgs/toolbar_svg.tsx +++ b/src/view/svgs/toolbar_svg.tsx @@ -355,13 +355,13 @@ export const SOUND_SVG = ( /> diff --git a/src/view/translations/en.json b/src/view/translations/en.json new file mode 100644 index 000000000..0cd7f7f98 --- /dev/null +++ b/src/view/translations/en.json @@ -0,0 +1,36 @@ +{ + "toolbar-gpio.description": "8 GPIOs on the device! Pin A1 - A7 can also be used as capacitive touch sensors, and A0 is a true analog output pin.", + "toolbar-gpio.title": "GPIO", + "toolbar-gpio.tryItDescription": "Use your mouse to interact with the pin A1 - A7 or use your keyboard SHIFT+”1” - “7”", + "toolbar-ir-sensor.description": "Allows you to send commands to the device with a remote control, or even send messages between multiple devices! You can also do very simple proximity sensing since it reads the reflected light.", + "toolbar-ir-sensor.title": "IR Transmit & Receiver", + "toolbar-ir-sensor.tryItDescription": "We’re working hard to support this sensor on the simulator in the Device Simulator Express. You can try it on MakeCode!", + "toolbar-ir-sensor.tryItTitle": "We’re working hard to support this sensor on the simulator in the Device Simulator Express. You can try it on MakeCode!", + "toolbar-light-sensor.description": "An analog light sensor can be used to detect ambient light, with similar spectral response to the human eye.", + "toolbar-light-sensor.title": "Light Sensor", + "toolbar-light-sensor.tryItDescription": "Change the brightness from 0 - 255 here!", + "toolbar-motion-sensor.description": "Detects acceleration in XYZ orientations. And can also detect 'tap' and 'double tap' strikes on the board and when the board is shaken.", + "toolbar-motion-sensor.title": "Motion Sensor", + "toolbar-motion-sensor.tryItDescription": "Change the acceleration here and click or click on the button to simulate a shake.The tap feature is not supported by the Device Simulator Express. You can try it on MakeCode!", + "toolbar-neo-pixels.description": "The 10 full color RGB LEDs surrounding the outer edge of the boards can be set to any color. Great for beautiful lighting effects!", + "toolbar-neo-pixels.title": "NeoPixels", + "toolbar-neo-pixels.tryItDescription": "Run your code and see the cool effects on the simulator!", + "toolbar-push-button.description": "Two push buttons A and B are connected to digital pin #4 (Left) and #5 (Right) each.", + "toolbar-push-button.title": "Push Buttons", + "toolbar-push-button.tryItDescription": "Click them with your mouse or by pressing “A” “B” on your keyboard!", + "toolbar-red-led.description": "This Red LED is connected to the digital #13 GPIO pin. It can be very handy when you want an indicator LED.", + "toolbar-red-led.title": "Red LED", + "toolbar-red-led.tryItDescription": "Run your code and see the cool effects on the simulator!", + "toolbar-sound-sensor.description": "A digital microphone can detect audio volume and even perform basic FFT functions but cannot read it like an analog voltage.", + "toolbar-sound-sensor.title": "Sound Sensor", + "toolbar-sound-sensor.tryItDescription": "We’re working hard to support this sensor on the simulator in the Device Simulator Express. You can try it on MakeCode!", + "toolbar-slider-switch.description": "This slide switch returns True or False depending on whether it's ON or OFF and can be used as a toggle switch in your code!", + "toolbar-slider-switch.title": "Slider Switch", + "toolbar-slider-switch.tryItDescription": "Click it with your mouse or press 'S' on your keyboard to switch it ON and OFF!", + "toolbar-speaker.description": "This speaker can play .wav file and different tones and also has a class D amplifier that is connected to an output A0 pin built in! You can turn it off using the shutdown control on pin #11 on the physical device.", + "toolbar-speaker.title": "Speaker", + "toolbar-speaker.tryItDescription": "Right now the tones are not supported yet on the simulator, but you can play it on your device!", + "toolbar-temperature-sensor.description": "This sensor uses an NTC thermistor to sense temperature an calculate it with the analog voltage on analog pin #A9.", + "toolbar-temperature-sensor.title": "Temperature Sensor", + "toolbar-temperature-sensor.tryItDescription": "You can set the temperature range from your code!" +} diff --git a/tsconfig.json b/tsconfig.json index df0b43c7c..8fd7e5501 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,11 +7,7 @@ "sourceMap": true, "rootDir": "src", "jsx": "react", - "strict": true /* enable all strict type-checking options */ - /* Additional Checks */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ + "alwaysStrict": true }, "exclude": ["node_modules", ".vscode-test"] } diff --git a/vendor/node-usb-native/.eslintrc b/vendor/node-usb-native/.eslintrc new file mode 100644 index 000000000..2b063c78c --- /dev/null +++ b/vendor/node-usb-native/.eslintrc @@ -0,0 +1,26 @@ +{ + "extends": [ + "standard" + ], + "plugins": [ + "require-path-exists" + ], + "env": { + "node": true, + "mocha": true + }, + "rules": { + "arrow-parens": [2, "as-needed", {"requireForBlockBody": true }], + "no-unused-vars": [2, { "vars": "all", "args": "after-used" }], + "object-curly-spacing": [2, "always"], + "prefer-arrow-callback": 2, + "prefer-const": 2, + "prefer-template": 2, + "require-path-exists/exists": 2, + "require-path-exists/notEmpty": 2, + "require-path-exists/tooManyArguments": 2, + "semi": [2, "always", {"omitLastInOneLineBlock": true}], + "space-before-function-paren": [2, "never"], + "standard/object-curly-even-spacing": 0 + } +} \ No newline at end of file diff --git a/vendor/node-usb-native/.gitignore b/vendor/node-usb-native/.gitignore new file mode 100644 index 000000000..8d2b1ae24 --- /dev/null +++ b/vendor/node-usb-native/.gitignore @@ -0,0 +1,14 @@ +$ cat .gitignore + +# Can ignore specific files +.settings.xml +.monitor +.DS_Store + +# Use wildcards as well +*~ +#*.swp + +# Can also ignore all directories and files in a directory. +node_modules/ +build/ diff --git a/vendor/node-usb-native/.npmignore b/vendor/node-usb-native/.npmignore new file mode 100644 index 000000000..b38db2f29 --- /dev/null +++ b/vendor/node-usb-native/.npmignore @@ -0,0 +1,2 @@ +node_modules/ +build/ diff --git a/vendor/node-usb-native/README.md b/vendor/node-usb-native/README.md new file mode 100644 index 000000000..5de5318a6 --- /dev/null +++ b/vendor/node-usb-native/README.md @@ -0,0 +1,4 @@ +This is a natvie package contains two modules: detector and serialport, detector provides the functionality of detecting usb changes and serialport provides the functionality of listing serial ports, openning serial ports and sending/receiving message to/from serial ports. + + require("../../../vendor/node-usb-native").SerialPort; + require("../../../vendor/node-usb-native").detector; diff --git a/vendor/node-usb-native/binding.gyp b/vendor/node-usb-native/binding.gyp new file mode 100644 index 000000000..c967103e1 --- /dev/null +++ b/vendor/node-usb-native/binding.gyp @@ -0,0 +1,64 @@ +{ + "targets": [ + { + "target_name": "usb-native", + "sources": [ + "src/detection.cpp", + "src/detection.h", + "src/deviceList.cpp", + "src/combined.cpp", + "src/serialport.cpp" + ], + "include_dirs": [ + " { + // Suss out the optional parameters + if (!pid && !callback) { + callback = vid; + vid = undefined; + } else if (!callback) { + callback = pid; + pid = undefined; + } + + return new Promise((resolve, reject) => { + // Assemble the optional args into something we can use with `apply` + var args = []; + if (vid) { + args = args.concat(vid); + } + if (pid) { + args = args.concat(pid); + } + + // Tack on our own callback that takes care of things + args = args.concat((err, devices) => { + // We call the callback if they passed one + if (callback) { + callback.call(callback, err, devices); + } + + // But also do the promise stuff + if (err) { + reject(err); + return; + } + resolve(devices); + }); + + // Fire off the `find` function that actually does all of the work + detection.find.apply(detection, args); + }); +}; +if (detection.registerAdded) { + detection.registerAdded((device) => { + detector.emit(`add:${device.vendorId}:${device.productId}`, device); + detector.emit(`insert:${device.vendorId}:${device.productId}`, device); + detector.emit(`add:${device.vendorId}`, device); + detector.emit(`insert:${device.vendorId}`, device); + detector.emit('add', device); + detector.emit('insert', device); + + detector.emit(`change:${device.vendorId}:${device.productId}`, device); + detector.emit(`change:${device.vendorId}`, device); + detector.emit('change', device); + }); + + detection.registerRemoved((device) => { + detector.emit(`remove:${device.vendorId}:${device.productId}`, device); + detector.emit(`remove:${device.vendorId}`, device); + detector.emit('remove', device); + + detector.emit(`change:${device.vendorId}:${device.productId}`, device); + detector.emit(`change:${device.vendorId}`, device); + detector.emit('change', device); + }); + + var started = true; + + detector.startMonitoring = () => { + if (started) { + return; + } + + started = true; + detection.startMonitoring(); + }; + + detector.stopMonitoring = () => { + if (!started) { + return; + } + + started = false; + detection.stopMonitoring(); + }; +} + +module.exports = detector; diff --git a/vendor/node-usb-native/lib/index.js b/vendor/node-usb-native/lib/index.js new file mode 100644 index 000000000..3a5c973fe --- /dev/null +++ b/vendor/node-usb-native/lib/index.js @@ -0,0 +1,2 @@ +exports.detector = require('./detector'); +exports.SerialPort = require('./serialport'); diff --git a/vendor/node-usb-native/lib/list-unix.js b/vendor/node-usb-native/lib/list-unix.js new file mode 100644 index 000000000..e3b6de297 --- /dev/null +++ b/vendor/node-usb-native/lib/list-unix.js @@ -0,0 +1,109 @@ +'use strict'; + +var childProcess = require('child_process'); +var fs = require('fs'); +var path = require('path'); + +function promisify(func) { + return (arg) => { + return new Promise((resolve, reject) => { + func(arg, (err, data) => { + if (err) { + return reject(err); + } + resolve(data); + }); + }); + }; +} + +function promisedFilter(func) { + return (data) => { + var shouldKeep = data.map(func); + return Promise.all(shouldKeep).then((keep) => { + return data.filter((path, index) => { + return keep[index]; + }); + }); + }; +} + +var statAsync = promisify(fs.stat); +var readdirAsync = promisify(fs.readdir); +var execAsync = promisify(childProcess.exec); + +function udevParser(output) { + var udevInfo = output.split('\n').reduce((info, line) => { + if (!line || line.trim() === '') { + return info; + } + var parts = line.split('=').map((part) => { + return part.trim(); + }); + + info[parts[0].toLowerCase()] = parts[1]; + + return info; + }, {}); + + var pnpId; + if (udevInfo.devlinks) { + udevInfo.devlinks.split(' ').forEach((path) => { + if (path.indexOf('/by-id/') === -1) { return } + pnpId = path.substring(path.lastIndexOf('/') + 1); + }); + } + + var vendorId = udevInfo.id_vendor_id; + if (vendorId && vendorId.substring(0, 2) !== '0x') { + vendorId = `0x${vendorId}`; + } + + var productId = udevInfo.id_model_id; + if (productId && productId.substring(0, 2) !== '0x') { + productId = `0x${productId}`; + } + + return { + comName: udevInfo.devname, + manufacturer: udevInfo.id_vendor, + serialNumber: udevInfo.id_serial, + pnpId: pnpId, + vendorId: vendorId, + productId: productId + }; +} + +function checkPathAndDevice(path) { + // get only serial port names + if (!(/(tty(S|ACM|USB|AMA|MFD)|rfcomm)/).test(path)) { + return false; + } + return statAsync(path).then((stats) => { + return stats.isCharacterDevice(); + }); +} + +function lookupPort(file) { + var udevadm = `udevadm info --query=property -p $(udevadm info -q path -n ${file})`; + return execAsync(udevadm).then(udevParser); +} + +function listUnix(callback) { + var dirName = '/dev'; + readdirAsync(dirName) + .catch((err) => { + // if this directory is not found we just pretend everything is OK + // TODO Depreciated this check? + if (err.errno === 34) { + return []; + } + throw err; + }) + .then((data) => { return data.map((file) => { return path.join(dirName, file) }) }) + .then(promisedFilter(checkPathAndDevice)) + .then((data) => { return Promise.all(data.map(lookupPort)) }) + .then((data) => { callback(null, data) }, (err) => { callback(err) }); +} + +module.exports = listUnix; diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.4.6_ia32.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.4.6_ia32.node new file mode 100644 index 000000000..9c0cd4283 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.4.6_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.4.6_x64.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.4.6_x64.node new file mode 100644 index 000000000..4623a53bf Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.4.6_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.6.6_ia32.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.6.6_ia32.node new file mode 100644 index 000000000..57028b867 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.6.6_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.6.6_x64.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.6.6_x64.node new file mode 100644 index 000000000..d2f921e58 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.6.6_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.7.3_ia32.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.7.3_ia32.node new file mode 100644 index 000000000..bff5f2ebe Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.7.3_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.7.3_x64.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.7.3_x64.node new file mode 100644 index 000000000..d5851886d Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_1.7.3_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_2.0.2_ia32.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_2.0.2_ia32.node new file mode 100644 index 000000000..b626c0c1a Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_2.0.2_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_2.0.2_x64.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_2.0.2_x64.node new file mode 100644 index 000000000..28cb86f0e Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_2.0.2_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_3.0.10_ia32.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_3.0.10_ia32.node new file mode 100644 index 000000000..36a4ed73a Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_3.0.10_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_3.0.10_x64.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_3.0.10_x64.node new file mode 100644 index 000000000..54e421b31 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_3.0.10_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_4.2.5_ia32.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_4.2.5_ia32.node new file mode 100644 index 000000000..0ea992b82 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_4.2.5_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_4.2.5_x64.node b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_4.2.5_x64.node new file mode 100644 index 000000000..a1d586205 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_Ubuntu14.04_4.2.5_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_darwin_1.4.6_x64.node b/vendor/node-usb-native/lib/native/usb-native_darwin_1.4.6_x64.node new file mode 100644 index 000000000..5a6d083c0 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_darwin_1.4.6_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_darwin_1.6.6_x64.node b/vendor/node-usb-native/lib/native/usb-native_darwin_1.6.6_x64.node new file mode 100644 index 000000000..e250b8a3c Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_darwin_1.6.6_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_darwin_1.7.3_x64.node b/vendor/node-usb-native/lib/native/usb-native_darwin_1.7.3_x64.node new file mode 100644 index 000000000..3a4ab3cac Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_darwin_1.7.3_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_darwin_2.0.2_x64.node b/vendor/node-usb-native/lib/native/usb-native_darwin_2.0.2_x64.node new file mode 100644 index 000000000..ab9554d91 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_darwin_2.0.2_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_darwin_3.0.10_x64.node b/vendor/node-usb-native/lib/native/usb-native_darwin_3.0.10_x64.node new file mode 100644 index 000000000..65c124d7f Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_darwin_3.0.10_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_darwin_4.2.5_x64.node b/vendor/node-usb-native/lib/native/usb-native_darwin_4.2.5_x64.node new file mode 100644 index 000000000..64d8ec806 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_darwin_4.2.5_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_1.4.6_ia32.node b/vendor/node-usb-native/lib/native/usb-native_win32_1.4.6_ia32.node new file mode 100644 index 000000000..d4a5c46aa Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_1.4.6_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_1.4.6_x64.node b/vendor/node-usb-native/lib/native/usb-native_win32_1.4.6_x64.node new file mode 100644 index 000000000..7a934e273 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_1.4.6_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_1.4.7_ia32.node b/vendor/node-usb-native/lib/native/usb-native_win32_1.4.7_ia32.node new file mode 100644 index 000000000..bb473cbaa Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_1.4.7_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_1.4.7_x64.node b/vendor/node-usb-native/lib/native/usb-native_win32_1.4.7_x64.node new file mode 100644 index 000000000..865eaf39c Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_1.4.7_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_1.6.6_ia32.node b/vendor/node-usb-native/lib/native/usb-native_win32_1.6.6_ia32.node new file mode 100644 index 000000000..853d52964 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_1.6.6_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_1.6.6_x64.node b/vendor/node-usb-native/lib/native/usb-native_win32_1.6.6_x64.node new file mode 100644 index 000000000..fc8dc8191 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_1.6.6_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_1.7.3_ia32.node b/vendor/node-usb-native/lib/native/usb-native_win32_1.7.3_ia32.node new file mode 100644 index 000000000..5c599c834 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_1.7.3_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_1.7.3_x64.node b/vendor/node-usb-native/lib/native/usb-native_win32_1.7.3_x64.node new file mode 100644 index 000000000..7175fc2dd Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_1.7.3_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_2.0.2_ia32.node b/vendor/node-usb-native/lib/native/usb-native_win32_2.0.2_ia32.node new file mode 100644 index 000000000..35eafbd98 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_2.0.2_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_2.0.2_x64.node b/vendor/node-usb-native/lib/native/usb-native_win32_2.0.2_x64.node new file mode 100644 index 000000000..7d10bd87b Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_2.0.2_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_3.0.10_ia32.node b/vendor/node-usb-native/lib/native/usb-native_win32_3.0.10_ia32.node new file mode 100644 index 000000000..d96312f25 Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_3.0.10_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_3.0.10_x64.node b/vendor/node-usb-native/lib/native/usb-native_win32_3.0.10_x64.node new file mode 100644 index 000000000..cee563b4f Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_3.0.10_x64.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_4.2.5_ia32.node b/vendor/node-usb-native/lib/native/usb-native_win32_4.2.5_ia32.node new file mode 100644 index 000000000..a5d5b53bc Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_4.2.5_ia32.node differ diff --git a/vendor/node-usb-native/lib/native/usb-native_win32_4.2.5_x64.node b/vendor/node-usb-native/lib/native/usb-native_win32_4.2.5_x64.node new file mode 100644 index 000000000..cb667a70b Binary files /dev/null and b/vendor/node-usb-native/lib/native/usb-native_win32_4.2.5_x64.node differ diff --git a/vendor/node-usb-native/lib/native_loader.js b/vendor/node-usb-native/lib/native_loader.js new file mode 100644 index 000000000..0f1fc2ebf --- /dev/null +++ b/vendor/node-usb-native/lib/native_loader.js @@ -0,0 +1,24 @@ +const glob = require('glob'); +const path = require('path'); +const loadLibrary = function(parentFolder, libraryName) { + const nodegypFiles = glob(path.join(__dirname, `../build/+(Release|Debug)/${libraryName}.node`), { + sync: true + }); + const nodepregypFiles = glob(`${parentFolder.replace(/\\/g, '/')}/${libraryName}*${process.arch}*.node`, { + sync: true + }); + var binding = null; + nodegypFiles.concat(nodepregypFiles).forEach((file) => { + try { + var _temp = require(file); + binding = _temp; + console.log('using', file); + } catch (e) { + } + }); + if (!binding) { + console.log('[Warn]', 'no library available after trying files', nodegypFiles.concat(nodepregypFiles)); + } + return binding; +}; +exports.load = loadLibrary; diff --git a/vendor/node-usb-native/lib/parsers.js b/vendor/node-usb-native/lib/parsers.js new file mode 100644 index 000000000..2783c1425 --- /dev/null +++ b/vendor/node-usb-native/lib/parsers.js @@ -0,0 +1,64 @@ +'use strict'; + +// Copyright 2011 Chris Williams + +module.exports = { + raw: function(emitter, buffer) { + emitter.emit('data', buffer); + }, + + // encoding: ascii utf8 utf16le ucs2 base64 binary hex + // More: http://nodejs.org/api/buffer.html#buffer_buffer + readline: function(delimiter, encoding) { + if (typeof delimiter === 'undefined' || delimiter === null) { delimiter = '\r' } + if (typeof encoding === 'undefined' || encoding === null) { encoding = 'utf8' } + // Delimiter buffer saved in closure + var data = ''; + return function(emitter, buffer) { + // Collect data + data += buffer.toString(encoding); + // Split collected data by delimiter + var parts = data.split(delimiter); + data = parts.pop(); + parts.forEach((part) => { + emitter.emit('data', part); + }); + }; + }, + + // Emit a data event every `length` bytes + byteLength: function(length) { + var data = Buffer.alloc(0); + return function(emitter, buffer) { + data = Buffer.concat([data, buffer]); + while (data.length >= length) { + var out = data.slice(0, length); + data = data.slice(length); + emitter.emit('data', out); + } + }; + }, + + // Emit a data event each time a byte sequence (delimiter is an array of byte) is found + // Sample usage : byteDelimiter([10, 13]) + byteDelimiter: function(delimiter) { + if (Object.prototype.toString.call(delimiter) !== '[object Array]') { + delimiter = [ delimiter ]; + } + var buf = []; + var nextDelimIndex = 0; + return function(emitter, buffer) { + for (var i = 0; i < buffer.length; i++) { + buf[buf.length] = buffer[i]; + if (buf[buf.length - 1] === delimiter[nextDelimIndex]) { + nextDelimIndex++; + } + if (nextDelimIndex === delimiter.length) { + emitter.emit('data', buf); + buf = []; + nextDelimIndex = 0; + } + } + }; + } +}; diff --git a/vendor/node-usb-native/lib/serialport.js b/vendor/node-usb-native/lib/serialport.js new file mode 100644 index 000000000..6fcc88e16 --- /dev/null +++ b/vendor/node-usb-native/lib/serialport.js @@ -0,0 +1,502 @@ +'use strict'; + +// Copyright 2011 Chris Williams + +const _debug = false; +const debug = (message) => { + if (_debug) console.log(message); +}; + +// shims +// Internal Dependencies +var SerialPortBinding = require('./bindings'); +var parsers = require('./parsers'); + +// Built-ins Dependencies +var fs = require('fs'); +var stream = require('stream'); +var util = require('util'); + +// VALIDATION ARRAYS +var DATABITS = [5, 6, 7, 8]; +var STOPBITS = [1, 1.5, 2]; +var PARITY = ['none', 'even', 'mark', 'odd', 'space']; +var FLOWCONTROLS = ['xon', 'xoff', 'xany', 'rtscts']; +var SET_OPTIONS = ['brk', 'cts', 'dtr', 'dts', 'rts']; + +// Stuff from ReadStream, refactored for our usage: +var kPoolSize = 40 * 1024; +var kMinPoolSpace = 128; + +var defaultSettings = { + baudRate: 9600, + autoOpen: true, + parity: 'none', + xon: false, + xoff: false, + xany: false, + rtscts: false, + hupcl: true, + dataBits: 8, + stopBits: 1, + bufferSize: 64 * 1024, + lock: true, + parser: parsers.raw, + platformOptions: SerialPortBinding.platformOptions +}; + +var defaultSetFlags = { + brk: false, + cts: false, + dtr: true, + dts: false, + rts: true +}; + +// deprecate the lowercase version of these options next major release +var LOWERCASE_OPTIONS = [ + 'baudRate', + 'dataBits', + 'stopBits', + 'bufferSize', + 'platformOptions' +]; + +function correctOptions(options) { + LOWERCASE_OPTIONS.forEach((name) => { + var lowerName = name.toLowerCase(); + if (options.hasOwnProperty(lowerName)) { + var value = options[lowerName]; + delete options[lowerName]; + options[name] = value; + } + }); + return options; +} + +function SerialPort(path, options, callback) { + if (typeof callback === 'boolean') { + throw new TypeError('`openImmediately` is now called `autoOpen` and is a property of options'); + } + + if (typeof options === 'function') { + callback = options; + options = {}; + } + + options = options || {}; + + stream.Stream.call(this); + + if (!path) { + throw new TypeError('No path specified'); + } + + this.path = path; + + var correctedOptions = correctOptions(options); + var settings = Object.assign({}, defaultSettings, correctedOptions); + + if (typeof settings.baudRate !== 'number') { + throw new TypeError(`Invalid "baudRate" must be a number got: ${settings.baudRate}`); + } + + if (DATABITS.indexOf(settings.dataBits) === -1) { + throw new TypeError(`Invalid "databits": ${settings.dataBits}`); + } + + if (STOPBITS.indexOf(settings.stopBits) === -1) { + throw new TypeError(`Invalid "stopbits": ${settings.stopBits}`); + } + + if (PARITY.indexOf(settings.parity) === -1) { + throw new TypeError(`Invalid "parity": ${settings.parity}`); + } + + FLOWCONTROLS.forEach((control) => { + if (typeof settings[control] !== 'boolean') { + throw new TypeError(`Invalid "${control}" is not boolean`); + } + }); + + settings.disconnectedCallback = this._disconnected.bind(this); + settings.dataCallback = settings.parser.bind(this, this); + + this.fd = null; + this.paused = true; + this.opening = false; + this.closing = false; + + if (process.platform !== 'win32') { + this.bufferSize = settings.bufferSize; + this.readable = true; + this.reading = false; + } + + this.options = settings; + + if (this.options.autoOpen) { + // is nextTick necessary? + process.nextTick(this.open.bind(this, callback)); + } +} + +util.inherits(SerialPort, stream.Stream); + +SerialPort.prototype._error = function(error, callback) { + if (callback) { + callback.call(this, error); + } else { + this.emit('error', error); + } +}; + +SerialPort.prototype.open = function(callback) { + if (this.isOpen()) { + return this._error(new Error('Port is already open'), callback); + } + + if (this.opening) { + return this._error(new Error('Port is opening'), callback); + } + + this.paused = true; + this.readable = true; + this.reading = false; + this.opening = true; + + SerialPortBinding.open(this.path, this.options, (err, fd) => { + this.opening = false; + if (err) { + debug('SerialPortBinding.open had an error', err); + return this._error(err, callback); + } + this.fd = fd; + this.paused = false; + + if (process.platform !== 'win32') { + this.serialPoller = new SerialPortBinding.SerialportPoller(this.fd, (err) => { + if (!err) { + this._read(); + } else { + this._disconnected(err); + } + }); + this.serialPoller.start(); + } + + this.emit('open'); + if (callback) { + callback.call(this, null); + } + }); +}; + +SerialPort.prototype.update = function(options, callback) { + if (!this.isOpen()) { + debug('update attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + var correctedOptions = correctOptions(options); + var settings = Object.assign({}, defaultSettings, correctedOptions); + this.options.baudRate = settings.baudRate; + + SerialPortBinding.update(this.fd, this.options, (err) => { + if (err) { + return this._error(err, callback); + } + if (callback) { + callback.call(this, null); + } + }); +}; + +SerialPort.prototype.isOpen = function() { + return this.fd !== null && !this.closing; +}; + +SerialPort.prototype.write = function(buffer, ending, callback) { + if (!this.isOpen()) { + debug('write attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + if (!Buffer.isBuffer(buffer)) { + buffer = Buffer.from(buffer); + } + + switch (ending) { + case 'Newline': + buffer = Buffer.concat([buffer, Buffer.from('\n')]); + break; + case 'Carriage return': + buffer = Buffer.concat([buffer, Buffer.from('\r')]); + break; + case 'Both NL & CR': + buffer = Buffer.concat([buffer, Buffer.from('\r\n')]); + break; + default: + break; + } + + debug(`write ${buffer.length} bytes of data`); + SerialPortBinding.write(this.fd, buffer, (err) => { + if (err) { + debug('SerialPortBinding.write had an error', err); + return this._error(err, callback); + } + if (callback) { + callback.call(this, null); + } + }); +}; + +if (process.platform !== 'win32') { + SerialPort.prototype._read = function() { + if (!this.readable || this.paused || this.reading || this.closing) { + return; + } + + this.reading = true; + + if (!this.pool || this.pool.length - this.pool.used < kMinPoolSpace) { + // discard the old pool. Can't add to the free list because + // users might have references to slices on it. + this.pool = Buffer.alloc(kPoolSize); + this.pool.used = 0; + } + + // Grab another reference to the pool in the case that while we're in the + // thread pool another read() finishes up the pool, and allocates a new + // one. + var toRead = Math.min(this.pool.length - this.pool.used, ~~this.bufferSize); + var start = this.pool.used; + + var _afterRead = (err, bytesRead, readPool, bytesRequested) => { + this.reading = false; + if (err) { + if (err.code && err.code === 'EAGAIN') { + if (this.isOpen()) { + this.serialPoller.start(); + } + // handle edge case were mac/unix doesn't clearly know the error. + } else if (err.code && (err.code === 'EBADF' || err.code === 'ENXIO' || (err.errno === -1 || err.code === 'UNKNOWN'))) { + this._disconnected(err); + } else { + this.fd = null; + this.readable = false; + this.emit('error', err); + } + return; + } + + // Since we will often not read the number of bytes requested, + // let's mark the ones we didn't need as available again. + this.pool.used -= bytesRequested - bytesRead; + + if (bytesRead === 0) { + if (this.isOpen()) { + this.serialPoller.start(); + } + } else { + var b = this.pool.slice(start, start + bytesRead); + + // do not emit events if the stream is paused + if (this.paused) { + if (!this.buffer) { + this.buffer = Buffer.alloc(0); + } + this.buffer = Buffer.concat([this.buffer, b]); + return; + } + this._emitData(b); + + // do not emit events anymore after we declared the stream unreadable + if (!this.readable) { + return; + } + this._read(); + } + }; + + fs.read(this.fd, this.pool, this.pool.used, toRead, null, (err, bytesRead) => { + var readPool = this.pool; + var bytesRequested = toRead; + _afterRead(err, bytesRead, readPool, bytesRequested); + }); + + this.pool.used += toRead; + }; + + SerialPort.prototype._emitData = function(data) { + this.options.dataCallback(data); + }; + + SerialPort.prototype.pause = function() { + this.paused = true; + }; + + SerialPort.prototype.resume = function() { + this.paused = false; + + if (this.buffer) { + var buffer = this.buffer; + this.buffer = null; + this._emitData(buffer); + } + + // No longer open? + if (!this.isOpen()) { + return; + } + + this._read(); + }; +} // if !'win32' + +SerialPort.prototype._disconnected = function(err) { + this.paused = true; + this.emit('disconnect', err); + if (this.closing) { + return; + } + + if (this.fd === null) { + return; + } + + this.closing = true; + if (process.platform !== 'win32') { + this.readable = false; + this.serialPoller.close(); + } + + SerialPortBinding.close(this.fd, (err) => { + this.closing = false; + if (err) { + debug('Disconnect close completed with error: ', err); + } + this.fd = null; + this.emit('close'); + }); +}; + +SerialPort.prototype.close = function(callback) { + this.paused = true; + + if (this.closing) { + debug('close attempted, but port is already closing'); + return this._error(new Error('Port is not open'), callback); + } + + if (!this.isOpen()) { + debug('close attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + this.closing = true; + + // Stop polling before closing the port. + if (process.platform !== 'win32') { + this.readable = false; + this.serialPoller.close(); + } + SerialPortBinding.close(this.fd, (err) => { + this.closing = false; + if (err) { + debug('SerialPortBinding.close had an error', err); + return this._error(err, callback); + } + + this.fd = null; + this.emit('close'); + if (callback) { + callback.call(this, null); + } + }); +}; + +SerialPort.prototype.flush = function(callback) { + if (!this.isOpen()) { + debug('flush attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + SerialPortBinding.flush(this.fd, (err, result) => { + if (err) { + debug('SerialPortBinding.flush had an error', err); + return this._error(err, callback); + } + if (callback) { + callback.call(this, null, result); + } + }); +}; + +SerialPort.prototype.set = function(options, callback) { + if (!this.isOpen()) { + debug('set attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + options = options || {}; + if (!callback && typeof options === 'function') { + callback = options; + options = {}; + } + + var settings = {}; + for (var i = SET_OPTIONS.length - 1; i >= 0; i--) { + var flag = SET_OPTIONS[i]; + if (options[flag] !== undefined) { + settings[flag] = options[flag]; + } else { + settings[flag] = defaultSetFlags[flag]; + } + } + + SerialPortBinding.set(this.fd, settings, (err) => { + if (err) { + debug('SerialPortBinding.set had an error', err); + return this._error(err, callback); + } + if (callback) { + callback.call(this, null); + } + }); +}; + +SerialPort.prototype.drain = function(callback) { + if (!this.isOpen()) { + debug('drain attempted, but port is not open'); + return this._error(new Error('Port is not open'), callback); + } + + SerialPortBinding.drain(this.fd, (err) => { + if (err) { + debug('SerialPortBinding.drain had an error', err); + return this._error(err, callback); + } + if (callback) { + callback.call(this, null); + } + }); +}; + +SerialPort.parsers = parsers; +SerialPort.list = SerialPortBinding.list; + +// Write a depreciation warning once +Object.defineProperty(SerialPort, 'SerialPort', { + get: function() { + // console.warn('DEPRECATION: Please use `require(\'serialport\')` instead of `require(\'serialport\').SerialPort`'); + Object.defineProperty(SerialPort, 'SerialPort', { + value: SerialPort + }); + return SerialPort; + }, + configurable: true +}); + +module.exports = SerialPort; diff --git a/vendor/node-usb-native/license b/vendor/node-usb-native/license new file mode 100644 index 000000000..8cff664c4 --- /dev/null +++ b/vendor/node-usb-native/license @@ -0,0 +1,19 @@ +Copyright (c) 2013 Kaba AG + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/node-usb-native/package.json b/vendor/node-usb-native/package.json new file mode 100644 index 000000000..c62428c71 --- /dev/null +++ b/vendor/node-usb-native/package.json @@ -0,0 +1,34 @@ +{ + "name": "usb-native", + "version": "0.0.1", + "description": "node-usb && node-serialport combined.", + "main": "lib/index.js", + "gypfile": true, + "keywords": [ + "usb", + "device", + "hardware", + "list", + "insert", + "add", + "remove", + "change", + "plug", + "unplug", + "notification" + ], + "license": "MIT", + "scripts": { + "install": "node-gyp rebuild --target=1.6.6 --arch=x64 --dist-url=https://atom.io/download/electron" + }, + "dependencies": { + "eventemitter2": "^4.1.0" + }, + "devDependencies": { + "chai": "^3.0.0", + "chai-as-promised": "^5.1.0", + "chalk": "^1.0.0", + "mocha": "^2.2.5", + "nan": "^2.12.1" + } +} \ No newline at end of file diff --git a/vendor/node-usb-native/src/combined.cpp b/vendor/node-usb-native/src/combined.cpp new file mode 100644 index 000000000..a7f4cc0ff --- /dev/null +++ b/vendor/node-usb-native/src/combined.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { + void init_serialport(v8::Handle target); +#ifndef DISABLE_USB_DETECTOR + void init_detector(v8::Handle target); +#endif + void initAll(v8::Handle target) { + init_serialport(target); + #ifndef DISABLE_USB_DETECTOR + init_detector(target); + #endif + } +} + +NODE_MODULE(detection, initAll); diff --git a/vendor/node-usb-native/src/detection.cpp b/vendor/node-usb-native/src/detection.cpp new file mode 100644 index 000000000..5a16729f7 --- /dev/null +++ b/vendor/node-usb-native/src/detection.cpp @@ -0,0 +1,222 @@ +#include "detection.h" + + +#define OBJECT_ITEM_LOCATION_ID "locationId" +#define OBJECT_ITEM_VENDOR_ID "vendorId" +#define OBJECT_ITEM_PRODUCT_ID "productId" +#define OBJECT_ITEM_DEVICE_NAME "deviceName" +#define OBJECT_ITEM_MANUFACTURER "manufacturer" +#define OBJECT_ITEM_SERIAL_NUMBER "serialNumber" +#define OBJECT_ITEM_DEVICE_ADDRESS "deviceAddress" + + +Nan::Callback* addedCallback; +bool isAddedRegistered = false; + +Nan::Callback* removedCallback; +bool isRemovedRegistered = false; + +void RegisterAdded(const Nan::FunctionCallbackInfo& args) { + Nan::HandleScope scope; + + v8::Local callback; + + if (args.Length() == 0) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + if (args.Length() == 1) { + // callback + if(!args[0]->IsFunction()) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + callback = args[0].As(); + } + + addedCallback = new Nan::Callback(callback); + isAddedRegistered = true; +} + +void NotifyAdded(ListResultItem_t* it) { + Nan::HandleScope scope; + + if (it == NULL) { + return; + } + + if (isAddedRegistered){ + v8::Local argv[1]; + v8::Local item = Nan::New(); + item->Set(Nan::New(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New(it->locationId)); + item->Set(Nan::New(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New(it->vendorId)); + item->Set(Nan::New(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New(it->productId)); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New(it->deviceName.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New(it->manufacturer.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New(it->serialNumber.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New(it->deviceAddress)); + argv[0] = item; + + addedCallback->Call(1, argv); + } +} + +void RegisterRemoved(const Nan::FunctionCallbackInfo& args) { + Nan::HandleScope scope; + + v8::Local callback; + + if (args.Length() == 0) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + if (args.Length() == 1) { + // callback + if(!args[0]->IsFunction()) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + callback = args[0].As(); + } + + removedCallback = new Nan::Callback(callback); + isRemovedRegistered = true; +} + +void NotifyRemoved(ListResultItem_t* it) { + Nan::HandleScope scope; + + if (it == NULL) { + return; + } + + if (isRemovedRegistered) { + v8::Local argv[1]; + v8::Local item = Nan::New(); + item->Set(Nan::New(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New(it->locationId)); + item->Set(Nan::New(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New(it->vendorId)); + item->Set(Nan::New(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New(it->productId)); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New(it->deviceName.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New(it->manufacturer.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New(it->serialNumber.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New(it->deviceAddress)); + argv[0] = item; + + removedCallback->Call(1, argv); + } +} + +void Find(const Nan::FunctionCallbackInfo& args) { + Nan::HandleScope scope; + + int vid = 0; + int pid = 0; + v8::Local callback; + + if (args.Length() == 0) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + if (args.Length() == 3) { + if (args[0]->IsNumber() && args[1]->IsNumber()) { + vid = (int) args[0]->NumberValue(); + pid = (int) args[1]->NumberValue(); + } + + // callback + if(!args[2]->IsFunction()) { + return Nan::ThrowTypeError("Third argument must be a function"); + } + + callback = args[2].As(); + } + + if (args.Length() == 2) { + if (args[0]->IsNumber()) { + vid = (int) args[0]->NumberValue(); + } + + // callback + if(!args[1]->IsFunction()) { + return Nan::ThrowTypeError("Second argument must be a function"); + } + + callback = args[1].As(); + } + + if (args.Length() == 1) { + // callback + if(!args[0]->IsFunction()) { + return Nan::ThrowTypeError("First argument must be a function"); + } + + callback = args[0].As(); + } + + ListBaton* baton = new ListBaton(); + strcpy(baton->errorString, ""); + baton->callback = new Nan::Callback(callback); + baton->vid = vid; + baton->pid = pid; + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_Find, (uv_after_work_cb)EIO_AfterFind); +} + +void EIO_AfterFind(uv_work_t* req) { + Nan::HandleScope scope; + + ListBaton* data = static_cast(req->data); + + v8::Local argv[2]; + if(data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + argv[1] = Nan::Undefined(); + } + else { + v8::Local results = Nan::New(); + int i = 0; + for(std::list::iterator it = data->results.begin(); it != data->results.end(); it++, i++) { + v8::Local item = Nan::New(); + item->Set(Nan::New(OBJECT_ITEM_LOCATION_ID).ToLocalChecked(), Nan::New((*it)->locationId)); + item->Set(Nan::New(OBJECT_ITEM_VENDOR_ID).ToLocalChecked(), Nan::New((*it)->vendorId)); + item->Set(Nan::New(OBJECT_ITEM_PRODUCT_ID).ToLocalChecked(), Nan::New((*it)->productId)); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_NAME).ToLocalChecked(), Nan::New((*it)->deviceName.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_MANUFACTURER).ToLocalChecked(), Nan::New((*it)->manufacturer.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_SERIAL_NUMBER).ToLocalChecked(), Nan::New((*it)->serialNumber.c_str()).ToLocalChecked()); + item->Set(Nan::New(OBJECT_ITEM_DEVICE_ADDRESS).ToLocalChecked(), Nan::New((*it)->deviceAddress)); + results->Set(i, item); + } + argv[0] = Nan::Undefined(); + argv[1] = results; + } + + data->callback->Call(2, argv); + + for(std::list::iterator it = data->results.begin(); it != data->results.end(); it++) { + delete *it; + } + delete data; + delete req; +} + +void StartMonitoring(const Nan::FunctionCallbackInfo& args) { + Start(); +} + +void StopMonitoring(const Nan::FunctionCallbackInfo& args) { + Stop(); +} + +extern "C" { + void init_detector (v8::Handle target) { + Nan::SetMethod(target, "find", Find); + Nan::SetMethod(target, "registerAdded", RegisterAdded); + Nan::SetMethod(target, "registerRemoved", RegisterRemoved); + Nan::SetMethod(target, "startMonitoring", StartMonitoring); + Nan::SetMethod(target, "stopMonitoring", StopMonitoring); + InitDetection(); + + } +} \ No newline at end of file diff --git a/vendor/node-usb-native/src/detection.h b/vendor/node-usb-native/src/detection.h new file mode 100644 index 000000000..39b21c735 --- /dev/null +++ b/vendor/node-usb-native/src/detection.h @@ -0,0 +1,42 @@ + +#ifndef _USB_DETECTION_H +#define _USB_DETECTION_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "deviceList.h" + +void Find(const Nan::FunctionCallbackInfo& args); +void EIO_Find(uv_work_t* req); +void EIO_AfterFind(uv_work_t* req); +void InitDetection(); +void StartMonitoring(const Nan::FunctionCallbackInfo& args); +void Start(); +void StopMonitoring(const Nan::FunctionCallbackInfo& args); +void Stop(); + + +struct ListBaton { + public: + //v8::Persistent callback; + Nan::Callback* callback; + std::list results; + char errorString[1024]; + int vid; + int pid; +}; + +void RegisterAdded(const Nan::FunctionCallbackInfo& args); +void NotifyAdded(ListResultItem_t* it); +void RegisterRemoved(const Nan::FunctionCallbackInfo& args); +void NotifyRemoved(ListResultItem_t* it); + +#endif diff --git a/vendor/node-usb-native/src/detection_linux.cpp b/vendor/node-usb-native/src/detection_linux.cpp new file mode 100644 index 000000000..aaa618f72 --- /dev/null +++ b/vendor/node-usb-native/src/detection_linux.cpp @@ -0,0 +1,317 @@ +#include +#include + +#include "detection.h" +#include "deviceList.h" + +using namespace std; + + + +/********************************** + * Local defines + **********************************/ +#define DEVICE_ACTION_ADDED "add" +#define DEVICE_ACTION_REMOVED "remove" + +#define DEVICE_TYPE_DEVICE "usb_device" + +#define DEVICE_PROPERTY_NAME "ID_MODEL" +#define DEVICE_PROPERTY_SERIAL "ID_SERIAL_SHORT" +#define DEVICE_PROPERTY_VENDOR "ID_VENDOR" + + +/********************************** + * Local typedefs + **********************************/ + + + +/********************************** + * Local Variables + **********************************/ +ListResultItem_t* currentItem; +bool isAdded; + +struct udev *udev; +struct udev_enumerate *enumerate; +struct udev_list_entry *devices, *dev_list_entry; +struct udev_device *dev; + +struct udev_monitor *mon; +int fd; + +pthread_t thread; +pthread_mutex_t notify_mutex; +pthread_cond_t notifyNewDevice; +pthread_cond_t notifyDeviceHandled; + +bool newDeviceAvailable = false; +bool deviceHandled = true; + +bool isRunning = false; +/********************************** + * Local Helper Functions protoypes + **********************************/ +void BuildInitialDeviceList(); + +void* ThreadFunc(void* ptr); +void WaitForDeviceHandled(); +void SignalDeviceHandled(); +void WaitForNewDevice(); +void SignalDeviceAvailable(); + +/********************************** + * Public Functions + **********************************/ +void NotifyAsync(uv_work_t* req) { + WaitForNewDevice(); +} + +void NotifyFinished(uv_work_t* req) { + if (isRunning) { + if (isAdded) { + NotifyAdded(currentItem); + } + else { + NotifyRemoved(currentItem); + } + } + + // Delete Item in case of removal + if(isAdded == false) { + delete currentItem; + } + + SignalDeviceHandled(); + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); +} + +void Start() { + isRunning = true; +} + +void Stop() { + isRunning = false; + pthread_mutex_lock(¬ify_mutex); + pthread_cond_signal(¬ifyNewDevice); + pthread_mutex_unlock(¬ify_mutex); +} + +void InitDetection() { + /* Create the udev object */ + udev = udev_new(); + if (!udev) + { + printf("Can't create udev\n"); + return; + } + + /* Set up a monitor to monitor devices */ + mon = udev_monitor_new_from_netlink(udev, "udev"); + udev_monitor_enable_receiving(mon); + + /* Get the file descriptor (fd) for the monitor. + This fd will get passed to select() */ + fd = udev_monitor_get_fd(mon); + + BuildInitialDeviceList(); + + pthread_mutex_init(¬ify_mutex, NULL); + pthread_cond_init(¬ifyNewDevice, NULL); + pthread_cond_init(¬ifyDeviceHandled, NULL); + + uv_work_t* req = new uv_work_t(); + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); + + pthread_create(&thread, NULL, ThreadFunc, NULL); + + Start(); +} + + +void EIO_Find(uv_work_t* req) { + ListBaton* data = static_cast(req->data); + + CreateFilteredList(&data->results, data->vid, data->pid); +} + +/********************************** + * Local Functions + **********************************/ +void WaitForDeviceHandled() { + pthread_mutex_lock(¬ify_mutex); + if(deviceHandled == false) { + pthread_cond_wait(¬ifyDeviceHandled, ¬ify_mutex); + } + deviceHandled = false; + pthread_mutex_unlock(¬ify_mutex); +} + +void SignalDeviceHandled() { + pthread_mutex_lock(¬ify_mutex); + deviceHandled = true; + pthread_cond_signal(¬ifyDeviceHandled); + pthread_mutex_unlock(¬ify_mutex); +} + +void WaitForNewDevice() { + pthread_mutex_lock(¬ify_mutex); + if(newDeviceAvailable == false){ + pthread_cond_wait(¬ifyNewDevice, ¬ify_mutex); + } + newDeviceAvailable = false; + pthread_mutex_unlock(¬ify_mutex); +} + +void SignalDeviceAvailable() { + pthread_mutex_lock(¬ify_mutex); + newDeviceAvailable = true; + pthread_cond_signal(¬ifyNewDevice); + pthread_mutex_unlock(¬ify_mutex); +} + + + ListResultItem_t* GetProperties(struct udev_device* dev, ListResultItem_t* item) { + struct udev_list_entry* sysattrs; + struct udev_list_entry* entry; + sysattrs = udev_device_get_properties_list_entry(dev); + udev_list_entry_foreach(entry, sysattrs) { + const char *name, *value; + name = udev_list_entry_get_name(entry); + value = udev_list_entry_get_value(entry); + + if(strcmp(name, DEVICE_PROPERTY_NAME) == 0) { + item->deviceName = value; + } + else if(strcmp(name, DEVICE_PROPERTY_SERIAL) == 0) { + item->serialNumber = value; + } + else if(strcmp(name, DEVICE_PROPERTY_VENDOR) == 0) { + item->manufacturer = value; + } + } + item->vendorId = strtol(udev_device_get_sysattr_value(dev,"idVendor"), NULL, 16); + item->productId = strtol(udev_device_get_sysattr_value(dev,"idProduct"), NULL, 16); + item->deviceAddress = 0; + item->locationId = 0; + + return item; +} + +void DeviceAdded(struct udev_device* dev) { + DeviceItem_t* item = new DeviceItem_t(); + GetProperties(dev, &item->deviceParams); + + AddItemToList((char *)udev_device_get_devnode(dev), item); + + currentItem = &item->deviceParams; + isAdded = true; + + SignalDeviceAvailable(); +} + +void DeviceRemoved(struct udev_device* dev) { + ListResultItem_t* item = NULL; + + if(IsItemAlreadyStored((char *)udev_device_get_devnode(dev))) { + DeviceItem_t* deviceItem = GetItemFromList((char *)udev_device_get_devnode(dev)); + if(deviceItem) { + item = CopyElement(&deviceItem->deviceParams); + } + RemoveItemFromList(deviceItem); + delete deviceItem; + } + + if(item == NULL) { + item = new ListResultItem_t(); + GetProperties(dev, item); + } + + currentItem = item; + isAdded = false; + + SignalDeviceAvailable(); +} + + +void* ThreadFunc(void* ptr) { + while (1) { + /* Make the call to receive the device. + select() ensured that this will not block. */ + dev = udev_monitor_receive_device(mon); + if (dev) { + if(udev_device_get_devtype(dev) && strcmp(udev_device_get_devtype(dev), DEVICE_TYPE_DEVICE) == 0) { + if(strcmp(udev_device_get_action(dev), DEVICE_ACTION_ADDED) == 0) { + WaitForDeviceHandled(); + DeviceAdded(dev); + } + else if(strcmp(udev_device_get_action(dev), DEVICE_ACTION_REMOVED) == 0) { + WaitForDeviceHandled(); + DeviceRemoved(dev); + } + } + udev_device_unref(dev); + } + } + + return NULL; +} + + +void BuildInitialDeviceList() { + /* Create a list of the devices */ + enumerate = udev_enumerate_new(udev); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + /* For each item enumerated, print out its information. + udev_list_entry_foreach is a macro which expands to + a loop. The loop will be executed for each member in + devices, setting dev_list_entry to a list entry + which contains the device's path in /sys. */ + udev_list_entry_foreach(dev_list_entry, devices) { + const char *path; + + /* Get the filename of the /sys entry for the device + and create a udev_device object (dev) representing it */ + path = udev_list_entry_get_name(dev_list_entry); + dev = udev_device_new_from_syspath(udev, path); + + /* usb_device_get_devnode() returns the path to the device node + itself in /dev. */ + if(udev_device_get_devnode(dev) == NULL || udev_device_get_sysattr_value(dev,"idVendor") == NULL) { + continue; + } + + /* From here, we can call get_sysattr_value() for each file + in the device's /sys entry. The strings passed into these + functions (idProduct, idVendor, serial, etc.) correspond + directly to the files in the /sys directory which + represents the USB device. Note that USB strings are + Unicode, UCS2 encoded, but the strings returned from + udev_device_get_sysattr_value() are UTF-8 encoded. */ + + DeviceItem_t* item = new DeviceItem_t(); + item->deviceParams.vendorId = strtol (udev_device_get_sysattr_value(dev,"idVendor"), NULL, 16); + item->deviceParams.productId = strtol (udev_device_get_sysattr_value(dev,"idProduct"), NULL, 16); + if(udev_device_get_sysattr_value(dev,"product") != NULL) { + item->deviceParams.deviceName = udev_device_get_sysattr_value(dev,"product"); + } + if(udev_device_get_sysattr_value(dev,"manufacturer") != NULL) { + item->deviceParams.manufacturer = udev_device_get_sysattr_value(dev,"manufacturer"); + } + if(udev_device_get_sysattr_value(dev,"serial") != NULL) { + item->deviceParams.serialNumber = udev_device_get_sysattr_value(dev, "serial"); + } + item->deviceParams.deviceAddress = 0; + item->deviceParams.locationId = 0; + + item->deviceState = DeviceState_Connect; + + AddItemToList((char *)udev_device_get_devnode(dev), item); + + udev_device_unref(dev); + } + /* Free the enumerator object */ + udev_enumerate_unref(enumerate); +} diff --git a/vendor/node-usb-native/src/detection_mac.cpp b/vendor/node-usb-native/src/detection_mac.cpp new file mode 100644 index 000000000..30f161cd1 --- /dev/null +++ b/vendor/node-usb-native/src/detection_mac.cpp @@ -0,0 +1,461 @@ +#include "detection.h" +#include "deviceList.h" + +#include + +#include +#include +#include +#include + +#include +#include + +#include + + +typedef struct DeviceListItem { + io_object_t notification; + IOUSBDeviceInterface** deviceInterface; + DeviceItem_t* deviceItem; +} stDeviceListItem; + +static IONotificationPortRef gNotifyPort; +static io_iterator_t gAddedIter; +static CFRunLoopRef gRunLoop; + + +CFMutableDictionaryRef matchingDict; +CFRunLoopSourceRef runLoopSource; + +static pthread_t lookupThread; + +pthread_mutex_t notify_mutex; +pthread_cond_t notifyNewDevice; +pthread_cond_t notifyDeviceHandled; + +bool newDeviceAvailable = false; +bool deviceHandled = true; + +ListResultItem_t* notify_item; +bool isAdded = false; +bool isRunning = false; +bool intialDeviceImport = true; + +void WaitForDeviceHandled(); +void SignalDeviceHandled(); +void WaitForNewDevice(); +void SignalDeviceAvailable(); + +//================================================================================================ +// +// DeviceRemoved +// +// This routine will get called whenever any kIOGeneralInterest notification happens. We are +// interested in the kIOMessageServiceIsTerminated message so that's what we look for. Other +// messages are defined in IOMessage.h. +// +//================================================================================================ +void DeviceRemoved(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) { + kern_return_t kr; + stDeviceListItem* deviceListItem = (stDeviceListItem *) refCon; + DeviceItem_t* deviceItem = deviceListItem->deviceItem; + + if(messageType == kIOMessageServiceIsTerminated) { + if(deviceListItem->deviceInterface) { + kr = (*deviceListItem->deviceInterface)->Release(deviceListItem->deviceInterface); + } + + kr = IOObjectRelease(deviceListItem->notification); + + + ListResultItem_t* item = NULL; + if(deviceItem) { + item = CopyElement(&deviceItem->deviceParams); + RemoveItemFromList(deviceItem); + delete deviceItem; + } + else { + item = new ListResultItem_t(); + } + + WaitForDeviceHandled(); + notify_item = item; + isAdded = false; + SignalDeviceAvailable(); + + } +} + +//================================================================================================ +// +// DeviceAdded +// +// This routine is the callback for our IOServiceAddMatchingNotification. When we get called +// we will look at all the devices that were added and we will: +// +// 1. Create some private data to relate to each device (in this case we use the service's name +// and the location ID of the device +// 2. Submit an IOServiceAddInterestNotification of type kIOGeneralInterest for this device, +// using the refCon field to store a pointer to our private data. When we get called with +// this interest notification, we can grab the refCon and access our private data. +// +//================================================================================================ +void DeviceAdded(void *refCon, io_iterator_t iterator) { + kern_return_t kr; + io_service_t usbDevice; + IOCFPlugInInterface **plugInInterface = NULL; + SInt32 score; + HRESULT res; + + while((usbDevice = IOIteratorNext(iterator))) { + io_name_t deviceName; + CFStringRef deviceNameAsCFString; + UInt32 locationID; + UInt16 vendorId; + UInt16 productId; + UInt16 addr; + + DeviceItem_t* deviceItem = new DeviceItem_t(); + + // Get the USB device's name. + kr = IORegistryEntryGetName(usbDevice, deviceName); + if(KERN_SUCCESS != kr) { + deviceName[0] = '\0'; + } + + deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName, kCFStringEncodingASCII); + + + if(deviceNameAsCFString) { + Boolean result; + char deviceName[MAXPATHLEN]; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString(deviceNameAsCFString, + deviceName, + sizeof(deviceName), + kCFStringEncodingUTF8); + + if(result) { + deviceItem->deviceParams.deviceName = deviceName; + } + + CFRelease(deviceNameAsCFString); + } + + CFStringRef manufacturerAsCFString = (CFStringRef)IORegistryEntrySearchCFProperty( + usbDevice, + kIOServicePlane, + CFSTR(kUSBVendorString), + kCFAllocatorDefault, + kIORegistryIterateRecursively + ); + + if(manufacturerAsCFString) { + Boolean result; + char manufacturer[MAXPATHLEN]; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString( + manufacturerAsCFString, + manufacturer, + sizeof(manufacturer), + kCFStringEncodingUTF8 + ); + + if(result) { + deviceItem->deviceParams.manufacturer = manufacturer; + } + + CFRelease(manufacturerAsCFString); + } + + CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty( + usbDevice, + kIOServicePlane, + CFSTR(kUSBSerialNumberString), + kCFAllocatorDefault, + kIORegistryIterateRecursively + ); + + if(serialNumberAsCFString) { + Boolean result; + char serialNumber[MAXPATHLEN]; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString( + serialNumberAsCFString, + serialNumber, + sizeof(serialNumber), + kCFStringEncodingUTF8 + ); + + if(result) { + deviceItem->deviceParams.serialNumber = serialNumber; + } + + CFRelease(serialNumberAsCFString); + } + + + // Now, get the locationID of this device. In order to do this, we need to create an IOUSBDeviceInterface + // for our device. This will create the necessary connections between our userland application and the + // kernel object for the USB Device. + kr = IOCreatePlugInInterfaceForService(usbDevice, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); + + if((kIOReturnSuccess != kr) || !plugInInterface) { + fprintf(stderr, "IOCreatePlugInInterfaceForService returned 0x%08x.\n", kr); + continue; + } + + stDeviceListItem *deviceListItem = new stDeviceListItem(); + + // Use the plugin interface to retrieve the device interface. + res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &deviceListItem->deviceInterface); + + // Now done with the plugin interface. + (*plugInInterface)->Release(plugInInterface); + + if(res || deviceListItem->deviceInterface == NULL) { + fprintf(stderr, "QueryInterface returned %d.\n", (int) res); + continue; + } + + // Now that we have the IOUSBDeviceInterface, we can call the routines in IOUSBLib.h. + // In this case, fetch the locationID. The locationID uniquely identifies the device + // and will remain the same, even across reboots, so long as the bus topology doesn't change. + + kr = (*deviceListItem->deviceInterface)->GetLocationID(deviceListItem->deviceInterface, &locationID); + if(KERN_SUCCESS != kr) { + fprintf(stderr, "GetLocationID returned 0x%08x.\n", kr); + continue; + } + deviceItem->deviceParams.locationId = locationID; + + + kr = (*deviceListItem->deviceInterface)->GetDeviceAddress(deviceListItem->deviceInterface, &addr); + if(KERN_SUCCESS != kr) { + fprintf(stderr, "GetDeviceAddress returned 0x%08x.\n", kr); + continue; + } + deviceItem->deviceParams.deviceAddress = addr; + + + kr = (*deviceListItem->deviceInterface)->GetDeviceVendor(deviceListItem->deviceInterface, &vendorId); + if(KERN_SUCCESS != kr) { + fprintf(stderr, "GetDeviceVendor returned 0x%08x.\n", kr); + continue; + } + deviceItem->deviceParams.vendorId = vendorId; + + kr = (*deviceListItem->deviceInterface)->GetDeviceProduct(deviceListItem->deviceInterface, &productId); + if(KERN_SUCCESS != kr) { + fprintf(stderr, "GetDeviceProduct returned 0x%08x.\n", kr); + continue; + } + deviceItem->deviceParams.productId = productId; + + + // Extract path name as unique key + io_string_t pathName; + IORegistryEntryGetPath(usbDevice, kIOServicePlane, pathName); + deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, pathName, kCFStringEncodingASCII); + char cPathName[MAXPATHLEN]; + + if(deviceNameAsCFString) { + Boolean result; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString( + deviceNameAsCFString, + cPathName, + sizeof(cPathName), + kCFStringEncodingUTF8 + ); + + + CFRelease(deviceNameAsCFString); + } + + AddItemToList(cPathName, deviceItem); + deviceListItem->deviceItem = deviceItem; + + if(intialDeviceImport == false) { + WaitForDeviceHandled(); + notify_item = &deviceItem->deviceParams; + isAdded = true; + SignalDeviceAvailable(); + } + + // Register for an interest notification of this device being removed. Use a reference to our + // private data as the refCon which will be passed to the notification callback. + kr = IOServiceAddInterestNotification( + gNotifyPort, // notifyPort + usbDevice, // service + kIOGeneralInterest, // interestType + DeviceRemoved, // callback + deviceListItem, // refCon + &(deviceListItem->notification) // notification + ); + + if(KERN_SUCCESS != kr) { + printf("IOServiceAddInterestNotification returned 0x%08x.\n", kr); + } + + // Done with this USB device; release the reference added by IOIteratorNext + kr = IOObjectRelease(usbDevice); + } +} + + +void WaitForDeviceHandled() { + pthread_mutex_lock(¬ify_mutex); + if(deviceHandled == false) { + pthread_cond_wait(¬ifyDeviceHandled, ¬ify_mutex); + } + deviceHandled = false; + pthread_mutex_unlock(¬ify_mutex); +} + +void SignalDeviceHandled() { + pthread_mutex_lock(¬ify_mutex); + deviceHandled = true; + pthread_cond_signal(¬ifyDeviceHandled); + pthread_mutex_unlock(¬ify_mutex); +} + +void WaitForNewDevice() { + pthread_mutex_lock(¬ify_mutex); + if(newDeviceAvailable == false) { + pthread_cond_wait(¬ifyNewDevice, ¬ify_mutex); + } + newDeviceAvailable = false; + pthread_mutex_unlock(¬ify_mutex); +} + +void SignalDeviceAvailable() { + pthread_mutex_lock(¬ify_mutex); + newDeviceAvailable = true; + pthread_cond_signal(¬ifyNewDevice); + pthread_mutex_unlock(¬ify_mutex); +} + + +void *RunLoop(void * arg) { + + runLoopSource = IONotificationPortGetRunLoopSource(gNotifyPort); + + gRunLoop = CFRunLoopGetCurrent(); + CFRunLoopAddSource(gRunLoop, runLoopSource, kCFRunLoopDefaultMode); + + // Start the run loop. Now we'll receive notifications. + CFRunLoopRun(); + + // We should never get here + fprintf(stderr, "Unexpectedly back from CFRunLoopRun()!\n"); + + return NULL; +} + +void NotifyAsync(uv_work_t* req) { + WaitForNewDevice(); +} + +void NotifyFinished(uv_work_t* req) { + if(isRunning) { + if(isAdded) { + NotifyAdded(notify_item); + } + else { + NotifyRemoved(notify_item); + } + } + + // Delete Item in case of removal + if(isAdded == false) { + delete notify_item; + } + + if(isRunning) { + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); + } + SignalDeviceHandled(); +} + +void Start() { + isRunning = true; +} + +void Stop() { + isRunning = false; + pthread_mutex_lock(¬ify_mutex); + pthread_cond_signal(¬ifyNewDevice); + pthread_mutex_unlock(¬ify_mutex); +} + +void InitDetection() { + + kern_return_t kr; + + // Set up the matching criteria for the devices we're interested in. The matching criteria needs to follow + // the same rules as kernel drivers: mainly it needs to follow the USB Common Class Specification, pp. 6-7. + // See also Technical Q&A QA1076 "Tips on USB driver matching on Mac OS X" + // . + // One exception is that you can use the matching dictionary "as is", i.e. without adding any matching + // criteria to it and it will match every IOUSBDevice in the system. IOServiceAddMatchingNotification will + // consume this dictionary reference, so there is no need to release it later on. + + // Interested in instances of class + // IOUSBDevice and its subclasses + matchingDict = IOServiceMatching(kIOUSBDeviceClassName); + + if (matchingDict == NULL) { + fprintf(stderr, "IOServiceMatching returned NULL.\n"); + } + + // Create a notification port and add its run loop event source to our run loop + // This is how async notifications get set up. + + gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault); + + // Now set up a notification to be called when a device is first matched by I/O Kit. + kr = IOServiceAddMatchingNotification( + gNotifyPort, // notifyPort + kIOFirstMatchNotification, // notificationType + matchingDict, // matching + DeviceAdded, // callback + NULL, // refCon + &gAddedIter // notification + ); + + if (KERN_SUCCESS != kr) { + printf("IOServiceAddMatchingNotification returned 0x%08x.\n", kr); + } + + // Iterate once to get already-present devices and arm the notification + DeviceAdded(NULL, gAddedIter); + intialDeviceImport = false; + + + pthread_mutex_init(¬ify_mutex, NULL); + pthread_cond_init(¬ifyNewDevice, NULL); + pthread_cond_init(¬ifyDeviceHandled, NULL); + + int rc = pthread_create(&lookupThread, NULL, RunLoop, NULL); + if (rc) { + printf("ERROR; return code from pthread_create() is %d\n", rc); + exit(-1); + } + + uv_work_t* req = new uv_work_t(); + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); + + Start(); +} + +void EIO_Find(uv_work_t* req) { + ListBaton* data = static_cast(req->data); + + CreateFilteredList(&data->results, data->vid, data->pid); +} diff --git a/vendor/node-usb-native/src/detection_win.cpp b/vendor/node-usb-native/src/detection_win.cpp new file mode 100644 index 000000000..eb02e18c4 --- /dev/null +++ b/vendor/node-usb-native/src/detection_win.cpp @@ -0,0 +1,471 @@ +#include +#include +#include +#include +#include + + +// Include Windows headers +#include +#include +#include +#include +#include + +#include "detection.h" +#include "deviceList.h" + +using namespace std; + +/********************************** + * Local defines + **********************************/ +#define VID_TAG "VID_" +#define PID_TAG "PID_" + +#define LIBRARY_NAME ("setupapi.dll") + + +#define DllImport __declspec(dllimport) + +#define MAX_THREAD_WINDOW_NAME 64 + +/********************************** + * Local typedefs + **********************************/ + + + +/********************************** + * Local Variables + **********************************/ +GUID GUID_DEVINTERFACE_USB_DEVICE = { + 0xA5DCBF10L, + 0x6530, + 0x11D2, + 0x90, + 0x1F, + 0x00, + 0xC0, + 0x4F, + 0xB9, + 0x51, + 0xED +}; + +HWND handle; +DWORD threadId; +HANDLE threadHandle; + +HANDLE deviceChangedRegisteredEvent; +HANDLE deviceChangedSentEvent; + +ListResultItem_t* currentDevice; +bool isAdded; +bool isRunning = false; + +HINSTANCE hinstLib; + + +typedef BOOL (WINAPI *_SetupDiEnumDeviceInfo) (HDEVINFO DeviceInfoSet, DWORD MemberIndex, PSP_DEVINFO_DATA DeviceInfoData); +typedef HDEVINFO (WINAPI *_SetupDiGetClassDevs) (const GUID *ClassGuid, PCTSTR Enumerator, HWND hwndParent, DWORD Flags); +typedef BOOL (WINAPI *_SetupDiDestroyDeviceInfoList) (HDEVINFO DeviceInfoSet); +typedef BOOL (WINAPI *_SetupDiGetDeviceInstanceId) (HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PTSTR DeviceInstanceId, DWORD DeviceInstanceIdSize, PDWORD RequiredSize); +typedef BOOL (WINAPI *_SetupDiGetDeviceRegistryProperty) (HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, DWORD Property, PDWORD PropertyRegDataType, PBYTE PropertyBuffer, DWORD PropertyBufferSize, PDWORD RequiredSize); + + +_SetupDiEnumDeviceInfo DllSetupDiEnumDeviceInfo; +_SetupDiGetClassDevs DllSetupDiGetClassDevs; +_SetupDiDestroyDeviceInfoList DllSetupDiDestroyDeviceInfoList; +_SetupDiGetDeviceInstanceId DllSetupDiGetDeviceInstanceId; +_SetupDiGetDeviceRegistryProperty DllSetupDiGetDeviceRegistryProperty; + + +/********************************** + * Local Helper Functions protoypes + **********************************/ +void UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam, DeviceState_t state); +DWORD WINAPI ListenerThread(LPVOID lpParam); + +void BuildInitialDeviceList(); + +void NotifyAsync(uv_work_t* req); +void NotifyFinished(uv_work_t* req); + +void ExtractDeviceInfo(HDEVINFO hDevInfo, SP_DEVINFO_DATA* pspDevInfoData, TCHAR* buf, DWORD buffSize, ListResultItem_t* resultItem); +bool CheckValidity(ListResultItem_t* item); + + +/********************************** + * Public Functions + **********************************/ +void NotifyAsync(uv_work_t* req) { + WaitForSingleObject(deviceChangedRegisteredEvent, INFINITE); +} + + +void NotifyFinished(uv_work_t* req) { + if (isRunning) { + if(isAdded) { + NotifyAdded(currentDevice); + } + else { + NotifyRemoved(currentDevice); + } + } + + // Delete Item in case of removal + if(isAdded == false) { + delete currentDevice; + } + + SetEvent(deviceChangedSentEvent); + + currentDevice = NULL; + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); +} + +void LoadFunctions() { + + bool success; + + hinstLib = LoadLibrary(LIBRARY_NAME); + + if (hinstLib != NULL) { + DllSetupDiEnumDeviceInfo = (_SetupDiEnumDeviceInfo) GetProcAddress(hinstLib, "SetupDiEnumDeviceInfo"); + + DllSetupDiGetClassDevs = (_SetupDiGetClassDevs) GetProcAddress(hinstLib, "SetupDiGetClassDevsA"); + + DllSetupDiDestroyDeviceInfoList = (_SetupDiDestroyDeviceInfoList) GetProcAddress(hinstLib, "SetupDiDestroyDeviceInfoList"); + + DllSetupDiGetDeviceInstanceId = (_SetupDiGetDeviceInstanceId) GetProcAddress(hinstLib, "SetupDiGetDeviceInstanceIdA"); + + DllSetupDiGetDeviceRegistryProperty = (_SetupDiGetDeviceRegistryProperty) GetProcAddress(hinstLib, "SetupDiGetDeviceRegistryPropertyA"); + + success = ( + DllSetupDiEnumDeviceInfo != NULL && + DllSetupDiGetClassDevs != NULL && + DllSetupDiDestroyDeviceInfoList != NULL && + DllSetupDiGetDeviceInstanceId != NULL && + DllSetupDiGetDeviceRegistryProperty != NULL + ); + } + else { + success = false; + } + + if(!success) { + printf("Could not load library functions from dll -> abort (Check if %s is available)\r\n", LIBRARY_NAME); + exit(1); + } +} + +void Start() { + isRunning = true; +} + +void Stop() { + isRunning = false; + SetEvent(deviceChangedRegisteredEvent); +} + +void InitDetection() { + + LoadFunctions(); + + deviceChangedRegisteredEvent = CreateEvent(NULL, false /* auto-reset event */, false /* non-signalled state */, ""); + deviceChangedSentEvent = CreateEvent(NULL, false /* auto-reset event */, true /* non-signalled state */, ""); + + BuildInitialDeviceList(); + + threadHandle = CreateThread( + NULL, // default security attributes + 0, // use default stack size + ListenerThread, // thread function name + NULL, // argument to thread function + 0, // use default creation flags + &threadId + ); + + uv_work_t* req = new uv_work_t(); + uv_queue_work(uv_default_loop(), req, NotifyAsync, (uv_after_work_cb)NotifyFinished); + + Start(); +} + + +void EIO_Find(uv_work_t* req) { + + ListBaton* data = static_cast(req->data); + + CreateFilteredList(&data->results, data->vid, data->pid); +} + + +/********************************** + * Local Functions + **********************************/ +void ToUpper(char * buf) { + char* c = buf; + while (*c != '\0') { + *c = toupper((unsigned char)*c); + c++; + } +} + + +void extractVidPid(char * buf, ListResultItem_t * item) { + if(buf == NULL) { + return; + } + + ToUpper(buf); + + char* string; + char* temp; + char* pidStr, *vidStr; + int vid = 0; + int pid = 0; + + string = new char[strlen(buf) + 1]; + memcpy(string, buf, strlen(buf) + 1); + + vidStr = strstr(string, VID_TAG); + pidStr = strstr(string, PID_TAG); + + if(vidStr != NULL) { + temp = (char*) (vidStr + strlen(VID_TAG)); + temp[4] = '\0'; + vid = strtol (temp, NULL, 16); + } + + if(pidStr != NULL) { + temp = (char*) (pidStr + strlen(PID_TAG)); + temp[4] = '\0'; + pid = strtol (temp, NULL, 16); + } + item->vendorId = vid; + item->productId = pid; + + delete string; +} + + +LRESULT CALLBACK DetectCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + if (msg == WM_DEVICECHANGE) { + if ( DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam ) { + PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam; + PDEV_BROADCAST_DEVICEINTERFACE pDevInf; + + if(pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; + UpdateDevice(pDevInf, wParam, (DBT_DEVICEARRIVAL == wParam) ? DeviceState_Connect : DeviceState_Disconnect); + } + } + } + + return 1; +} + + +DWORD WINAPI ListenerThread( LPVOID lpParam ) { + char className[MAX_THREAD_WINDOW_NAME]; + _snprintf_s(className, MAX_THREAD_WINDOW_NAME, "ListnerThreadUsbDetection_%d", GetCurrentThreadId()); + + WNDCLASSA wincl = {0}; + wincl.hInstance = GetModuleHandle(0); + wincl.lpszClassName = className; + wincl.lpfnWndProc = DetectCallback; + + if (!RegisterClassA(&wincl)) { + DWORD le = GetLastError(); + printf("RegisterClassA() failed [Error: %x]\r\n", le); + return 1; + } + + + HWND hwnd = CreateWindowExA(WS_EX_TOPMOST, className, className, 0, 0, 0, 0, 0, NULL, 0, 0, 0); + if (!hwnd) { + DWORD le = GetLastError(); + printf("CreateWindowExA() failed [Error: %x]\r\n", le); + return 1; + } + + DEV_BROADCAST_DEVICEINTERFACE_A notifyFilter = {0}; + notifyFilter.dbcc_size = sizeof(notifyFilter); + notifyFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + notifyFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE; + + HDEVNOTIFY hDevNotify = RegisterDeviceNotificationA(hwnd, ¬ifyFilter, DEVICE_NOTIFY_WINDOW_HANDLE); + if (!hDevNotify) { + DWORD le = GetLastError(); + printf("RegisterDeviceNotificationA() failed [Error: %x]\r\n", le); + return 1; + } + + MSG msg; + while(TRUE) { + BOOL bRet = GetMessage(&msg, hwnd, 0, 0); + if ((bRet == 0) || (bRet == -1)) { + break; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +} + + +void BuildInitialDeviceList() { + TCHAR buf[MAX_PATH]; + DWORD dwFlag = (DIGCF_ALLCLASSES | DIGCF_PRESENT); + HDEVINFO hDevInfo = DllSetupDiGetClassDevs(NULL, "USB", NULL, dwFlag); + + if(INVALID_HANDLE_VALUE == hDevInfo) { + return; + } + + SP_DEVINFO_DATA* pspDevInfoData = (SP_DEVINFO_DATA*) HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA)); + pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA); + for(int i=0; DllSetupDiEnumDeviceInfo(hDevInfo, i, pspDevInfoData); i++) { + DWORD nSize=0 ; + + if (!DllSetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize)) { + break; + } + + DeviceItem_t* item = new DeviceItem_t(); + item->deviceState = DeviceState_Connect; + + DWORD DataT; + DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, MAX_PATH, &nSize); + + AddItemToList(buf, item); + ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, &item->deviceParams); + } + + if(pspDevInfoData) { + HeapFree(GetProcessHeap(), 0, pspDevInfoData); + } + + if(hDevInfo) { + DllSetupDiDestroyDeviceInfoList(hDevInfo); + } +} + + +void ExtractDeviceInfo(HDEVINFO hDevInfo, SP_DEVINFO_DATA* pspDevInfoData, TCHAR* buf, DWORD buffSize, ListResultItem_t* resultItem) { + + DWORD DataT; + DWORD nSize; + static int dummy = 1; + + resultItem->locationId = 0; + resultItem->deviceAddress = dummy++; + + // device found + if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_FRIENDLYNAME, &DataT, (PBYTE)buf, buffSize, &nSize)) { + resultItem->deviceName = buf; + } + else if ( DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_DEVICEDESC, &DataT, (PBYTE)buf, buffSize, &nSize)) + { + resultItem->deviceName = buf; + } + if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_MFG, &DataT, (PBYTE)buf, buffSize, &nSize)) { + resultItem->manufacturer = buf; + } + if (DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, buffSize, &nSize)) { + // Use this to extract VID / PID + extractVidPid(buf, resultItem); + } +} + + +void UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam, DeviceState_t state) { + // dbcc_name: + // \\?\USB#Vid_04e8&Pid_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed} + // convert to + // USB\Vid_04e8&Pid_503b\0002F9A9828E0F06 + CString szDevId = pDevInf->dbcc_name+4; + int idx = szDevId.ReverseFind(_T('#')); + + szDevId.Truncate(idx); + szDevId.Replace(_T('#'), _T('\\')); + szDevId.MakeUpper(); + + CString szClass; + idx = szDevId.Find(_T('\\')); + szClass = szDevId.Left(idx); + + // if we are adding device, we only need present devices + // otherwise, we need all devices + DWORD dwFlag = DBT_DEVICEARRIVAL != wParam ? DIGCF_ALLCLASSES : (DIGCF_ALLCLASSES | DIGCF_PRESENT); + HDEVINFO hDevInfo = DllSetupDiGetClassDevs(NULL, szClass, NULL, dwFlag); + if(INVALID_HANDLE_VALUE == hDevInfo) { + return; + } + + SP_DEVINFO_DATA* pspDevInfoData = (SP_DEVINFO_DATA*) HeapAlloc(GetProcessHeap(), 0, sizeof(SP_DEVINFO_DATA)); + pspDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA); + for(int i=0; DllSetupDiEnumDeviceInfo(hDevInfo, i, pspDevInfoData); i++) { + DWORD nSize=0 ; + TCHAR buf[MAX_PATH]; + + if (!DllSetupDiGetDeviceInstanceId(hDevInfo, pspDevInfoData, buf, sizeof(buf), &nSize)) { + break; + } + + if(szDevId == buf) { + + WaitForSingleObject(deviceChangedSentEvent, INFINITE); + + DWORD DataT; + DWORD nSize; + DllSetupDiGetDeviceRegistryProperty(hDevInfo, pspDevInfoData, SPDRP_HARDWAREID, &DataT, (PBYTE)buf, MAX_PATH, &nSize); + + if(state == DeviceState_Connect) { + DeviceItem_t* device = new DeviceItem_t(); + + AddItemToList(buf, device); + ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, &device->deviceParams); + + currentDevice = &device->deviceParams; + isAdded = true; + } + else { + + ListResultItem_t* item = NULL; + if(IsItemAlreadyStored(buf)) { + DeviceItem_t* deviceItem = GetItemFromList(buf); + if(deviceItem) + { + item = CopyElement(&deviceItem->deviceParams); + } + RemoveItemFromList(deviceItem); + delete deviceItem; + } + + if(item == NULL) { + item = new ListResultItem_t(); + ExtractDeviceInfo(hDevInfo, pspDevInfoData, buf, MAX_PATH, item); + } + currentDevice = item; + isAdded = false; + } + + break; + } + } + + if (pspDevInfoData) { + HeapFree(GetProcessHeap(), 0, pspDevInfoData); + } + + if(hDevInfo) { + DllSetupDiDestroyDeviceInfoList(hDevInfo); + } + + SetEvent(deviceChangedRegisteredEvent); +} diff --git a/vendor/node-usb-native/src/deviceList.cpp b/vendor/node-usb-native/src/deviceList.cpp new file mode 100644 index 000000000..1cdcbdb1e --- /dev/null +++ b/vendor/node-usb-native/src/deviceList.cpp @@ -0,0 +1,75 @@ +#include +#include +#include + +#include "deviceList.h" + + +using namespace std; + +map deviceMap; + +void AddItemToList(char* key, DeviceItem_t * item) { + item->SetKey(key); + deviceMap.insert(pair(item->GetKey(), item)); +} + +void RemoveItemFromList(DeviceItem_t* item) { + deviceMap.erase(item->GetKey()); +} + +DeviceItem_t* GetItemFromList(char* key) { + map::iterator it; + + it = deviceMap.find(key); + if(it == deviceMap.end()) { + return NULL; + } + else { + return it->second; + } +} + +bool IsItemAlreadyStored(char* key) { + map::iterator it; + + it = deviceMap.find(key); + if(it == deviceMap.end()) { + return false; + } + else { + return true; + } + + return true; +} + +ListResultItem_t* CopyElement(ListResultItem_t* item) { + ListResultItem_t* dst = new ListResultItem_t(); + dst->locationId = item->locationId; + dst->vendorId = item->vendorId; + dst->productId = item->productId; + dst->deviceName = item->deviceName; + dst->manufacturer = item->manufacturer; + dst->serialNumber = item->serialNumber; + dst->deviceAddress = item->deviceAddress; + + return dst; +} + +void CreateFilteredList(list *filteredList, int vid, int pid) { + map::iterator it; + + for (it = deviceMap.begin(); it != deviceMap.end(); ++it) { + DeviceItem_t* item = it->second; + + if ( + (( vid != 0 && pid != 0) && (vid == item->deviceParams.vendorId && pid == item->deviceParams.productId)) + || ((vid != 0 && pid == 0) && vid == item->deviceParams.vendorId) + || (vid == 0 && pid == 0) + ) { + (*filteredList).push_back(CopyElement(&item->deviceParams)); + } + + } +} diff --git a/vendor/node-usb-native/src/deviceList.h b/vendor/node-usb-native/src/deviceList.h new file mode 100644 index 000000000..23d121bcf --- /dev/null +++ b/vendor/node-usb-native/src/deviceList.h @@ -0,0 +1,63 @@ +#ifndef _DEVICE_LIST_H +#define _DEVICE_LIST_H + +#include +#include + +typedef struct { + public: + int locationId; + int vendorId; + int productId; + std::string deviceName; + std::string manufacturer; + std::string serialNumber; + int deviceAddress; +} ListResultItem_t; + +typedef enum _DeviceState_t { + DeviceState_Connect, + DeviceState_Disconnect, +} DeviceState_t; + +typedef struct _DeviceItem_t { + ListResultItem_t deviceParams; + DeviceState_t deviceState; + + private: + char* key; + + + public: + _DeviceItem_t() { + key = NULL; + } + + ~_DeviceItem_t() { + if(this->key != NULL) { + delete this->key; + } + } + + void SetKey(char* key) { + if(this->key != NULL) { + delete this->key; + } + this->key = new char[strlen(key) + 1]; + memcpy(this->key, key, strlen(key) + 1); + } + + char* GetKey() { + return this->key; + } +} DeviceItem_t; + + +void AddItemToList(char* key, DeviceItem_t * item); +void RemoveItemFromList(DeviceItem_t* item); +bool IsItemAlreadyStored(char* identifier); +DeviceItem_t* GetItemFromList(char* key); +ListResultItem_t* CopyElement(ListResultItem_t* item); +void CreateFilteredList(std::list* filteredList, int vid, int pid); + +#endif diff --git a/vendor/node-usb-native/src/serialport.cpp b/vendor/node-usb-native/src/serialport.cpp new file mode 100644 index 000000000..755037b38 --- /dev/null +++ b/vendor/node-usb-native/src/serialport.cpp @@ -0,0 +1,717 @@ +#include "./serialport.h" + +#ifdef WIN32 +#define strncasecmp strnicmp +#else +#include "./serialport_poller.h" +#endif + +struct _WriteQueue { + const int _fd; // the fd that is associated with this write queue + QueuedWrite _write_queue; + uv_mutex_t _write_queue_mutex; + _WriteQueue *_next; + + _WriteQueue(const int fd) : _fd(fd), _write_queue(), _next(NULL) { + uv_mutex_init(&_write_queue_mutex); + } + + void lock() { uv_mutex_lock(&_write_queue_mutex); } + void unlock() { uv_mutex_unlock(&_write_queue_mutex); } + + QueuedWrite &get() { return _write_queue; } +}; + +static _WriteQueue *write_queues = NULL; + +static _WriteQueue *qForFD(const int fd) { + _WriteQueue *q = write_queues; + while (q != NULL) { + if (q->_fd == fd) { + return q; + } + q = q->_next; + } + return NULL; +} + +static _WriteQueue *newQForFD(const int fd) { + _WriteQueue *q = qForFD(fd); + + if (q == NULL) { + if (write_queues == NULL) { + write_queues = new _WriteQueue(fd); + return write_queues; + } else { + q = write_queues; + while (q->_next != NULL) { + q = q->_next; + } + q->_next = new _WriteQueue(fd); + return q->_next; + } + } + + return q; +} + +static void deleteQForFD(const int fd) { + if (write_queues == NULL) + return; + + _WriteQueue *q = write_queues; + if (write_queues->_fd == fd) { + write_queues = write_queues->_next; + delete q; + + return; + } + + while (q->_next != NULL) { + if (q->_next->_fd == fd) { + _WriteQueue *out_q = q->_next; + q->_next = q->_next->_next; + delete out_q; + + return; + } + q = q->_next; + } + + // It wasn't found... +} + +v8::Local getValueFromObject(v8::Local options, std::string key) { + v8::Local v8str = Nan::New(key).ToLocalChecked(); + return Nan::Get(options, v8str).ToLocalChecked(); +} + +int getIntFromObject(v8::Local options, std::string key) { + #if NODE_MAJOR_VERSION >= 10 + return getValueFromObject(options, key)->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + return getValueFromObject(options, key)->ToInt32()->Int32Value(); + #endif +} + +bool getBoolFromObject(v8::Local options, std::string key) { + return getValueFromObject(options, key)->ToBoolean()->BooleanValue(); +} + +v8::Local getStringFromObj(v8::Local options, std::string key) { + return getValueFromObject(options, key)->ToString(); +} + +double getDoubleFromObject(v8::Local options, std::string key) { + #if NODE_MAJOR_VERSION >= 10 + return getValueFromObject(options, key)->ToNumber(v8::Isolate::GetCurrent())->NumberValue(); + #else + return getValueFromObject(options, key)->ToNumber()->NumberValue(); + #endif +} + +NAN_METHOD(Open) { + // path + if (!info[0]->IsString()) { + Nan::ThrowTypeError("First argument must be a string"); + return; + } + v8::String::Utf8Value path(info[0]->ToString()); + + // options + if (!info[1]->IsObject()) { + Nan::ThrowTypeError("Second argument must be an object"); + return; + } + v8::Local options = info[1]->ToObject(); + + // callback + if (!info[2]->IsFunction()) { + Nan::ThrowTypeError("Third argument must be a function"); + return; + } + + OpenBaton* baton = new OpenBaton(); + memset(baton, 0, sizeof(OpenBaton)); + strcpy(baton->path, *path); + baton->baudRate = getIntFromObject(options, "baudRate"); + baton->dataBits = getIntFromObject(options, "dataBits"); + baton->bufferSize = getIntFromObject(options, "bufferSize"); + baton->parity = ToParityEnum(getStringFromObj(options, "parity")); + baton->stopBits = ToStopBitEnum(getDoubleFromObject(options, "stopBits")); + baton->rtscts = getBoolFromObject(options, "rtscts"); + baton->xon = getBoolFromObject(options, "xon"); + baton->xoff = getBoolFromObject(options, "xoff"); + baton->xany = getBoolFromObject(options, "xany"); + baton->hupcl = getBoolFromObject(options, "hupcl"); + baton->lock = getBoolFromObject(options, "lock"); + + v8::Local platformOptions = getValueFromObject(options, "platformOptions")->ToObject(); + baton->platformOptions = ParsePlatformOptions(platformOptions); + + baton->callback.Reset(info[2].As()); + baton->dataCallback = new Nan::Callback(getValueFromObject(options, "dataCallback").As()); + baton->disconnectedCallback = new Nan::Callback(getValueFromObject(options, "disconnectedCallback").As()); + baton->errorCallback = new Nan::Callback(getValueFromObject(options, "errorCallback").As()); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + + uv_queue_work(uv_default_loop(), req, EIO_Open, (uv_after_work_cb)EIO_AfterOpen); + + return; +} + +void EIO_AfterOpen(uv_work_t* req) { + Nan::HandleScope scope; + + OpenBaton* data = static_cast(req->data); + + v8::Local argv[2]; + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + argv[1] = Nan::Undefined(); + // not needed because we're not calling AfterOpenSuccess + delete data->dataCallback; + delete data->errorCallback; + delete data->disconnectedCallback; + } else { + argv[0] = Nan::Null(); + argv[1] = Nan::New(data->result); + + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = argv[1]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + fd = argv[1]->ToInt32()->Int32Value(); + #endif + newQForFD(fd); + + AfterOpenSuccess(data->result, data->dataCallback, data->disconnectedCallback, data->errorCallback); + } + + data->callback.Call(2, argv); + + delete data->platformOptions; + delete data; + delete req; +} + +NAN_METHOD(Update) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + fd = info[0]->ToInt32()->Int32Value(); + #endif + + // options + if (!info[1]->IsObject()) { + Nan::ThrowTypeError("Second argument must be an object"); + return; + } + v8::Local options = info[1]->ToObject(); + + if (!Nan::Has(options, Nan::New("baudRate").ToLocalChecked()).FromMaybe(false)) { + Nan::ThrowTypeError("baudRate must be set on options object"); + return; + } + + // callback + if (!info[2]->IsFunction()) { + Nan::ThrowTypeError("Third argument must be a function"); + return; + } + + ConnectionOptionsBaton* baton = new ConnectionOptionsBaton(); + memset(baton, 0, sizeof(ConnectionOptionsBaton)); + + baton->fd = fd; + #if NODE_MAJOR_VERSION >= 10 + baton->baudRate = Nan::Get(options, Nan::New("baudRate").ToLocalChecked()).ToLocalChecked()->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + baton->baudRate = Nan::Get(options, Nan::New("baudRate").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value(); + #endif + baton->callback.Reset(info[2].As()); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + + uv_queue_work(uv_default_loop(), req, EIO_Update, (uv_after_work_cb)EIO_AfterUpdate); + + return; +} + +void EIO_AfterUpdate(uv_work_t* req) { + Nan::HandleScope scope; + + ConnectionOptionsBaton* data = static_cast(req->data); + + v8::Local argv[1]; + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Null(); + } + + data->callback.Call(1, argv); + + delete data; + delete req; +} + +NAN_METHOD(Write) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + fd = info[0]->ToInt32()->Int32Value(); + #endif + + // buffer + if (!info[1]->IsObject() || !node::Buffer::HasInstance(info[1])) { + Nan::ThrowTypeError("Second argument must be a buffer"); + return; + } + v8::Local buffer = info[1]->ToObject(); + char* bufferData = node::Buffer::Data(buffer); + size_t bufferLength = node::Buffer::Length(buffer); + + // callback + if (!info[2]->IsFunction()) { + Nan::ThrowTypeError("Third argument must be a function"); + return; + } + + WriteBaton* baton = new WriteBaton(); + memset(baton, 0, sizeof(WriteBaton)); + baton->fd = fd; + baton->buffer.Reset(buffer); + baton->bufferData = bufferData; + baton->bufferLength = bufferLength; + baton->offset = 0; + baton->callback.Reset(info[2].As()); + + QueuedWrite* queuedWrite = new QueuedWrite(); + memset(queuedWrite, 0, sizeof(QueuedWrite)); + queuedWrite->baton = baton; + queuedWrite->req.data = queuedWrite; + + _WriteQueue *q = qForFD(fd); + if (!q) { + Nan::ThrowTypeError("There's no write queue for that file descriptor (write)!"); + return; + } + + q->lock(); + QueuedWrite &write_queue = q->get(); + bool empty = write_queue.empty(); + + write_queue.insert_tail(queuedWrite); + + if (empty) { + uv_queue_work(uv_default_loop(), &queuedWrite->req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); + } + q->unlock(); + + return; +} + +void EIO_AfterWrite(uv_work_t* req) { + Nan::HandleScope scope; + + QueuedWrite* queuedWrite = static_cast(req->data); + WriteBaton* data = static_cast(queuedWrite->baton); + + v8::Local argv[1]; + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Null(); + } + + if (data->offset < data->bufferLength && !data->errorString[0]) { + // We're not done with this baton, so throw it right back onto the queue. + // Don't re-push the write in the event loop if there was an error; because same error could occur again! + // TODO: Add a uv_poll here for unix... + // fprintf(stderr, "Write again...\n"); + uv_queue_work(uv_default_loop(), req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); + return; + } + + // throwing errors instead of returning them at this point is rude + int fd = data->fd; + _WriteQueue *q = qForFD(fd); + if (!q) { + Nan::ThrowTypeError("There's no write queue for that file descriptor (after write)!"); + return; + } + + q->lock(); + QueuedWrite &write_queue = q->get(); + + // remove this one from the list + queuedWrite->remove(); + + data->callback.Call(1, argv); + + // If there are any left, start a new thread to write the next one. + if (!write_queue.empty()) { + // Always pull the next work item from the head of the queue + QueuedWrite* nextQueuedWrite = write_queue.next; + uv_queue_work(uv_default_loop(), &nextQueuedWrite->req, EIO_Write, (uv_after_work_cb)EIO_AfterWrite); + } + q->unlock(); + + data->buffer.Reset(); + delete data; + delete queuedWrite; +} + +NAN_METHOD(Close) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + + // callback + if (!info[1]->IsFunction()) { + Nan::ThrowTypeError("Second argument must be a function"); + return; + } + + CloseBaton* baton = new CloseBaton(); + memset(baton, 0, sizeof(CloseBaton)); + #if NODE_MAJOR_VERSION >= 10 + baton->fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + baton->fd = info[0]->ToInt32()->Int32Value(); + #endif + + baton->callback.Reset(info[1].As()); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_Close, (uv_after_work_cb)EIO_AfterClose); + + return; +} + +void EIO_AfterClose(uv_work_t* req) { + Nan::HandleScope scope; + CloseBaton* data = static_cast(req->data); + + v8::Local argv[1]; + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Null(); + + // We don't have an error, so clean up the write queue for that fd + _WriteQueue *q = qForFD(data->fd); + if (q) { + q->lock(); + QueuedWrite &write_queue = q->get(); + while (!write_queue.empty()) { + QueuedWrite *del_q = write_queue.next; + del_q->baton->buffer.Reset(); + del_q->remove(); + } + q->unlock(); + deleteQForFD(data->fd); + } + } + data->callback.Call(1, argv); + + delete data; + delete req; +} + +NAN_METHOD(List) { + // callback + if (!info[0]->IsFunction()) { + Nan::ThrowTypeError("First argument must be a function"); + return; + } + + ListBaton* baton = new ListBaton(); + strcpy(baton->errorString, ""); + baton->callback.Reset(info[0].As()); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_List, (uv_after_work_cb)EIO_AfterList); + + return; +} + +void setIfNotEmpty(v8::Local item, std::string key, const char *value) { + v8::Local v8key = Nan::New(key).ToLocalChecked(); + if (strlen(value) > 0) { + Nan::Set(item, v8key, Nan::New(value).ToLocalChecked()); + } else { + Nan::Set(item, v8key, Nan::Undefined()); + } + +} + +void EIO_AfterList(uv_work_t* req) { + Nan::HandleScope scope; + + ListBaton* data = static_cast(req->data); + + v8::Local argv[2]; + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + argv[1] = Nan::Undefined(); + } else { + v8::Local results = Nan::New(); + int i = 0; + for (std::list::iterator it = data->results.begin(); it != data->results.end(); ++it, i++) { + v8::Local item = Nan::New(); + + setIfNotEmpty(item, "comName", (*it)->comName.c_str()); + setIfNotEmpty(item, "manufacturer", (*it)->manufacturer.c_str()); + setIfNotEmpty(item, "serialNumber", (*it)->serialNumber.c_str()); + setIfNotEmpty(item, "pnpId", (*it)->pnpId.c_str()); + setIfNotEmpty(item, "locationId", (*it)->locationId.c_str()); + setIfNotEmpty(item, "vendorId", (*it)->vendorId.c_str()); + setIfNotEmpty(item, "productId", (*it)->productId.c_str()); + + Nan::Set(results, i, item); + } + argv[0] = Nan::Null(); + argv[1] = results; + } + data->callback.Call(2, argv); + + for (std::list::iterator it = data->results.begin(); it != data->results.end(); ++it) { + delete *it; + } + delete data; + delete req; +} + +NAN_METHOD(Flush) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + fd = info[0]->ToInt32()->Int32Value(); + #endif + + // callback + if (!info[1]->IsFunction()) { + Nan::ThrowTypeError("Second argument must be a function"); + return; + } + v8::Local callback = info[1].As(); + + FlushBaton* baton = new FlushBaton(); + memset(baton, 0, sizeof(FlushBaton)); + baton->fd = fd; + baton->callback.Reset(callback); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_Flush, (uv_after_work_cb)EIO_AfterFlush); + + return; +} + +void EIO_AfterFlush(uv_work_t* req) { + Nan::HandleScope scope; + + FlushBaton* data = static_cast(req->data); + + v8::Local argv[2]; + + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + argv[1] = Nan::Undefined(); + } else { + argv[0] = Nan::Undefined(); + argv[1] = Nan::New(data->result); + } + + data->callback.Call(2, argv); + + delete data; + delete req; +} + +NAN_METHOD(Set) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + fd = info[0]->ToInt32()->Int32Value(); + #endif + + // options + if (!info[1]->IsObject()) { + Nan::ThrowTypeError("Second argument must be an object"); + return; + } + v8::Local options = info[1]->ToObject(); + + // callback + if (!info[2]->IsFunction()) { + Nan::ThrowTypeError("Third argument must be a function"); + return; + } + v8::Local callback = info[2].As(); + + SetBaton* baton = new SetBaton(); + memset(baton, 0, sizeof(SetBaton)); + baton->fd = fd; + baton->callback.Reset(callback); + baton->brk = getBoolFromObject(options, "brk"); + baton->rts = getBoolFromObject(options, "rts"); + baton->cts = getBoolFromObject(options, "cts"); + baton->dtr = getBoolFromObject(options, "dtr"); + baton->dsr = getBoolFromObject(options, "dsr"); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_Set, (uv_after_work_cb)EIO_AfterSet); + + return; +} + +void EIO_AfterSet(uv_work_t* req) { + Nan::HandleScope scope; + + SetBaton* data = static_cast(req->data); + + v8::Local argv[1]; + + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Null(); + } + data->callback.Call(1, argv); + + delete data; + delete req; +} + +NAN_METHOD(Drain) { + // file descriptor + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an int"); + return; + } + int fd; + #if NODE_MAJOR_VERSION >= 10 + fd = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + fd = info[0]->ToInt32()->Int32Value(); + #endif + + // callback + if (!info[1]->IsFunction()) { + Nan::ThrowTypeError("Second argument must be a function"); + return; + } + + DrainBaton* baton = new DrainBaton(); + memset(baton, 0, sizeof(DrainBaton)); + baton->fd = fd; + baton->callback.Reset(info[1].As()); + + uv_work_t* req = new uv_work_t(); + req->data = baton; + uv_queue_work(uv_default_loop(), req, EIO_Drain, (uv_after_work_cb)EIO_AfterDrain); + + return; +} + +void EIO_AfterDrain(uv_work_t* req) { + Nan::HandleScope scope; + + DrainBaton* data = static_cast(req->data); + + v8::Local argv[1]; + + if (data->errorString[0]) { + argv[0] = v8::Exception::Error(Nan::New(data->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Null(); + } + data->callback.Call(1, argv); + + delete data; + delete req; +} + +SerialPortParity NAN_INLINE(ToParityEnum(const v8::Local& v8str)) { + Nan::HandleScope scope; + Nan::Utf8String str(v8str); + size_t count = strlen(*str); + SerialPortParity parity = SERIALPORT_PARITY_NONE; + if (!strncasecmp(*str, "none", count)) { + parity = SERIALPORT_PARITY_NONE; + } else if (!strncasecmp(*str, "even", count)) { + parity = SERIALPORT_PARITY_EVEN; + } else if (!strncasecmp(*str, "mark", count)) { + parity = SERIALPORT_PARITY_MARK; + } else if (!strncasecmp(*str, "odd", count)) { + parity = SERIALPORT_PARITY_ODD; + } else if (!strncasecmp(*str, "space", count)) { + parity = SERIALPORT_PARITY_SPACE; + } + return parity; +} + +SerialPortStopBits NAN_INLINE(ToStopBitEnum(double stopBits)) { + if (stopBits > 1.4 && stopBits < 1.6) { + return SERIALPORT_STOPBITS_ONE_FIVE; + } + if (stopBits == 2) { + return SERIALPORT_STOPBITS_TWO; + } + return SERIALPORT_STOPBITS_ONE; +} + +extern "C" { + void init_serialport(v8::Handle target) { + Nan::HandleScope scope; + Nan::SetMethod(target, "set", Set); + Nan::SetMethod(target, "open", Open); + Nan::SetMethod(target, "update", Update); + Nan::SetMethod(target, "write", Write); + Nan::SetMethod(target, "close", Close); + Nan::SetMethod(target, "list", List); + Nan::SetMethod(target, "flush", Flush); + Nan::SetMethod(target, "drain", Drain); + +#ifndef WIN32 + SerialportPoller::Init(target); +#endif + } +} + +//NODE_MODULE(serialport, init); diff --git a/vendor/node-usb-native/src/serialport.h b/vendor/node-usb-native/src/serialport.h new file mode 100644 index 000000000..392fc2017 --- /dev/null +++ b/vendor/node-usb-native/src/serialport.h @@ -0,0 +1,195 @@ +#ifndef SRC_SERIALPORT_H_ +#define SRC_SERIALPORT_H_ + +#include +#include +#include +#include +#include +#include + +#define ERROR_STRING_SIZE 1024 + +NAN_METHOD(List); +void EIO_List(uv_work_t* req); +void EIO_AfterList(uv_work_t* req); + +NAN_METHOD(Open); +void EIO_Open(uv_work_t* req); +void EIO_AfterOpen(uv_work_t* req); +void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback); + +NAN_METHOD(Update); +void EIO_Update(uv_work_t* req); +void EIO_AfterUpdate(uv_work_t* req); + +NAN_METHOD(Write); +void EIO_Write(uv_work_t* req); +void EIO_AfterWrite(uv_work_t* req); + +NAN_METHOD(Close); +void EIO_Close(uv_work_t* req); +void EIO_AfterClose(uv_work_t* req); + +NAN_METHOD(Flush); +void EIO_Flush(uv_work_t* req); +void EIO_AfterFlush(uv_work_t* req); + +NAN_METHOD(Set); +void EIO_Set(uv_work_t* req); +void EIO_AfterSet(uv_work_t* req); + +NAN_METHOD(Drain); +void EIO_Drain(uv_work_t* req); +void EIO_AfterDrain(uv_work_t* req); + +enum SerialPortParity { + SERIALPORT_PARITY_NONE = 1, + SERIALPORT_PARITY_MARK = 2, + SERIALPORT_PARITY_EVEN = 3, + SERIALPORT_PARITY_ODD = 4, + SERIALPORT_PARITY_SPACE = 5 +}; + +enum SerialPortStopBits { + SERIALPORT_STOPBITS_ONE = 1, + SERIALPORT_STOPBITS_ONE_FIVE = 2, + SERIALPORT_STOPBITS_TWO = 3 +}; + +SerialPortParity ToParityEnum(const v8::Local& str); +SerialPortStopBits ToStopBitEnum(double stopBits); + +struct OpenBatonPlatformOptions { }; +OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local& options); + +struct OpenBaton { + char errorString[ERROR_STRING_SIZE]; + Nan::Callback callback; + char path[1024]; + int fd; + int result; + int baudRate; + int dataBits; + int bufferSize; + bool rtscts; + bool xon; + bool xoff; + bool xany; + bool dsrdtr; + bool hupcl; + bool lock; + Nan::Callback* dataCallback; + Nan::Callback* disconnectedCallback; + Nan::Callback* errorCallback; + SerialPortParity parity; + SerialPortStopBits stopBits; + OpenBatonPlatformOptions* platformOptions; +}; + +struct ConnectionOptionsBaton { + char errorString[ERROR_STRING_SIZE]; + Nan::Callback callback; + int fd; + int baudRate; +}; + +struct WriteBaton { + int fd; + char* bufferData; + size_t bufferLength; + size_t offset; + Nan::Persistent buffer; + Nan::Callback callback; + int result; + char errorString[ERROR_STRING_SIZE]; +}; + +struct QueuedWrite { + uv_work_t req; + QueuedWrite *prev; + QueuedWrite *next; + WriteBaton* baton; + + QueuedWrite() { + prev = this; + next = this; + + baton = 0; + } + + ~QueuedWrite() { + remove(); + } + + void remove() { + prev->next = next; + next->prev = prev; + + next = this; + prev = this; + } + + void insert_tail(QueuedWrite *qw) { + qw->next = this; + qw->prev = this->prev; + qw->prev->next = qw; + this->prev = qw; + } + + bool empty() { + return next == this; + } +}; + +struct CloseBaton { + int fd; + Nan::Callback callback; + char errorString[ERROR_STRING_SIZE]; +}; + +struct ListResultItem { + std::string comName; + std::string manufacturer; + std::string serialNumber; + std::string pnpId; + std::string locationId; + std::string vendorId; + std::string productId; +}; + +struct ListBaton { + Nan::Callback callback; + std::list results; + char errorString[ERROR_STRING_SIZE]; +}; + +struct FlushBaton { + int fd; + Nan::Callback callback; + int result; + char errorString[ERROR_STRING_SIZE]; +}; + +struct SetBaton { + int fd; + Nan::Callback callback; + int result; + char errorString[ERROR_STRING_SIZE]; + bool rts; + bool cts; + bool dtr; + bool dsr; + bool brk; +}; + +struct DrainBaton { + int fd; + Nan::Callback callback; + int result; + char errorString[ERROR_STRING_SIZE]; +}; + +int setup(int fd, OpenBaton *data); +int setBaudRate(ConnectionOptionsBaton *data); +#endif // SRC_SERIALPORT_H_ diff --git a/vendor/node-usb-native/src/serialport_poller.cpp b/vendor/node-usb-native/src/serialport_poller.cpp new file mode 100644 index 000000000..7471f2a38 --- /dev/null +++ b/vendor/node-usb-native/src/serialport_poller.cpp @@ -0,0 +1,128 @@ +// Copyright (C) 2013 Robert Giseburt +// serialport_poller.cpp Written as a part of https://github.com/voodootikigod/node-serialport +// License to use this is the same as that of node-serialport. + +#include +#include "./serialport_poller.h" + +using namespace v8; + +static Nan::Persistent serialportpoller_constructor; + +SerialportPoller::SerialportPoller() : Nan::ObjectWrap() {} +SerialportPoller::~SerialportPoller() { + // printf("~SerialportPoller\n"); + delete callback_; +} + +void _serialportReadable(uv_poll_t *req, int status, int events) { + SerialportPoller* sp = (SerialportPoller*) req->data; + // We can stop polling until we have read all of the data... + sp->_stop(); + sp->callCallback(status); +} + +void SerialportPoller::callCallback(int status) { + Nan::HandleScope scope; + // uv_work_t* req = new uv_work_t; + + // Call the callback to go read more data... + + v8::Local argv[1]; + if (status != 0) { + // error handling changed in libuv, see: + // https://github.com/joyent/libuv/commit/3ee4d3f183331 + #ifdef UV_ERRNO_H_ + const char* err_string = uv_strerror(status); + #else + uv_err_t errno = uv_last_error(uv_default_loop()); + const char* err_string = uv_strerror(errno); + #endif + snprintf(this->errorString, sizeof(this->errorString), "Error %s on polling", err_string); + argv[0] = v8::Exception::Error(Nan::New(this->errorString).ToLocalChecked()); + } else { + argv[0] = Nan::Undefined(); + } + + callback_->Call(1, argv); +} + + + +void SerialportPoller::Init(Handle target) { + Nan::HandleScope scope; + + // Prepare constructor template + Local tpl = Nan::New(New); + tpl->SetClassName(Nan::New("SerialportPoller").ToLocalChecked()); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + + + // Prototype + + // SerialportPoller.close() + Nan::SetPrototypeMethod(tpl, "close", Close); + + // SerialportPoller.start() + Nan::SetPrototypeMethod(tpl, "start", Start); + + serialportpoller_constructor.Reset(tpl); + + Nan::Set(target, Nan::New("SerialportPoller").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); +} + +NAN_METHOD(SerialportPoller::New) { + if (!info[0]->IsInt32()) { + Nan::ThrowTypeError("First argument must be an fd"); + return; + } + + if (!info[1]->IsFunction()) { + Nan::ThrowTypeError("Third argument must be a function"); + return; + } + + SerialportPoller* obj = new SerialportPoller(); + #if NODE_MAJOR_VERSION >= 10 + obj->fd_ = info[0]->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + obj->fd_ = info[0]->ToInt32()->Int32Value(); + #endif + obj->callback_ = new Nan::Callback(info[1].As()); + // obj->callCallback(); + + obj->Wrap(info.This()); + + obj->poll_handle_.data = obj; + + uv_poll_init(uv_default_loop(), &obj->poll_handle_, obj->fd_); + + uv_poll_start(&obj->poll_handle_, UV_READABLE, _serialportReadable); + + info.GetReturnValue().Set(info.This()); +} + +void SerialportPoller::_start() { + uv_poll_start(&poll_handle_, UV_READABLE, _serialportReadable); +} + +void SerialportPoller::_stop() { + uv_poll_stop(&poll_handle_); +} + + +NAN_METHOD(SerialportPoller::Start) { + SerialportPoller* obj = Nan::ObjectWrap::Unwrap(info.This()); + obj->_start(); + + return; +} + +NAN_METHOD(SerialportPoller::Close) { + SerialportPoller* obj = Nan::ObjectWrap::Unwrap(info.This()); + obj->_stop(); + + // DO SOMETHING! + + return; +} diff --git a/vendor/node-usb-native/src/serialport_poller.h b/vendor/node-usb-native/src/serialport_poller.h new file mode 100644 index 000000000..4d20b07d6 --- /dev/null +++ b/vendor/node-usb-native/src/serialport_poller.h @@ -0,0 +1,35 @@ +// Copyright (C) 2013 Robert Giseburt +// serialport_poller.h Written as a part of https://github.com/voodootikigod/node-serialport +// License to use this is the same as that of node-serialport. + +#ifndef SERIALPORT_POLLER_H +#define SERIALPORT_POLLER_H + +#include +#include "./serialport.h" + +class SerialportPoller : public Nan::ObjectWrap { + public: + static void Init(v8::Handle target); + + void callCallback(int status); + + void _start(); + void _stop(); + + private: + SerialportPoller(); + ~SerialportPoller(); + + static NAN_METHOD(New); + static NAN_METHOD(Close); + static NAN_METHOD(Start); + + uv_poll_t poll_handle_; + int fd_; + char errorString[ERROR_STRING_SIZE]; + + Nan::Callback* callback_; +}; + +#endif diff --git a/vendor/node-usb-native/src/serialport_unix.cpp b/vendor/node-usb-native/src/serialport_unix.cpp new file mode 100644 index 000000000..cd27b40c6 --- /dev/null +++ b/vendor/node-usb-native/src/serialport_unix.cpp @@ -0,0 +1,740 @@ +#include "./serialport.h" +#include "./serialport_poller.h" +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#include +#include +#include +#include + +uv_mutex_t list_mutex; +Boolean lockInitialised = FALSE; +#endif + +#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) +#include +#include +#endif + +#if defined(__OpenBSD__) +#include +#endif + +#if defined(__linux__) +#include +#include +#endif + +struct UnixPlatformOptions : OpenBatonPlatformOptions { + uint8_t vmin; + uint8_t vtime; +}; + +OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local& options) { + Nan::HandleScope scope; + + UnixPlatformOptions* result = new UnixPlatformOptions(); + #if NODE_MAJOR_VERSION >= 10 + result->vmin = Nan::Get(options, Nan::New("vmin").ToLocalChecked()).ToLocalChecked()->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + result->vtime = Nan::Get(options, Nan::New("vtime").ToLocalChecked()).ToLocalChecked()->ToInt32(v8::Isolate::GetCurrent())->Int32Value(); + #else + result->vmin = Nan::Get(options, Nan::New("vmin").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value(); + result->vtime = Nan::Get(options, Nan::New("vtime").ToLocalChecked()).ToLocalChecked()->ToInt32()->Int32Value(); + #endif + + return result; +} + +int ToBaudConstant(int baudRate); +int ToDataBitsConstant(int dataBits); +int ToStopBitsConstant(SerialPortStopBits stopBits); + +void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback) { + delete dataCallback; + delete errorCallback; + delete disconnectedCallback; +} + +int ToBaudConstant(int baudRate) { + switch (baudRate) { + case 0: return B0; + case 50: return B50; + case 75: return B75; + case 110: return B110; + case 134: return B134; + case 150: return B150; + case 200: return B200; + case 300: return B300; + case 600: return B600; + case 1200: return B1200; + case 1800: return B1800; + case 2400: return B2400; + case 4800: return B4800; + case 9600: return B9600; + case 19200: return B19200; + case 38400: return B38400; + case 57600: return B57600; + case 115200: return B115200; + case 230400: return B230400; +#if defined(__linux__) + case 460800: return B460800; + case 500000: return B500000; + case 576000: return B576000; + case 921600: return B921600; + case 1000000: return B1000000; + case 1152000: return B1152000; + case 1500000: return B1500000; + case 2000000: return B2000000; + case 2500000: return B2500000; + case 3000000: return B3000000; + case 3500000: return B3500000; + case 4000000: return B4000000; +#endif + } + return -1; +} + +#ifdef __APPLE__ +typedef struct SerialDevice { + char port[MAXPATHLEN]; + char locationId[MAXPATHLEN]; + char vendorId[MAXPATHLEN]; + char productId[MAXPATHLEN]; + char manufacturer[MAXPATHLEN]; + char serialNumber[MAXPATHLEN]; +} stSerialDevice; + +typedef struct DeviceListItem { + struct SerialDevice value; + struct DeviceListItem *next; + int* length; +} stDeviceListItem; +#endif + +int ToDataBitsConstant(int dataBits) { + switch (dataBits) { + case 8: default: return CS8; + case 7: return CS7; + case 6: return CS6; + case 5: return CS5; + } + return -1; +} + +void EIO_Open(uv_work_t* req) { + OpenBaton* data = static_cast(req->data); + + int flags = (O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC | O_SYNC); + int fd = open(data->path, flags); + + if (-1 == fd) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot open %s", strerror(errno), data->path); + return; + } + + if (-1 == setup(fd, data)) { + close(fd); + return; + } + + data->result = fd; +} + +int setBaudRate(ConnectionOptionsBaton *data) { + // lookup the standard baudrates from the table + int baudRate = ToBaudConstant(data->baudRate); + int fd = data->fd; + + // get port options + struct termios options; + if (tcgetattr(fd, &options)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: tcgetattr encountering %s", strerror(errno)); + return -1; + } + + // If there is a custom baud rate on linux you can do the following trick with B38400 + #if defined(__linux__) && defined(ASYNC_SPD_CUST) + if (baudRate == -1) { + struct serial_struct serinfo; + serinfo.reserved_char[0] = 0; + if (-1 != ioctl(fd, TIOCGSERIAL, &serinfo)) { + serinfo.flags &= ~ASYNC_SPD_MASK; + serinfo.flags |= ASYNC_SPD_CUST; + serinfo.custom_divisor = (serinfo.baud_base + (data->baudRate / 2)) / data->baudRate; + if (serinfo.custom_divisor < 1) + serinfo.custom_divisor = 1; + + ioctl(fd, TIOCSSERIAL, &serinfo); + ioctl(fd, TIOCGSERIAL, &serinfo); + } else { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s setting custom baud rate of %d", strerror(errno), data->baudRate); + return -1; + } + + // Now we use "B38400" to trigger the special baud rate. + baudRate = B38400; + } + #endif + + // On OS X, starting with Tiger, we can set a custom baud rate with ioctl + #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) + if (-1 == baudRate) { + speed_t speed = data->baudRate; + if (-1 == ioctl(fd, IOSSIOSPEED, &speed)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s calling ioctl(.., IOSSIOSPEED, %ld )", strerror(errno), speed ); + return -1; + } else { + return 1; + } + } + #endif + + // If we have a good baud rate set it and lets go + if (-1 != baudRate) { + cfsetospeed(&options, baudRate); + cfsetispeed(&options, baudRate); + tcflush(fd, TCIFLUSH); + tcsetattr(fd, TCSANOW, &options); + return 1; + } + + snprintf(data->errorString, sizeof(data->errorString), "Error baud rate of %d is not supported on your platform", data->baudRate); + return -1; +} + +void EIO_Update(uv_work_t* req) { + ConnectionOptionsBaton* data = static_cast(req->data); + setBaudRate(data); +} + +int setup(int fd, OpenBaton *data) { + UnixPlatformOptions* platformOptions = static_cast(data->platformOptions); + + int dataBits = ToDataBitsConstant(data->dataBits); + if (-1 == dataBits) { + snprintf(data->errorString, sizeof(data->errorString), "Invalid data bits setting %d", data->dataBits); + return -1; + } + + // Snow Leopard doesn't have O_CLOEXEC + if (-1 == fcntl(fd, F_SETFD, FD_CLOEXEC)) { + snprintf(data->errorString, sizeof(data->errorString), "Error %s Cannot open %s", strerror(errno), data->path); + return -1; + } + + // Copy the connection options into the ConnectionOptionsBaton to set the baud rate + ConnectionOptionsBaton* connectionOptions = new ConnectionOptionsBaton(); + memset(connectionOptions, 0, sizeof(ConnectionOptionsBaton)); + connectionOptions->fd = fd; + connectionOptions->baudRate = data->baudRate; + + if (-1 == setBaudRate(connectionOptions)) { + strncpy(data->errorString, connectionOptions->errorString, sizeof(data->errorString)); + delete(connectionOptions); + return -1; + } + delete(connectionOptions); + + // Get port configuration for modification + struct termios options; + if (tcgetattr(fd, &options)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: tcgetattr encountering %s", strerror(errno)); + return -1; + } + + // IGNPAR: ignore bytes with parity errors + options.c_iflag = IGNPAR; + + // ICRNL: map CR to NL (otherwise a CR input on the other computer will not terminate input) + // Future potential option + // options.c_iflag = ICRNL; + // otherwise make device raw (no other input processing) + + // Specify data bits + options.c_cflag &= ~CSIZE; + options.c_cflag |= dataBits; + + options.c_cflag &= ~(CRTSCTS); + + if (data->rtscts) { + options.c_cflag |= CRTSCTS; + // evaluate specific flow control options + } + + options.c_iflag &= ~(IXON | IXOFF | IXANY); + + if (data->xon) { + options.c_iflag |= IXON; + } + + if (data->xoff) { + options.c_iflag |= IXOFF; + } + + if (data->xany) { + options.c_iflag |= IXANY; + } + + switch (data->parity) { + case SERIALPORT_PARITY_NONE: + options.c_cflag &= ~PARENB; + // options.c_cflag &= ~CSTOPB; + // options.c_cflag &= ~CSIZE; + // options.c_cflag |= CS8; + break; + case SERIALPORT_PARITY_ODD: + options.c_cflag |= PARENB; + options.c_cflag |= PARODD; + // options.c_cflag &= ~CSTOPB; + // options.c_cflag &= ~CSIZE; + // options.c_cflag |= CS7; + break; + case SERIALPORT_PARITY_EVEN: + options.c_cflag |= PARENB; + options.c_cflag &= ~PARODD; + // options.c_cflag &= ~CSTOPB; + // options.c_cflag &= ~CSIZE; + // options.c_cflag |= CS7; + break; + default: + snprintf(data->errorString, sizeof(data->errorString), "Invalid parity setting %d", data->parity); + return -1; + } + + switch (data->stopBits) { + case SERIALPORT_STOPBITS_ONE: + options.c_cflag &= ~CSTOPB; + break; + case SERIALPORT_STOPBITS_TWO: + options.c_cflag |= CSTOPB; + break; + default: + snprintf(data->errorString, sizeof(data->errorString), "Invalid stop bits setting %d", data->stopBits); + return -1; + } + + options.c_cflag |= CLOCAL; // ignore status lines + options.c_cflag |= CREAD; // enable receiver + if (data->hupcl) { + options.c_cflag |= HUPCL; // drop DTR (i.e. hangup) on close + } + + // Raw output + options.c_oflag = 0; + + // ICANON makes partial lines not readable. It should be optional. + // It works with ICRNL. + options.c_lflag = 0; // ICANON; + + options.c_cc[VMIN]= platformOptions->vmin; + options.c_cc[VTIME]= platformOptions->vtime; + + // why? + tcflush(fd, TCIFLUSH); + + // check for error? + tcsetattr(fd, TCSANOW, &options); + + if (data->lock){ + if (-1 == flock(fd, LOCK_EX | LOCK_NB)) { + snprintf(data->errorString, sizeof(data->errorString), "Error %s Cannot lock port", strerror(errno)); + return -1; + } + } + + return 1; +} + +void EIO_Write(uv_work_t* req) { + QueuedWrite* queuedWrite = static_cast(req->data); + WriteBaton* data = static_cast(queuedWrite->baton); + int bytesWritten = 0; + + do { + errno = 0; // probably don't need this + bytesWritten = write(data->fd, data->bufferData + data->offset, data->bufferLength - data->offset); + if (-1 != bytesWritten) { + // there wasn't an error, do the math on what we actually wrote and keep writing until finished + data->offset += bytesWritten; + continue; + } + + // The write call was interrupted before anything was written, try again immediately. + if (errno == EINTR) { + // why try again right away instead of in another event loop? + continue; + } + + // Try again in another event loop + if (errno == EAGAIN || errno == EWOULDBLOCK){ + return; + } + + // EBAD would mean we're "disconnected" + + // a real error so lets bail + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, calling write", strerror(errno)); + return; + } while (data->bufferLength > data->offset); +} + +void EIO_Close(uv_work_t* req) { + CloseBaton* data = static_cast(req->data); + if (-1 == close(data->fd)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, unable to close fd %d", strerror(errno), data->fd); + } +} + +#ifdef __APPLE__ + +// Function prototypes +static kern_return_t FindModems(io_iterator_t *matchingServices); +static io_service_t GetUsbDevice(io_service_t service); +static stDeviceListItem* GetSerialDevices(); + + +static kern_return_t FindModems(io_iterator_t *matchingServices) { + kern_return_t kernResult; + CFMutableDictionaryRef classesToMatch; + classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); + if (classesToMatch != NULL) { + CFDictionarySetValue(classesToMatch, + CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDAllTypes)); + } + + kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, matchingServices); + + return kernResult; +} + +static io_service_t GetUsbDevice(io_service_t service) { + IOReturn status; + io_iterator_t iterator = 0; + io_service_t device = 0; + + if (!service) { + return device; + } + + status = IORegistryEntryCreateIterator(service, + kIOServicePlane, + (kIORegistryIterateParents | kIORegistryIterateRecursively), + &iterator); + + if (status == kIOReturnSuccess) { + io_service_t currentService; + while ((currentService = IOIteratorNext(iterator)) && device == 0) { + io_name_t serviceName; + status = IORegistryEntryGetNameInPlane(currentService, kIOServicePlane, serviceName); + if (status == kIOReturnSuccess && IOObjectConformsTo(currentService, kIOUSBDeviceClassName)) { + device = currentService; + } else { + // Release the service object which is no longer needed + (void) IOObjectRelease(currentService); + } + } + + // Release the iterator + (void) IOObjectRelease(iterator); + } + + return device; +} + +static void ExtractUsbInformation(stSerialDevice *serialDevice, IOUSBDeviceInterface **deviceInterface) { + kern_return_t kernResult; + UInt32 locationID; + kernResult = (*deviceInterface)->GetLocationID(deviceInterface, &locationID); + if (KERN_SUCCESS == kernResult) { + snprintf(serialDevice->locationId, 11, "0x%08x", locationID); + } + + UInt16 vendorID; + kernResult = (*deviceInterface)->GetDeviceVendor(deviceInterface, &vendorID); + if (KERN_SUCCESS == kernResult) { + snprintf(serialDevice->vendorId, 7, "0x%04x", vendorID); + } + + UInt16 productID; + kernResult = (*deviceInterface)->GetDeviceProduct(deviceInterface, &productID); + if (KERN_SUCCESS == kernResult) { + snprintf(serialDevice->productId, 7, "0x%04x", productID); + } +} + +static stDeviceListItem* GetSerialDevices() { + kern_return_t kernResult; + io_iterator_t serialPortIterator; + char bsdPath[MAXPATHLEN]; + + FindModems(&serialPortIterator); + + io_service_t modemService; + kernResult = KERN_FAILURE; + Boolean modemFound = false; + + // Initialize the returned path + *bsdPath = '\0'; + + stDeviceListItem* devices = NULL; + stDeviceListItem* lastDevice = NULL; + int length = 0; + + while ((modemService = IOIteratorNext(serialPortIterator))) { + CFTypeRef bsdPathAsCFString; + bsdPathAsCFString = IORegistryEntrySearchCFProperty( + modemService, + kIOServicePlane, + CFSTR(kIOCalloutDeviceKey), + kCFAllocatorDefault, + kIORegistryIterateRecursively); + + if (bsdPathAsCFString) { + Boolean result; + + // Convert the path from a CFString to a C (NUL-terminated) + result = CFStringGetCString((CFStringRef) bsdPathAsCFString, + bsdPath, + sizeof(bsdPath), + kCFStringEncodingUTF8); + CFRelease(bsdPathAsCFString); + + if (result) { + stDeviceListItem *deviceListItem = (stDeviceListItem*) malloc(sizeof(stDeviceListItem)); + stSerialDevice *serialDevice = &(deviceListItem->value); + strcpy(serialDevice->port, bsdPath); + memset(serialDevice->locationId, 0, sizeof(serialDevice->locationId)); + memset(serialDevice->vendorId, 0, sizeof(serialDevice->vendorId)); + memset(serialDevice->productId, 0, sizeof(serialDevice->productId)); + serialDevice->manufacturer[0] = '\0'; + serialDevice->serialNumber[0] = '\0'; + deviceListItem->next = NULL; + deviceListItem->length = &length; + + if (devices == NULL) { + devices = deviceListItem; + } else { + lastDevice->next = deviceListItem; + } + + lastDevice = deviceListItem; + length++; + + modemFound = true; + kernResult = KERN_SUCCESS; + + uv_mutex_lock(&list_mutex); + + io_service_t device = GetUsbDevice(modemService); + + if (device) { + CFStringRef manufacturerAsCFString = (CFStringRef) IORegistryEntryCreateCFProperty(device, + CFSTR(kUSBVendorString), + kCFAllocatorDefault, + 0); + + if (manufacturerAsCFString) { + Boolean result; + char manufacturer[MAXPATHLEN]; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString(manufacturerAsCFString, + manufacturer, + sizeof(manufacturer), + kCFStringEncodingUTF8); + + if (result) { + strcpy(serialDevice->manufacturer, manufacturer); + } + + CFRelease(manufacturerAsCFString); + } + + CFStringRef serialNumberAsCFString = (CFStringRef) IORegistryEntrySearchCFProperty(device, + kIOServicePlane, + CFSTR(kUSBSerialNumberString), + kCFAllocatorDefault, + kIORegistryIterateRecursively); + + if (serialNumberAsCFString) { + Boolean result; + char serialNumber[MAXPATHLEN]; + + // Convert from a CFString to a C (NUL-terminated) + result = CFStringGetCString(serialNumberAsCFString, + serialNumber, + sizeof(serialNumber), + kCFStringEncodingUTF8); + + if (result) { + strcpy(serialDevice->serialNumber, serialNumber); + } + + CFRelease(serialNumberAsCFString); + } + + IOCFPlugInInterface **plugInInterface = NULL; + SInt32 score; + HRESULT res; + + IOUSBDeviceInterface **deviceInterface = NULL; + + kernResult = IOCreatePlugInInterfaceForService(device, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, + &plugInInterface, &score); + + if ((kIOReturnSuccess != kernResult) || !plugInInterface) { + continue; + } + + // Use the plugin interface to retrieve the device interface. + res = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), + (LPVOID*) &deviceInterface); + + // Now done with the plugin interface. + (*plugInInterface)->Release(plugInInterface); + + if (res || deviceInterface == NULL) { + continue; + } + + // Extract the desired Information + ExtractUsbInformation(serialDevice, deviceInterface); + + // Release the Interface + (*deviceInterface)->Release(deviceInterface); + + // Release the device + (void) IOObjectRelease(device); + } + + uv_mutex_unlock(&list_mutex); + } + } + + // Release the io_service_t now that we are done with it. + (void) IOObjectRelease(modemService); + } + + IOObjectRelease(serialPortIterator); // Release the iterator. + + return devices; +} + +#endif + +void EIO_List(uv_work_t* req) { + ListBaton* data = static_cast(req->data); + +#ifndef __APPLE__ + // This code exists in javascript for other unix platforms + snprintf(data->errorString, sizeof(data->errorString), "List is not Implemented"); + return; +# else + if (!lockInitialised) { + uv_mutex_init(&list_mutex); + lockInitialised = TRUE; + } + + stDeviceListItem* devices = GetSerialDevices(); + if (*(devices->length) > 0) { + stDeviceListItem* next = devices; + + for (int i = 0, len = *(devices->length); i < len; i++) { + stSerialDevice device = (* next).value; + + ListResultItem* resultItem = new ListResultItem(); + resultItem->comName = device.port; + + if (*device.locationId) { + resultItem->locationId = device.locationId; + } + if (*device.vendorId) { + resultItem->vendorId = device.vendorId; + } + if (*device.productId) { + resultItem->productId = device.productId; + } + if (*device.manufacturer) { + resultItem->manufacturer = device.manufacturer; + } + if (*device.serialNumber) { + resultItem->serialNumber = device.serialNumber; + } + data->results.push_back(resultItem); + + stDeviceListItem* current = next; + + if (next->next != NULL) { + next = next->next; + } + + free(current); + } + } +#endif +} + +void EIO_Flush(uv_work_t* req) { + FlushBaton* data = static_cast(req->data); + + data->result = tcflush(data->fd, TCIFLUSH); +} + +void EIO_Set(uv_work_t* req) { + SetBaton* data = static_cast(req->data); + + int bits; + ioctl(data->fd, TIOCMGET, &bits); + + bits &= ~(TIOCM_RTS | TIOCM_CTS | TIOCM_DTR | TIOCM_DSR); + + if (data->rts) { + bits |= TIOCM_RTS; + } + + if (data->cts) { + bits |= TIOCM_CTS; + } + + if (data->dtr) { + bits |= TIOCM_DTR; + } + + if (data->dsr) { + bits |= TIOCM_DSR; + } + + int result = 0; + if (data->brk) { + result = ioctl(data->fd, TIOCSBRK, NULL); + } else { + result = ioctl(data->fd, TIOCCBRK, NULL); + } + + if (-1 == result) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno)); + return; + } + + if (-1 == ioctl(data->fd, TIOCMSET, &bits)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno)); + return; + } +} + +void EIO_Drain(uv_work_t* req) { + DrainBaton* data = static_cast(req->data); + + if (-1 == tcdrain(data->fd)) { + snprintf(data->errorString, sizeof(data->errorString), "Error: %s, cannot drain", strerror(errno)); + return; + } +} diff --git a/vendor/node-usb-native/src/serialport_win.cpp b/vendor/node-usb-native/src/serialport_win.cpp new file mode 100644 index 000000000..0440da987 --- /dev/null +++ b/vendor/node-usb-native/src/serialport_win.cpp @@ -0,0 +1,582 @@ +#include +#include +#include +#include "./serialport.h" +#include +#include +#include +#include +#pragma comment (lib, "setupapi.lib") + +#ifdef WIN32 + +#define MAX_BUFFER_SIZE 1000 + +struct WindowsPlatformOptions : OpenBatonPlatformOptions { +}; + +OpenBatonPlatformOptions* ParsePlatformOptions(const v8::Local& options) { + // currently none + return new WindowsPlatformOptions(); +} + +// Declare type of pointer to CancelIoEx function +typedef BOOL (WINAPI *CancelIoExType)(HANDLE hFile, LPOVERLAPPED lpOverlapped); + +std::list g_closingHandles; +int bufferSize; +void ErrorCodeToString(const char* prefix, int errorCode, char *errorStr) { + switch (errorCode) { + case ERROR_FILE_NOT_FOUND: + _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: File not found", prefix); + break; + case ERROR_INVALID_HANDLE: + _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Invalid handle", prefix); + break; + case ERROR_ACCESS_DENIED: + _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Access denied", prefix); + break; + case ERROR_OPERATION_ABORTED: + _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: operation aborted", prefix); + break; + default: + _snprintf_s(errorStr, ERROR_STRING_SIZE, _TRUNCATE, "%s: Unknown error code %d", prefix, errorCode); + break; + } +} + +void EIO_Open(uv_work_t* req) { + OpenBaton* data = static_cast(req->data); + + char originalPath[1024]; + strncpy_s(originalPath, sizeof(originalPath), data->path, _TRUNCATE); + // data->path is char[1024] but on Windows it has the form "COMx\0" or "COMxx\0" + // We want to prepend "\\\\.\\" to it before we call CreateFile + strncpy(data->path + 20, data->path, 10); + strncpy(data->path, "\\\\.\\", 4); + strncpy(data->path + 4, data->path + 20, 10); + + int shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + if (data->lock) { + shareMode = 0; + } + + HANDLE file = CreateFile( + data->path, + GENERIC_READ | GENERIC_WRITE, + shareMode, // dwShareMode 0 Prevents other processes from opening if they request delete, read, or write access + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, // allows for reading and writing at the same time and sets the handle for asynchronous I/O + NULL + ); + + if (file == INVALID_HANDLE_VALUE) { + DWORD errorCode = GetLastError(); + char temp[100]; + _snprintf_s(temp, sizeof(temp), _TRUNCATE, "Opening %s", originalPath); + ErrorCodeToString(temp, errorCode, data->errorString); + return; + } + + bufferSize = data->bufferSize; + if (bufferSize > MAX_BUFFER_SIZE) { + bufferSize = MAX_BUFFER_SIZE; + } + + DCB dcb = { 0 }; + SecureZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + + if (!GetCommState(file, &dcb)) { + ErrorCodeToString("Open (GetCommState)", GetLastError(), data->errorString); + CloseHandle(file); + return; + } + + if (data->hupcl) { + dcb.fDtrControl = DTR_CONTROL_ENABLE; + } else { + dcb.fDtrControl = DTR_CONTROL_DISABLE; // disable DTR to avoid reset + } + + dcb.Parity = NOPARITY; + dcb.ByteSize = 8; + dcb.StopBits = ONESTOPBIT; + dcb.fInX = FALSE; + dcb.fOutX = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fOutxCtsFlow = FALSE; + dcb.fRtsControl = RTS_CONTROL_ENABLE; + + dcb.fBinary = true; + dcb.BaudRate = data->baudRate; + dcb.ByteSize = data->dataBits; + + switch (data->parity) { + case SERIALPORT_PARITY_NONE: + dcb.Parity = NOPARITY; + break; + case SERIALPORT_PARITY_MARK: + dcb.Parity = MARKPARITY; + break; + case SERIALPORT_PARITY_EVEN: + dcb.Parity = EVENPARITY; + break; + case SERIALPORT_PARITY_ODD: + dcb.Parity = ODDPARITY; + break; + case SERIALPORT_PARITY_SPACE: + dcb.Parity = SPACEPARITY; + break; + } + + switch (data->stopBits) { + case SERIALPORT_STOPBITS_ONE: + dcb.StopBits = ONESTOPBIT; + break; + case SERIALPORT_STOPBITS_ONE_FIVE: + dcb.StopBits = ONE5STOPBITS; + break; + case SERIALPORT_STOPBITS_TWO: + dcb.StopBits = TWOSTOPBITS; + break; + } + + if (!SetCommState(file, &dcb)) { + ErrorCodeToString("Open (SetCommState)", GetLastError(), data->errorString); + CloseHandle(file); + return; + } + + // Set the com port read/write timeouts + DWORD serialBitsPerByte = 8/*std data bits*/ + 1/*start bit*/; + serialBitsPerByte += (data->parity == SERIALPORT_PARITY_NONE) ? 0 : 1; + serialBitsPerByte += (data->stopBits == SERIALPORT_STOPBITS_ONE) ? 1 : 2; + DWORD msPerByte = (data->baudRate > 0) ? + ((1000 * serialBitsPerByte + data->baudRate - 1) / data->baudRate) : + 1; + if (msPerByte < 1) { + msPerByte = 1; + } + COMMTIMEOUTS commTimeouts = {0}; + commTimeouts.ReadIntervalTimeout = msPerByte; // Minimize chance of concatenating of separate serial port packets on read + commTimeouts.ReadTotalTimeoutMultiplier = 0; // Do not allow big read timeout when big read buffer used + commTimeouts.ReadTotalTimeoutConstant = 1000; // Total read timeout (period of read loop) + commTimeouts.WriteTotalTimeoutConstant = 1000; // Const part of write timeout + commTimeouts.WriteTotalTimeoutMultiplier = msPerByte; // Variable part of write timeout (per byte) + if (!SetCommTimeouts(file, &commTimeouts)) { + ErrorCodeToString("Open (SetCommTimeouts)", GetLastError(), data->errorString); + CloseHandle(file); + return; + } + + // Remove garbage data in RX/TX queues + PurgeComm(file, PURGE_RXCLEAR); + PurgeComm(file, PURGE_TXCLEAR); + + data->result = (int)file; +} + +struct WatchPortBaton { + HANDLE fd; + DWORD bytesRead; + char buffer[MAX_BUFFER_SIZE]; + char errorString[ERROR_STRING_SIZE]; + DWORD errorCode; + bool disconnected; + Nan::Callback* dataCallback; + Nan::Callback* errorCallback; + Nan::Callback* disconnectedCallback; +}; + +void EIO_Update(uv_work_t* req) { + ConnectionOptionsBaton* data = static_cast(req->data); + + DCB dcb = { 0 }; + SecureZeroMemory(&dcb, sizeof(DCB)); + dcb.DCBlength = sizeof(DCB); + + if (!GetCommState((HANDLE)data->fd, &dcb)) { + ErrorCodeToString("GetCommState", GetLastError(), data->errorString); + return; + } + + dcb.BaudRate = data->baudRate; + + if (!SetCommState((HANDLE)data->fd, &dcb)) { + ErrorCodeToString("SetCommState", GetLastError(), data->errorString); + return; + } +} + +void EIO_Set(uv_work_t* req) { + SetBaton* data = static_cast(req->data); + + if (data->rts) { + EscapeCommFunction((HANDLE)data->fd, SETRTS); + } else { + EscapeCommFunction((HANDLE)data->fd, CLRRTS); + } + + if (data->dtr) { + EscapeCommFunction((HANDLE)data->fd, SETDTR); + } else { + EscapeCommFunction((HANDLE)data->fd, CLRDTR); + } + + if (data->brk) { + EscapeCommFunction((HANDLE)data->fd, SETBREAK); + } else { + EscapeCommFunction((HANDLE)data->fd, CLRBREAK); + } + + DWORD bits = 0; + + GetCommMask((HANDLE)data->fd, &bits); + + bits &= ~(EV_CTS | EV_DSR); + + if (data->cts) { + bits |= EV_CTS; + } + + if (data->dsr) { + bits |= EV_DSR; + } + + if (!SetCommMask((HANDLE)data->fd, bits)) { + ErrorCodeToString("Setting options on COM port (SetCommMask)", GetLastError(), data->errorString); + return; + } +} + + +void EIO_WatchPort(uv_work_t* req) { + WatchPortBaton* data = static_cast(req->data); + data->bytesRead = 0; + data->disconnected = false; + + // Event used by GetOverlappedResult(..., TRUE) to wait for incoming data or timeout + // Event MUST be used if program has several simultaneous asynchronous operations + // on the same handle (i.e. ReadFile and WriteFile) + HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + while (true) { + OVERLAPPED ov = {0}; + ov.hEvent = hEvent; + + // Start read operation - synchrounous or asynchronous + DWORD bytesReadSync = 0; + if (!ReadFile((HANDLE)data->fd, data->buffer, bufferSize, &bytesReadSync, &ov)) { + data->errorCode = GetLastError(); + if (data->errorCode != ERROR_IO_PENDING) { + // Read operation error + if (data->errorCode == ERROR_OPERATION_ABORTED) { + data->disconnected = true; + } else { + ErrorCodeToString("Reading from COM port (ReadFile)", data->errorCode, data->errorString); + CloseHandle(hEvent); + return; + } + break; + } + + // Read operation is asynchronous and is pending + // We MUST wait for operation completion before deallocation of OVERLAPPED struct + // or read data buffer + + // Wait for async read operation completion or timeout + DWORD bytesReadAsync = 0; + if (!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesReadAsync, TRUE)) { + // Read operation error + data->errorCode = GetLastError(); + if (data->errorCode == ERROR_OPERATION_ABORTED) { + data->disconnected = true; + } else { + ErrorCodeToString("Reading from COM port (GetOverlappedResult)", data->errorCode, data->errorString); + CloseHandle(hEvent); + return; + } + break; + } else { + // Read operation completed asynchronously + data->bytesRead = bytesReadAsync; + } + } else { + // Read operation completed synchronously + data->bytesRead = bytesReadSync; + } + + // Return data received if any + if (data->bytesRead > 0) { + break; + } + } + + CloseHandle(hEvent); +} + +bool IsClosingHandle(int fd) { + for (std::list::iterator it = g_closingHandles.begin(); it != g_closingHandles.end(); ++it) { + if (fd == *it) { + g_closingHandles.remove(fd); + return true; + } + } + return false; +} + +void DisposeWatchPortCallbacks(WatchPortBaton* data) { + delete data->dataCallback; + delete data->errorCallback; + delete data->disconnectedCallback; +} + +// FinalizerCallback will prevent WatchPortBaton::buffer from getting +// collected by gc while finalizing v8::ArrayBuffer. The buffer will +// get cleaned up through this callback. +static void FinalizerCallback(char* data, void* hint) { + uv_work_t* req = reinterpret_cast(hint); + WatchPortBaton* wpb = static_cast(req->data); + delete wpb; + delete req; +} + +void EIO_AfterWatchPort(uv_work_t* req) { + Nan::HandleScope scope; + + WatchPortBaton* data = static_cast(req->data); + if (data->disconnected) { + data->disconnectedCallback->Call(0, NULL); + DisposeWatchPortCallbacks(data); + goto cleanup; + } + + bool skipCleanup = false; + if (data->bytesRead > 0) { + v8::Local argv[1]; + argv[0] = Nan::NewBuffer(data->buffer, data->bytesRead, FinalizerCallback, req).ToLocalChecked(); + skipCleanup = true; + data->dataCallback->Call(1, argv); + } else if (data->errorCode > 0) { + if (data->errorCode == ERROR_INVALID_HANDLE && IsClosingHandle((int)data->fd)) { + DisposeWatchPortCallbacks(data); + goto cleanup; + } else { + v8::Local argv[1]; + argv[0] = Nan::Error(data->errorString); + data->errorCallback->Call(1, argv); + Sleep(100); // prevent the errors from occurring too fast + } + } + AfterOpenSuccess((int)data->fd, data->dataCallback, data->disconnectedCallback, data->errorCallback); + +cleanup: + if (!skipCleanup) { + delete data; + delete req; + } +} + +void AfterOpenSuccess(int fd, Nan::Callback* dataCallback, Nan::Callback* disconnectedCallback, Nan::Callback* errorCallback) { + WatchPortBaton* baton = new WatchPortBaton(); + memset(baton, 0, sizeof(WatchPortBaton)); + baton->fd = (HANDLE)fd; + baton->dataCallback = dataCallback; + baton->errorCallback = errorCallback; + baton->disconnectedCallback = disconnectedCallback; + + uv_work_t* req = new uv_work_t(); + req->data = baton; + + uv_queue_work(uv_default_loop(), req, EIO_WatchPort, (uv_after_work_cb)EIO_AfterWatchPort); +} + +void EIO_Write(uv_work_t* req) { + QueuedWrite* queuedWrite = static_cast(req->data); + WriteBaton* data = static_cast(queuedWrite->baton); + data->result = 0; + + do { + OVERLAPPED ov = {0}; + // Event used by GetOverlappedResult(..., TRUE) to wait for outgoing data or timeout + // Event MUST be used if program has several simultaneous asynchronous operations + // on the same handle (i.e. ReadFile and WriteFile) + ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + // Start write operation - synchronous or asynchronous + DWORD bytesWritten = 0; + if (!WriteFile((HANDLE)data->fd, data->bufferData, static_cast(data->bufferLength), &bytesWritten, &ov)) { + DWORD lastError = GetLastError(); + if (lastError != ERROR_IO_PENDING) { + // Write operation error + ErrorCodeToString("Writing to COM port (WriteFile)", lastError, data->errorString); + CloseHandle(ov.hEvent); + return; + } + // Write operation is completing asynchronously + // We MUST wait for the operation completion before deallocation of OVERLAPPED struct + // or write data buffer + + // block for async write operation completion + bytesWritten = 0; + if (!GetOverlappedResult((HANDLE)data->fd, &ov, &bytesWritten, TRUE)) { + // Write operation error + DWORD lastError = GetLastError(); + ErrorCodeToString("Writing to COM port (GetOverlappedResult)", lastError, data->errorString); + CloseHandle(ov.hEvent); + return; + } + } + // Write operation completed synchronously + data->result = bytesWritten; + data->offset += data->result; + CloseHandle(ov.hEvent); + } while (data->bufferLength > data->offset); +} + +void EIO_Close(uv_work_t* req) { + CloseBaton* data = static_cast(req->data); + + g_closingHandles.push_back(data->fd); + + HMODULE hKernel32 = LoadLibrary("kernel32.dll"); + // Look up function address + CancelIoExType pCancelIoEx = (CancelIoExType)GetProcAddress(hKernel32, "CancelIoEx"); + // Do something with it + if (pCancelIoEx) { + // Function exists so call it + // Cancel all pending IO Requests for the current device + pCancelIoEx((HANDLE)data->fd, NULL); + } + if (!CloseHandle((HANDLE)data->fd)) { + ErrorCodeToString("closing connection", GetLastError(), data->errorString); + return; + } +} + +char *copySubstring(char *someString, int n) +{ + char *new_ = (char*)malloc(sizeof(char)*n + 1); + strncpy_s(new_, n + 1, someString, n); + new_[n] = '\0'; + return new_; +} + +void EIO_List(uv_work_t* req) { + ListBaton* data = static_cast(req->data); + + GUID *guidDev = (GUID*)& GUID_DEVCLASS_PORTS; + HDEVINFO hDevInfo = SetupDiGetClassDevs(guidDev, NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE); + SP_DEVINFO_DATA deviceInfoData; + + int memberIndex = 0; + DWORD dwSize, dwPropertyRegDataType; + char szBuffer[400]; + char *pnpId; + char *vendorId; + char *productId; + char *name; + char *manufacturer; + char *locationId; + bool isCom; + while (true) { + pnpId = NULL; + vendorId = NULL; + productId = NULL; + name = NULL; + manufacturer = NULL; + locationId = NULL; + + ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA)); + deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + if (SetupDiEnumDeviceInfo(hDevInfo, memberIndex, &deviceInfoData) == FALSE) { + if (GetLastError() == ERROR_NO_MORE_ITEMS) { + break; + } + } + + dwSize = sizeof(szBuffer); + SetupDiGetDeviceInstanceId(hDevInfo, &deviceInfoData, szBuffer, dwSize, &dwSize); + szBuffer[dwSize] = '\0'; + pnpId = strdup(szBuffer); + + vendorId = strstr(szBuffer, "VID_"); + if (vendorId) { + vendorId += 4; + vendorId = copySubstring(vendorId, 4); + } + productId = strstr(szBuffer, "PID_"); + if (productId) { + productId += 4; + productId = copySubstring(productId, 4); + } + + if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_LOCATION_INFORMATION, &dwPropertyRegDataType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) { + locationId = strdup(szBuffer); + } + if (SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, SPDRP_MFG, &dwPropertyRegDataType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize)) { + manufacturer = strdup(szBuffer); + } + + HKEY hkey = SetupDiOpenDevRegKey(hDevInfo, &deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); + if (hkey != INVALID_HANDLE_VALUE) { + dwSize = sizeof(szBuffer); + if (RegQueryValueEx(hkey, "PortName", NULL, NULL, (LPBYTE)&szBuffer, &dwSize) == ERROR_SUCCESS) { + szBuffer[dwSize] = '\0'; + name = strdup(szBuffer); + isCom = strstr(szBuffer, "COM") != NULL; + } + } + if (isCom) { + ListResultItem* resultItem = new ListResultItem(); + resultItem->comName = name; + resultItem->manufacturer = manufacturer; + resultItem->pnpId = pnpId; + if (vendorId) { + resultItem->vendorId = vendorId; + } + if (productId) { + resultItem->productId = productId; + } + if (locationId) { + resultItem->locationId = locationId; + } + data->results.push_back(resultItem); + } + free(pnpId); + free(vendorId); + free(productId); + free(locationId); + free(manufacturer); + free(name); + + RegCloseKey(hkey); + memberIndex++; + } + if (hDevInfo) { + SetupDiDestroyDeviceInfoList(hDevInfo); + } +} + +void EIO_Flush(uv_work_t* req) { + FlushBaton* data = static_cast(req->data); + + if (!FlushFileBuffers((HANDLE)data->fd)) { + ErrorCodeToString("flushing connection (FlushFileBuffers)", GetLastError(), data->errorString); + return; + } +} + +void EIO_Drain(uv_work_t* req) { + DrainBaton* data = static_cast(req->data); + + if (!FlushFileBuffers((HANDLE)data->fd)) { + ErrorCodeToString("draining connection (FlushFileBuffers)", GetLastError(), data->errorString); + return; + } +} + +#endif