Compare commits
No commits in common. "master" and "add-arduino-generator" have entirely different histories.
master
...
add-arduin
8
.env
@ -1,8 +0,0 @@
|
|||||||
REACT_APP_COMPILER_URL=https://compiler.sensebox.de
|
|
||||||
REACT_APP_BOARD=sensebox-mcu
|
|
||||||
REACT_APP_BLOCKLY_API=https://api.blockly.sensebox.de
|
|
||||||
|
|
||||||
GENERATE_SOURCEMAP=false
|
|
||||||
|
|
||||||
# in days
|
|
||||||
REACT_APP_SHARE_LINK_EXPIRES=30
|
|
@ -1,41 +0,0 @@
|
|||||||
name: Build and push image
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container:
|
|
||||||
image: catthehacker/ubuntu:act-latest
|
|
||||||
#defaults:
|
|
||||||
# run:
|
|
||||||
# working-directory: /repo
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0 # all history for all branches and tags
|
|
||||||
|
|
||||||
- name: Login to gitea.simonzeyer.de Repo
|
|
||||||
uses: docker/login-action@v2
|
|
||||||
with:
|
|
||||||
registry: gitea.simonzeyer.de
|
|
||||||
username: ${{ secrets.DOCKER_REPO_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_REPO_PASSWD }}
|
|
||||||
|
|
||||||
- name: Get Meta
|
|
||||||
id: meta
|
|
||||||
run: |
|
|
||||||
echo REPO_NAME=$(echo ${GITHUB_REPOSITORY} | awk -F"/" '{print $2}') >> $GITHUB_OUTPUT
|
|
||||||
echo REPO_VERSION=$(git describe --always | sed 's/^v//') >> $GITHUB_OUTPUT
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v4
|
|
||||||
env:
|
|
||||||
ACTIONS_RUNTIME_TOKEN: '' # See https://gitea.com/gitea/act_runner/issues/119
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
gitea.simonzeyer.de/schuelerlabor-cleverlab/smarti:${{ steps.meta.outputs.REPO_VERSION }}
|
|
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: Create a report to help us improve
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Steps to reproduce the behavior:
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
|
||||||
- OS: [e.g. iOS]
|
|
||||||
- Browser [e.g. chrome, safari]
|
|
||||||
- Version [e.g. 22]
|
|
||||||
|
|
||||||
**Smartphone (please complete the following information):**
|
|
||||||
- Device: [e.g. iPhone6]
|
|
||||||
- OS: [e.g. iOS8.1]
|
|
||||||
- Browser [e.g. stock browser, safari]
|
|
||||||
- Version [e.g. 22]
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,20 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request
|
|
||||||
about: Suggest an idea for this project
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
14
.github/ISSUE_TEMPLATE/short-issue.md
vendored
@ -1,14 +0,0 @@
|
|||||||
---
|
|
||||||
name: Short Issue
|
|
||||||
about: Template for Short Issues
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Current behaviour
|
|
||||||
Describe the current behaviour
|
|
||||||
|
|
||||||
### Expected behaviour
|
|
||||||
Describe how it is supposed to work
|
|
1
.gitignore
vendored
@ -22,4 +22,3 @@ npm-debug.log*
|
|||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
49
.idea/workspace.xml
generated
@ -1,49 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ChangeListManager">
|
|
||||||
<list default="true" id="a119841b-a70a-4b0e-91b6-4da6cd81f169" name="Default Changelist" comment="">
|
|
||||||
<change beforePath="$PROJECT_DIR$/src/components/Tutorial/tutorials.json" beforeDir="false" />
|
|
||||||
</list>
|
|
||||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
||||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
||||||
</component>
|
|
||||||
<component name="RunDashboard">
|
|
||||||
<option name="ruleStates">
|
|
||||||
<list>
|
|
||||||
<RuleState>
|
|
||||||
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
|
|
||||||
</RuleState>
|
|
||||||
<RuleState>
|
|
||||||
<option name="name" value="StatusDashboardGroupingRule" />
|
|
||||||
</RuleState>
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="SvnConfiguration">
|
|
||||||
<configuration />
|
|
||||||
</component>
|
|
||||||
<component name="TaskManager">
|
|
||||||
<task active="true" id="Default" summary="Default task">
|
|
||||||
<changelist id="a119841b-a70a-4b0e-91b6-4da6cd81f169" name="Default Changelist" comment="" />
|
|
||||||
<created>1599559503505</created>
|
|
||||||
<option name="number" value="Default" />
|
|
||||||
<option name="presentableId" value="Default" />
|
|
||||||
<updated>1599559503505</updated>
|
|
||||||
</task>
|
|
||||||
<servers />
|
|
||||||
</component>
|
|
||||||
<component name="Vcs.Log.Tabs.Properties">
|
|
||||||
<option name="TAB_STATES">
|
|
||||||
<map>
|
|
||||||
<entry key="MAIN">
|
|
||||||
<value>
|
|
||||||
<State />
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
26
CITATION.cff
@ -1,26 +0,0 @@
|
|||||||
# This CITATION.cff file was generated with cffinit.
|
|
||||||
# Visit https://bit.ly/cffinit to generate yours today!
|
|
||||||
|
|
||||||
cff-version: 1.2.0
|
|
||||||
title: senseBox Learn- and Programming Environment
|
|
||||||
message: Please cite this software using these metadata.
|
|
||||||
type: software
|
|
||||||
version: 1.0.0
|
|
||||||
date-released: 2021-09-30
|
|
||||||
url: "https://github.com/sensebox/React-Ardublockly"
|
|
||||||
authors:
|
|
||||||
- given-names: Mario
|
|
||||||
family-names: Pesch
|
|
||||||
email: mario.pesch@uni-muenster.de
|
|
||||||
affiliation: >-
|
|
||||||
Institute for geoinformatics University of
|
|
||||||
Muenster
|
|
||||||
- given-names: Luc
|
|
||||||
family-names: Niski
|
|
||||||
affiliation: >-
|
|
||||||
Institute for geoinformatics University of
|
|
||||||
Muenster
|
|
||||||
- given-names: Felix
|
|
||||||
family-names: Erdmann
|
|
||||||
email: f.erdmann@reedu.de
|
|
||||||
affiliation: Reedu GmbH & Co. KG
|
|
12
Dockerfile
@ -1,12 +0,0 @@
|
|||||||
# specify the node base image with your desired version node:<version>
|
|
||||||
FROM node:16 as build
|
|
||||||
WORKDIR /app
|
|
||||||
copy ./ /app
|
|
||||||
RUN npm install --verbose
|
|
||||||
RUN npm run build --verbose
|
|
||||||
|
|
||||||
FROM nginx:alpine
|
|
||||||
COPY --from=build /app/build/ /usr/share/nginx/html
|
|
||||||
RUN chmod 755 /usr/share/nginx/html/ -R
|
|
||||||
EXPOSE 80
|
|
||||||
ENTRYPOINT ["sh", "-c", "cd /usr/share/nginx/html/ && nginx -g 'daemon off;'"]
|
|
201
LICENSE
@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
http://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.
|
|
93
NEWS.md
@ -1,93 +0,0 @@
|
|||||||
# Blockly 2020
|
|
||||||
|
|
||||||
(aktuelles Preview unter: https://deploy-preview-37--blockly-react.netlify.app/)
|
|
||||||
|
|
||||||
In den letzten Wochen haben wir eine komplett neue Lern- und Programmierumgebung für die senseBox geschaffen. Die Basis bildet hierbei weiterhin Google Blockly und das Frontend wird über React realisiert. Fast alle Blöcke wurden bereits aus der alten Version in die neue Version migriert.
|
|
||||||
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Blockly Core
|
|
||||||
Nachdem die bisherige Version, die unter [blockly.sensebox.de](https://blockly.sensebox.de) verfügbar ist, auf einen Google Blockly Core von 2016 aufbaut, wurde es Zeit ein großes Update durchzuführen. Durch den neuen Blockly Core lassen sich auch andere Renderer der Blöcke verwenden. In den Einstellungen kannst du zwischen den zwei Renderern Geras und Zelos auswählen. Geras ist der klassische Blockly Renderer während Zelos vor allem für Touchoberfläche optimiert worden ist.
|
|
||||||
|
|
||||||
### Typed Variablen
|
|
||||||
|
|
||||||
Neue Variablen werden nun direkt mit einem bestimmten Datentyp angelegt. Ein einfacher Check überprüft ob der zurückgegeben Typ eines Blockes mit dem der Variablen kompatibel ist.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### Funktionen (funktioniert aktuell nicht!)
|
|
||||||
|
|
||||||
Funktionen mit Rückgabe Wert und Eingabeparametern können angelegt werden. Durch die Verwendung von Funktionen lassen sich auch komplexere Programme übersichtlicher darstellen und bearbeiten. Beim Anlegen einer Funktion kann über das Zahnrad weitere Eingabeparameter hinzuefügt werden.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### GPS
|
|
||||||
|
|
||||||
Der Code für das GPS Modul wurde neu aufgebaut und ermöglicht es deutlich schneller einen GPS Fix zu bekommen. Zusätzlich lassen sich bald! die Koordinaten in zwei verschiedenen Formaten zurückgebenlassen. Zum einen als Kommazahl zum anderen als Zahl ohne nachkommastellen
|
|
||||||
|
|
||||||
### MQTT
|
|
||||||
Zwei einfache Blöcke ermöglichen es nun die Daten über MQTT an einen Broker zu versenden. Zwei Broker sind bereits "vorprogrammiert" (Adafruit IO und DIOTY). Natürlich können auch eigene Broker verwendet werden. Falls ihr gute freie Broker kennt, die wir hinzufügen sollten meldet euch einfach bei uns.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### TTN Mapper
|
|
||||||
Bisher war es möglich einen "kleinen" TTN Mapper zu bauen, der die Daten als Cayenne Payload versendet hat. Es gibt nun einen Block der es direkt ermöglicht einen vollständigen TTN Mapper zu Programmieren, der die Daten auch auf [TTNMapper](https://ttnmapper.org/) veröffentlichen kann.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## Fronted
|
|
||||||
|
|
||||||
In der Oberfläche gibt es einige Neuigkeiten. Ziel ist es Blockly für die senseBox zu einer vollständigen Lern- und Programmierumgebung weiterzuentwickeln.
|
|
||||||
|
|
||||||
Die Codeanzeige ist standardmäßig ausgeblendet kann aber einfach durch eine Klick auf das `</>` Icon hinzugefügt werden.
|
|
||||||
|
|
||||||
### Login mit openSenseMap/senseBox Account
|
|
||||||
|
|
||||||
Im Menü unter Login könnt ihr euch mit eurem openSenseMap Account anmelden. Sobald ihr angemeldet seid habt ihr die Möglichkeit Projekte online zu speichern.
|
|
||||||
Sobald der Login mit dem openSenseMap Account erfolgreich war lassen sich die bereits registrierten senseBox unter Account einsehen.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
Im Block zum senden an die openSenseMap müssen dann auch keine IDs mehr ausgewählt werden sondern die registrierten senseBox können einfach aus dem Dropdown Menü ausgewählt werden.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Speichern von Projekten
|
|
||||||
|
|
||||||
Nach dem Login über den openSenseMap Account lassen sich Projekte online speichern und wieder abrufen
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### Tutorials
|
|
||||||
|
|
||||||
Es gibt jetzt Tutorials! Eine reihe von verschiedenen Tutorials zeigt dir die ersten Schritte in der Programmierung mit Blockly. (Inhalte werden noch ausgebaut)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Gallery
|
|
||||||
|
|
||||||
In der Gallery finden sich Beispiele mit verschiedenen Programmen. Die Beispiele können direkt in Blockly geöffnet werden, um Änderungen vorzunehmen oder das Programm direkt auf deine senseBox zu übertragen.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
### Teilen von Programmen
|
|
||||||
|
|
||||||
Über den Share Button kann ein Link zum Teilen der aktuellen Blöcke erstellt werden. Wann immer du dein Projekt mit anderen Teilen willst musst du nicht mehr eine XML Datei erstellen und verschicken sondern kannst einen Link erstellen, der direkt zu deinem Programm führt. Beachte, dass vor dem Teilen von Blöcken sämtliche sensiblen Daten, wie zum Beispiel Passwörter, Netzwerknamen, Lora oder openSenseMap Keys entfernt werden sollten. Die Links zum teilen von Programmen laufen nach 30 Tagen ab.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## Fehler
|
|
||||||
|
|
||||||
Falls ihr Fehler findet legt bitte ein Issue in folgendem Repository an: https://github.com/sensebox/React-Ardublockly/issues
|
|
||||||
|
|
73
README.md
@ -1,25 +1,68 @@
|
|||||||
<img src="/src/components/sensebox_logo.svg?raw=true" height="128" alt="senseBox Logo"/>
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
# React Ardublockly
|
## Available Scripts
|
||||||
|
|
||||||
This repository contains the source code and documentation of [sensebox-ardublockly](https://sensebox-ardublockly.netlify.app/).
|
In the project directory, you can run:
|
||||||
|
|
||||||
This project was created with [Create React App](https://github.com/facebook/create-react-app) and represents the continuation or improvement of [blockly.sensebox.de](https://blockly.sensebox.de/ardublockly/?lang=de&board=sensebox-mcu).
|
### `npm start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.<br />
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||||
|
|
||||||
## Getting Started
|
The page will reload if you make edits.<br />
|
||||||
|
You will also see any lint errors in the console.
|
||||||
|
|
||||||
1. [Download](https://github.com/sensebox/React-Ardublockly/archive/master.zip) or clone the GitHub Repository ``git clone https://github.com/sensebox/React-Ardublockly`` and checkout to branch ``master``.
|
### `npm test`
|
||||||
|
|
||||||
2. install [Node.js v10.xx](https://nodejs.org/en/) on your local machine
|
Launches the test runner in the interactive watch mode.<br />
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
3. open shell and navigate inside folder ``React-Ardublockly``
|
### `npm run build`
|
||||||
* run ``npm install``
|
|
||||||
* run ``npm start``
|
|
||||||
4. open [localhost:3000](http://localhost:3000)
|
|
||||||
|
|
||||||
## Troubleshoot
|
Builds the app for production to the `build` folder.<br />
|
||||||
Ensure that line 14 in [store.js](https://github.com/sensebox/React-Ardublockly/blob/master/src/store.js#L14) is commented out or otherwise you have installed [Redux DevTools Extension](http://extension.remotedev.io/).
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
## Demo
|
The build is minified and the filenames include the hashes.<br />
|
||||||
A demo of the current status of the master branch can be accessed via [https://blockly-react.netlify.app/](https://blockly-react.netlify.app/) :rocket:.
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `npm run eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||||
|
|
||||||
|
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||||
|
|
||||||
|
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||||
|
|
||||||
|
### Code Splitting
|
||||||
|
|
||||||
|
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
|
||||||
|
|
||||||
|
### Analyzing the Bundle Size
|
||||||
|
|
||||||
|
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
|
||||||
|
|
||||||
|
### Making a Progressive Web App
|
||||||
|
|
||||||
|
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
|
||||||
|
|
||||||
|
### Advanced Configuration
|
||||||
|
|
||||||
|
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
|
||||||
|
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
|
||||||
|
|
||||||
|
### `npm run build` fails to minify
|
||||||
|
|
||||||
|
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
# docker-compose.yml
|
|
||||||
services:
|
|
||||||
smarti:
|
|
||||||
mem_limit: 2048m
|
|
||||||
mem_reservation: 128M
|
|
||||||
cpus: 2
|
|
||||||
build:
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
ports:
|
|
||||||
- "80"
|
|
||||||
|
|
14282
package-lock.json
generated
Normal file
88
package.json
@ -1,64 +1,26 @@
|
|||||||
{
|
{
|
||||||
"name": "blockly-react",
|
"name": "blockly-react",
|
||||||
"version": "1.0.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blockly/block-plus-minus": "^4.0.4",
|
"@fortawesome/fontawesome-svg-core": "^1.2.30",
|
||||||
"@blockly/field-grid-dropdown": "^2.0.4",
|
"@fortawesome/free-solid-svg-icons": "^5.14.0",
|
||||||
"@blockly/field-slider": "4.0.4",
|
"@fortawesome/react-fontawesome": "^0.1.11",
|
||||||
"@blockly/plugin-scroll-options": "^3.0.5",
|
"@material-ui/core": "^4.11.0",
|
||||||
"@blockly/plugin-typed-variable-modal": "^5.0.6",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@blockly/workspace-backpack": "^3.0.4",
|
"@testing-library/react": "^9.5.0",
|
||||||
"@blockly/zoom-to-fit": "^3.0.4",
|
|
||||||
"@emotion/react": "^11.10.5",
|
|
||||||
"@emotion/styled": "^11.10.5",
|
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.2.1",
|
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
|
||||||
"@monaco-editor/react": "^4.3.1",
|
|
||||||
"@mui/lab": "^5.0.0-alpha.110",
|
|
||||||
"@mui/material": "^5.10.16",
|
|
||||||
"@mui/styles": "^5.10.16",
|
|
||||||
"@testing-library/jest-dom": "^5.16.1",
|
|
||||||
"@testing-library/react": "^12.1.2",
|
|
||||||
"@testing-library/user-event": "^7.2.1",
|
"@testing-library/user-event": "^7.2.1",
|
||||||
"axios": "^0.22.0",
|
"blockly": "^3.20200625.2",
|
||||||
"blockly": "^9.2.0",
|
"react": "^16.13.1",
|
||||||
"file-saver": "^2.0.5",
|
"react-dom": "^16.13.1",
|
||||||
"markdown-it": "^12.3.2",
|
"react-redux": "^7.2.0",
|
||||||
"mnemonic-id": "^3.2.7",
|
"react-router-dom": "^5.2.0",
|
||||||
"moment": "^2.29.4",
|
"react-scripts": "3.4.1",
|
||||||
"prismjs": "^1.27.0",
|
"redux": "^4.0.5",
|
||||||
"qrcode.react": "^3.1.0",
|
"redux-thunk": "^2.3.0"
|
||||||
"react": "^17.0.2",
|
|
||||||
"react-cookie-consent": "^7.2.1",
|
|
||||||
"react-dom": "^17.0.2",
|
|
||||||
"react-markdown": "^8.0.0",
|
|
||||||
"react-markdown-editor-lite": "^1.3.3",
|
|
||||||
"react-mde": "^11.5.0",
|
|
||||||
"react-rating-stars-component": "^2.2.0",
|
|
||||||
"react-redux": "^7.2.9",
|
|
||||||
"react-router-dom": "^5.3.3",
|
|
||||||
"react-scripts": "^5.0.1",
|
|
||||||
"react-share": "^4.4.0",
|
|
||||||
"react-spinners": "^0.13.3",
|
|
||||||
"reactour": "^1.18.7",
|
|
||||||
"redux": "^4.2.0",
|
|
||||||
"redux-thunk": "^2.4.1",
|
|
||||||
"rehype-raw": "^6.1.1",
|
|
||||||
"remark-gemoji": "^7.0.1",
|
|
||||||
"remark-gfm": "^3.0.1",
|
|
||||||
"styled-components": "^4.4.1",
|
|
||||||
"uuid": "^8.3.1",
|
|
||||||
"watchpack": "^2.3.1"
|
|
||||||
},
|
|
||||||
"resolutions": {
|
|
||||||
"//": "See https://github.com/facebook/create-react-app/issues/11773",
|
|
||||||
"react-error-overlay": "6.0.9"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node_modules/react-scripts/bin/react-scripts.js start",
|
"start": "react-scripts start",
|
||||||
"dev": "set \"REACT_APP_BLOCKLY_API=http://localhost:8080\" && npm start",
|
|
||||||
"build": "react-scripts build",
|
"build": "react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject"
|
||||||
@ -66,12 +28,16 @@
|
|||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": "react-app"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": {
|
||||||
">0.2%",
|
"production": [
|
||||||
"not dead",
|
">0.2%",
|
||||||
"not op_mini all"
|
"not dead",
|
||||||
],
|
"not op_mini all"
|
||||||
"devDependencies": {
|
],
|
||||||
"@babel/plugin-proposal-private-property-in-object": "7.21.11"
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
/* /index.html 200
|
|
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.3 KiB |
@ -2,9 +2,13 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#4EAF47" />
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Web site created using create-react-app"
|
||||||
|
/>
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
@ -20,17 +24,10 @@
|
|||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
<title>senseBox Blockly</title>
|
<title>React App</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<!-- Matomo Image Tracker-->
|
|
||||||
<img
|
|
||||||
src="https://piwik.sensebox.kaufen/matomo.php?idsite=9&rec=1"
|
|
||||||
style="border: 0; display: none"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
<!-- End Matomo -->
|
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<!--
|
<!--
|
||||||
This HTML file is a template.
|
This HTML file is a template.
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
"name": "Create React App Sample",
|
"name": "Create React App Sample",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.png",
|
"src": "favicon.ico",
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
"type": "image/png"
|
"type": "image/x-icon"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "logo192.png",
|
"src": "logo192.png",
|
||||||
@ -20,6 +20,6 @@
|
|||||||
],
|
],
|
||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"theme_color": "#4EAF47",
|
"theme_color": "#000000",
|
||||||
"background_color": "#4EAF47"
|
"background_color": "#ffffff"
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 43 B |
@ -1 +0,0 @@
|
|||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="12.71" height="8.79" viewBox="0 0 12.71 8.79"><title>dropdown-arrow</title><g opacity="0.1"><path d="M12.71,2.44A2.41,2.41,0,0,1,12,4.16L8.08,8.08a2.45,2.45,0,0,1-3.45,0L0.72,4.16A2.42,2.42,0,0,1,0,2.44,2.48,2.48,0,0,1,.71.71C1,0.47,1.43,0,6.36,0S11.75,0.46,12,.71A2.44,2.44,0,0,1,12.71,2.44Z" fill="#231f20"/></g><path d="M6.36,7.79a1.43,1.43,0,0,1-1-.42L1.42,3.45a1.44,1.44,0,0,1,0-2c0.56-.56,9.31-0.56,9.87,0a1.44,1.44,0,0,1,0,2L7.37,7.37A1.43,1.43,0,0,1,6.36,7.79Z" fill="#fff"/></svg>
|
|
Before Width: | Height: | Size: 569 B |
Before Width: | Height: | Size: 326 B |
Before Width: | Height: | Size: 766 B |
Before Width: | Height: | Size: 198 B |
Before Width: | Height: | Size: 1010 B |
Before Width: | Height: | Size: 771 B |
Before Width: | Height: | Size: 738 B |
Before Width: | Height: | Size: 2.5 KiB |
@ -1,74 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="96px" height="124px">
|
|
||||||
<style type="text/css">
|
|
||||||
#background {
|
|
||||||
fill: none;
|
|
||||||
}
|
|
||||||
.arrows {
|
|
||||||
fill: #000;
|
|
||||||
stroke: none;
|
|
||||||
}
|
|
||||||
.selected>.arrows {
|
|
||||||
fill: #fff;
|
|
||||||
}
|
|
||||||
.checkmark {
|
|
||||||
fill: #000;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 10pt;
|
|
||||||
text-anchor: middle;
|
|
||||||
}
|
|
||||||
.trash {
|
|
||||||
fill: #888;
|
|
||||||
}
|
|
||||||
.zoom {
|
|
||||||
fill: none;
|
|
||||||
stroke: #888;
|
|
||||||
stroke-width: 2;
|
|
||||||
stroke-linecap: round;
|
|
||||||
}
|
|
||||||
.zoom>.center {
|
|
||||||
fill: #888;
|
|
||||||
stroke-width: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<rect id="background" width="96" height="124" x="0" y="0" />
|
|
||||||
|
|
||||||
<g>
|
|
||||||
<path class="arrows" d="M 13,1.5 13,14.5 1.74,8 z" />
|
|
||||||
<path class="arrows" d="M 17.5,3 30.5,3 24,14.26 z" />
|
|
||||||
<path class="arrows" d="M 35,1.5 35,14.5 46.26,8 z" />
|
|
||||||
</g>
|
|
||||||
<g class="selected" transform="translate(0, 16)">
|
|
||||||
<path class="arrows" d="M 13,1.5 13,14.5 1.74,8 z" />
|
|
||||||
<path class="arrows" d="M 17.5,3 30.5,3 24,14.26 z" />
|
|
||||||
<path class="arrows" d="M 35,1.5 35,14.5 46.26,8 z" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<text class="checkmark" x="55.5" y="28">✓</text>
|
|
||||||
|
|
||||||
<g class="trash">
|
|
||||||
<path d="M 2,41 v 6 h 42 v -6 h -10.5 l -3,-3 h -15 l -3,3 z" />
|
|
||||||
<rect width="36" height="20" x="5" y="50" />
|
|
||||||
<rect width="36" height="42" x="5" y="50" rx="4" ry="4" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g class="zoom">
|
|
||||||
<circle r="11.5" cx="16" cy="108" />
|
|
||||||
<circle r="4.3" cx="16" cy="108" class="center" />
|
|
||||||
<path d="m 28,108 h3" />
|
|
||||||
<path d="m 1,108 h3" />
|
|
||||||
<path d="m 16,120 v3" />
|
|
||||||
<path d="m 16,93 v3" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g class="zoom">
|
|
||||||
<circle r="15" cx="48" cy="108" />
|
|
||||||
<path d="m 48,101.6 v12.8" />
|
|
||||||
<path d="m 41.6,108 h12.8" />
|
|
||||||
</g>
|
|
||||||
|
|
||||||
<g class="zoom">
|
|
||||||
<circle r="15" cx="80" cy="108" />
|
|
||||||
<path d="m 73.6,108 h12.8" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 194 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 777 KiB |
Before Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 728 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 286 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 163 KiB |
Before Width: | Height: | Size: 343 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 59 KiB |
Before Width: | Height: | Size: 472 KiB |
Before Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 47 KiB |
71
src/App.css
@ -1,74 +1,7 @@
|
|||||||
.wrapper {
|
.wrapper {
|
||||||
min-height: calc(
|
min-height: 100vh; /* will cover the 100% of viewport */
|
||||||
100vh - 60px
|
|
||||||
); /* will cover the 100% of viewport - height of footer (padding-bottom) */
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-bottom: 60px; /* height of your footer + 30px*/
|
padding-bottom: 60px; /* height of your footer + 30px*/y
|
||||||
}
|
|
||||||
|
|
||||||
.tutorial img {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
max-height: 40vh;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.news img {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
max-height: 40vh;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tutorial blockquote {
|
|
||||||
background: #f9f9f9;
|
|
||||||
border-left: 10px solid#4EAF47;
|
|
||||||
margin: 1.5em 10px;
|
|
||||||
padding: 0.5em 10px;
|
|
||||||
quotes: "\201C""\201D""\2018""\2019";
|
|
||||||
}
|
|
||||||
blockquote:before {
|
|
||||||
color: #4eaf47;
|
|
||||||
content: open-quote;
|
|
||||||
font-size: 4em;
|
|
||||||
line-height: 0.1em;
|
|
||||||
margin-right: 0.25em;
|
|
||||||
vertical-align: -0.4em;
|
|
||||||
}
|
|
||||||
blockquote p {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tutorial table,
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tutorial th {
|
|
||||||
padding-top: 12px;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
text-align: left;
|
|
||||||
border-color: #4eaf47;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlay {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--url: url('./data/mcu_opacity.png');
|
|
||||||
}
|
|
||||||
|
|
||||||
.blocklySvg {
|
|
||||||
background-image: var(--url);
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
}
|
67
src/App.js
@ -1,52 +1,41 @@
|
|||||||
import React, { Component } from "react";
|
import React from 'react';
|
||||||
|
|
||||||
import { Router } from "react-router-dom";
|
import { BrowserRouter as Router } from 'react-router-dom';
|
||||||
import { createBrowserHistory } from "history";
|
|
||||||
|
|
||||||
import { Provider } from "react-redux";
|
import { Provider } from 'react-redux';
|
||||||
import store from "./store";
|
import store from './store';
|
||||||
import { loadUser } from "./actions/authActions";
|
|
||||||
|
|
||||||
import "./App.css";
|
import { ThemeProvider, createMuiTheme } from '@material-ui/core/styles';
|
||||||
|
|
||||||
import { ThemeProvider, StyledEngineProvider, createTheme } from "@mui/material/styles";
|
import Navbar from './components/Navbar';
|
||||||
|
import Footer from './components/Footer';
|
||||||
|
import Routes from './components/Routes';
|
||||||
|
|
||||||
import Content from "./components/Content";
|
const theme = createMuiTheme({
|
||||||
|
|
||||||
const theme = createTheme({
|
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: "#4EAF47",
|
main: '#4EAF47',
|
||||||
contrastText: "#ffffff",
|
}
|
||||||
},
|
}
|
||||||
secondary: {
|
|
||||||
main: "#DDDDDD",
|
|
||||||
},
|
|
||||||
button: {
|
|
||||||
compile: "#e27136",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
class App extends Component {
|
|
||||||
componentDidMount() {
|
|
||||||
store.dispatch(loadUser());
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
function App() {
|
||||||
const customHistory = createBrowserHistory();
|
return (
|
||||||
return (
|
<ThemeProvider theme={theme}>
|
||||||
<StyledEngineProvider injectFirst>
|
<Provider store={store}>
|
||||||
<ThemeProvider theme={theme}>
|
<Router>
|
||||||
<Provider store={store}>
|
<div className="wrapper">
|
||||||
<Router history={customHistory}>
|
<Navbar />
|
||||||
<Content />
|
<div style={{ margin: '0 22px' }}>
|
||||||
</Router>
|
<Routes />
|
||||||
</Provider>
|
</div>
|
||||||
</ThemeProvider>
|
<Footer />
|
||||||
</StyledEngineProvider>
|
</div>
|
||||||
);
|
</Router>
|
||||||
}
|
</Provider>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
@ -1,251 +0,0 @@
|
|||||||
import {
|
|
||||||
GET_STATUS,
|
|
||||||
USER_LOADED,
|
|
||||||
USER_LOADING,
|
|
||||||
AUTH_ERROR,
|
|
||||||
LOGIN_SUCCESS,
|
|
||||||
LOGIN_FAIL,
|
|
||||||
LOGOUT_SUCCESS,
|
|
||||||
LOGOUT_FAIL,
|
|
||||||
REFRESH_TOKEN_SUCCESS,
|
|
||||||
} from "../actions/types";
|
|
||||||
|
|
||||||
import axios from "axios";
|
|
||||||
import { returnErrors, returnSuccess } from "./messageActions";
|
|
||||||
import { setLanguage } from "./generalActions";
|
|
||||||
|
|
||||||
// Check token & load user
|
|
||||||
export const loadUser = () => (dispatch) => {
|
|
||||||
// user loading
|
|
||||||
dispatch({
|
|
||||||
type: USER_LOADING,
|
|
||||||
});
|
|
||||||
const config = {
|
|
||||||
success: (res) => {
|
|
||||||
dispatch({
|
|
||||||
type: GET_STATUS,
|
|
||||||
payload: res.data.user.status,
|
|
||||||
});
|
|
||||||
dispatch(setLanguage(res.data.user.language));
|
|
||||||
dispatch({
|
|
||||||
type: USER_LOADED,
|
|
||||||
payload: res.data.user,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
error: (err) => {
|
|
||||||
if (err.response) {
|
|
||||||
dispatch(returnErrors(err.response.data.message, err.response.status));
|
|
||||||
}
|
|
||||||
var status = [];
|
|
||||||
if (window.localStorage.getItem("status")) {
|
|
||||||
status = JSON.parse(window.localStorage.getItem("status"));
|
|
||||||
}
|
|
||||||
dispatch({
|
|
||||||
type: GET_STATUS,
|
|
||||||
payload: status,
|
|
||||||
});
|
|
||||||
dispatch({
|
|
||||||
type: AUTH_ERROR,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
axios
|
|
||||||
.get(
|
|
||||||
`${process.env.REACT_APP_BLOCKLY_API}/user`,
|
|
||||||
config,
|
|
||||||
dispatch(authInterceptor())
|
|
||||||
)
|
|
||||||
.then((res) => {
|
|
||||||
res.config.success(res);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
err.config.error(err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Login user
|
|
||||||
export const login =
|
|
||||||
({ email, password }) =>
|
|
||||||
(dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: USER_LOADING,
|
|
||||||
});
|
|
||||||
// Headers
|
|
||||||
const config = {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// Request Body
|
|
||||||
const body = JSON.stringify({ email, password });
|
|
||||||
axios
|
|
||||||
.post(`${process.env.REACT_APP_BLOCKLY_API}/user`, body, config)
|
|
||||||
.then((res) => {
|
|
||||||
dispatch(setLanguage(res.data.user.language));
|
|
||||||
dispatch({
|
|
||||||
type: LOGIN_SUCCESS,
|
|
||||||
payload: res.data,
|
|
||||||
});
|
|
||||||
dispatch({
|
|
||||||
type: GET_STATUS,
|
|
||||||
payload: res.data.user.status,
|
|
||||||
});
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status, "LOGIN_SUCCESS"));
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
dispatch(
|
|
||||||
returnErrors(
|
|
||||||
err.response.data.message,
|
|
||||||
err.response.status,
|
|
||||||
"LOGIN_FAIL"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
dispatch({
|
|
||||||
type: LOGIN_FAIL,
|
|
||||||
});
|
|
||||||
var status = [];
|
|
||||||
if (window.localStorage.getItem("status")) {
|
|
||||||
status = JSON.parse(window.localStorage.getItem("status"));
|
|
||||||
}
|
|
||||||
dispatch({
|
|
||||||
type: GET_STATUS,
|
|
||||||
payload: status,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Logout User
|
|
||||||
export const logout = () => (dispatch) => {
|
|
||||||
const config = {
|
|
||||||
success: (res) => {
|
|
||||||
dispatch({
|
|
||||||
type: LOGOUT_SUCCESS,
|
|
||||||
});
|
|
||||||
var status = [];
|
|
||||||
if (window.localStorage.getItem("status")) {
|
|
||||||
status = JSON.parse(window.localStorage.getItem("status"));
|
|
||||||
}
|
|
||||||
dispatch({
|
|
||||||
type: GET_STATUS,
|
|
||||||
payload: status,
|
|
||||||
});
|
|
||||||
var locale = "en_US";
|
|
||||||
if (window.localStorage.getItem("locale")) {
|
|
||||||
locale = window.localStorage.getItem("locale");
|
|
||||||
} else if (navigator.language === "de-DE") {
|
|
||||||
locale = "de_DE";
|
|
||||||
}
|
|
||||||
dispatch(setLanguage(locale));
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status, "LOGOUT_SUCCESS"));
|
|
||||||
},
|
|
||||||
error: (err) => {
|
|
||||||
dispatch(
|
|
||||||
returnErrors(
|
|
||||||
err.response.data.message,
|
|
||||||
err.response.status,
|
|
||||||
"LOGOUT_FAIL"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
dispatch({
|
|
||||||
type: LOGOUT_FAIL,
|
|
||||||
});
|
|
||||||
var status = [];
|
|
||||||
if (window.localStorage.getItem("status")) {
|
|
||||||
status = JSON.parse(window.localStorage.getItem("status"));
|
|
||||||
}
|
|
||||||
dispatch({
|
|
||||||
type: GET_STATUS,
|
|
||||||
payload: status,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
axios
|
|
||||||
.post("https://api.opensensemap.org/users/sign-out", {}, config)
|
|
||||||
.then((res) => {
|
|
||||||
res.config.success(res);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (err.response && err.response.status !== 401) {
|
|
||||||
err.config.error(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const authInterceptor = () => (dispatch, getState) => {
|
|
||||||
// Add a request interceptor
|
|
||||||
axios.interceptors.request.use(
|
|
||||||
(config) => {
|
|
||||||
config.headers["Content-Type"] = "application/json";
|
|
||||||
const token = getState().auth.token;
|
|
||||||
if (token) {
|
|
||||||
config.headers["Authorization"] = `Bearer ${token}`;
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add a response interceptor
|
|
||||||
axios.interceptors.response.use(
|
|
||||||
(response) => {
|
|
||||||
// request was successfull
|
|
||||||
return response;
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
const originalRequest = error.config;
|
|
||||||
const refreshToken = getState().auth.refreshToken;
|
|
||||||
if (refreshToken) {
|
|
||||||
// try to refresh the token failed
|
|
||||||
if (error.response.status === 401 && originalRequest._retry) {
|
|
||||||
// router.push('/login');
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
// token was not valid and 1st try to refresh the token
|
|
||||||
if (error.response.status === 401 && !originalRequest._retry) {
|
|
||||||
originalRequest._retry = true;
|
|
||||||
const refreshToken = getState().auth.refreshToken;
|
|
||||||
// request to refresh the token, in request-body is the refreshToken
|
|
||||||
axios
|
|
||||||
.post("https://api.opensensemap.org/users/refresh-auth", {
|
|
||||||
token: refreshToken,
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (res.status === 200) {
|
|
||||||
dispatch({
|
|
||||||
type: REFRESH_TOKEN_SUCCESS,
|
|
||||||
payload: res.data,
|
|
||||||
});
|
|
||||||
axios.defaults.headers.common["Authorization"] =
|
|
||||||
"Bearer " + getState().auth.token;
|
|
||||||
// request was successfull, new request with the old parameters and the refreshed token
|
|
||||||
return axios(originalRequest)
|
|
||||||
.then((res) => {
|
|
||||||
originalRequest.success(res);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
originalRequest.error(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Promise.reject(error);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
// request failed, token could not be refreshed
|
|
||||||
if (err.response) {
|
|
||||||
dispatch(
|
|
||||||
returnErrors(err.response.data.message, err.response.status)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dispatch({
|
|
||||||
type: AUTH_ERROR,
|
|
||||||
});
|
|
||||||
return Promise.reject(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// request status was unequal to 401, no possibility to refresh the token
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,15 +0,0 @@
|
|||||||
import {
|
|
||||||
BOARD,
|
|
||||||
} from "./types";
|
|
||||||
import mini_opacity from "../data/mini_opacity.png"
|
|
||||||
import mcu_opacity from "../data/mcu_opacity.png"
|
|
||||||
|
|
||||||
export const setBoard = (board) => (dispatch) => {
|
|
||||||
window.sessionStorage.setItem("board", board);
|
|
||||||
const root = document.querySelector(':root');
|
|
||||||
root.style.setProperty('--url', `url(${board === "mcu" ? mcu_opacity : mini_opacity})`);
|
|
||||||
dispatch({
|
|
||||||
type: BOARD,
|
|
||||||
payload: board,
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,52 +0,0 @@
|
|||||||
import {
|
|
||||||
VISIT,
|
|
||||||
LANGUAGE,
|
|
||||||
RENDERER,
|
|
||||||
SOUNDS,
|
|
||||||
STATISTICS,
|
|
||||||
PLATFORM,
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
export const visitPage = () => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: VISIT,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setPlatform = (platform) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: PLATFORM,
|
|
||||||
payload: platform,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setLanguage = (language) => (dispatch, getState) => {
|
|
||||||
if (!getState().auth.progress && !getState().auth.isAuthenticated) {
|
|
||||||
window.localStorage.setItem("locale", language);
|
|
||||||
}
|
|
||||||
dispatch({
|
|
||||||
type: LANGUAGE,
|
|
||||||
payload: language,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setRenderer = (renderer) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: RENDERER,
|
|
||||||
payload: renderer,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setSounds = (sounds) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: SOUNDS,
|
|
||||||
payload: sounds,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setStatistics = (showStatistics) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: STATISTICS,
|
|
||||||
payload: showStatistics,
|
|
||||||
});
|
|
||||||
};
|
|
@ -1,32 +0,0 @@
|
|||||||
import { GET_ERRORS, CLEAR_MESSAGES, GET_SUCCESS } from './types';
|
|
||||||
|
|
||||||
// RETURN Errors
|
|
||||||
export const returnErrors = (msg, status, id = null) => {
|
|
||||||
return {
|
|
||||||
type: GET_ERRORS,
|
|
||||||
payload: {
|
|
||||||
msg: msg,
|
|
||||||
status: status,
|
|
||||||
id: id
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// RETURN Success
|
|
||||||
export const returnSuccess = (msg, status, id = null) => {
|
|
||||||
return {
|
|
||||||
type: GET_SUCCESS,
|
|
||||||
payload: {
|
|
||||||
msg: msg,
|
|
||||||
status: status,
|
|
||||||
id: id
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// CLEAR_MESSAGES
|
|
||||||
export const clearMessages = () => {
|
|
||||||
return {
|
|
||||||
type: CLEAR_MESSAGES
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,204 +0,0 @@
|
|||||||
import { PROJECT_PROGRESS, GET_PROJECT, GET_PROJECTS, PROJECT_TYPE, PROJECT_DESCRIPTION } from './types';
|
|
||||||
|
|
||||||
import axios from 'axios';
|
|
||||||
import { returnErrors, returnSuccess } from './messageActions';
|
|
||||||
|
|
||||||
export const setType = (type) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: PROJECT_TYPE,
|
|
||||||
payload: type
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setDescription = (description) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: PROJECT_DESCRIPTION,
|
|
||||||
payload: description
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getProject = (type, id) => (dispatch) => {
|
|
||||||
dispatch({ type: PROJECT_PROGRESS });
|
|
||||||
dispatch(setType(type));
|
|
||||||
const config = {
|
|
||||||
success: res => {
|
|
||||||
var data = type === 'share' ? 'content' : type;
|
|
||||||
var project = res.data[data];
|
|
||||||
if (project) {
|
|
||||||
dispatch({
|
|
||||||
type: GET_PROJECT,
|
|
||||||
payload: project
|
|
||||||
});
|
|
||||||
dispatch({
|
|
||||||
type: PROJECT_DESCRIPTION,
|
|
||||||
payload: project.description
|
|
||||||
});
|
|
||||||
dispatch({ type: PROJECT_PROGRESS });
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status, 'GET_PROJECT_SUCCESS'));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
dispatch({ type: PROJECT_PROGRESS });
|
|
||||||
dispatch(returnErrors(res.data.message, res.status, 'PROJECT_EMPTY'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: err => {
|
|
||||||
if (err.response) {
|
|
||||||
dispatch(returnErrors(err.response.data.message, err.response.status, 'GET_PROJECT_FAIL'));
|
|
||||||
}
|
|
||||||
dispatch({ type: PROJECT_PROGRESS });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
axios.get(`${process.env.REACT_APP_BLOCKLY_API}/${type}/${id}`, config)
|
|
||||||
.then(res => {
|
|
||||||
res.config.success(res);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
err.config.error(err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getProjects = (type) => (dispatch) => {
|
|
||||||
dispatch({ type: PROJECT_PROGRESS });
|
|
||||||
const config = {
|
|
||||||
success: res => {
|
|
||||||
var data = type === 'project' ? 'projects' : 'galleries';
|
|
||||||
var projects = res.data[data];
|
|
||||||
dispatch({
|
|
||||||
type: GET_PROJECTS,
|
|
||||||
payload: projects
|
|
||||||
});
|
|
||||||
dispatch({ type: PROJECT_PROGRESS });
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status));
|
|
||||||
},
|
|
||||||
error: err => {
|
|
||||||
if (err.response) {
|
|
||||||
dispatch(returnErrors(err.response.data.message, err.response.status, 'GET_PROJECTS_FAIL'));
|
|
||||||
}
|
|
||||||
dispatch({ type: PROJECT_PROGRESS });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
axios.get(`${process.env.REACT_APP_BLOCKLY_API}/${type}`, config)
|
|
||||||
.then(res => {
|
|
||||||
res.config.success(res);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
err.config.error(err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateProject = (type, id) => (dispatch, getState) => {
|
|
||||||
var workspace = getState().workspace;
|
|
||||||
var body = {
|
|
||||||
xml: workspace.code.xml,
|
|
||||||
title: workspace.name
|
|
||||||
};
|
|
||||||
var project = getState().project;
|
|
||||||
if (type === 'gallery') {
|
|
||||||
body.description = project.description;
|
|
||||||
}
|
|
||||||
const config = {
|
|
||||||
success: res => {
|
|
||||||
var project = res.data[type];
|
|
||||||
var projects = getState().project.projects;
|
|
||||||
var index = projects.findIndex(res => res._id === project._id);
|
|
||||||
projects[index] = project;
|
|
||||||
dispatch({
|
|
||||||
type: GET_PROJECTS,
|
|
||||||
payload: projects
|
|
||||||
});
|
|
||||||
if (type === 'project') {
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status, 'PROJECT_UPDATE_SUCCESS'));
|
|
||||||
} else {
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status, 'GALLERY_UPDATE_SUCCESS'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: err => {
|
|
||||||
if (err.response) {
|
|
||||||
if (type === 'project') {
|
|
||||||
dispatch(returnErrors(err.response.data.message, err.response.status, 'PROJECT_UPDATE_FAIL'));
|
|
||||||
} else {
|
|
||||||
dispatch(returnErrors(err.response.data.message, err.response.status, 'GALLERY_UPDATE_FAIL'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
axios.put(`${process.env.REACT_APP_BLOCKLY_API}/${type}/${id}`, body, config)
|
|
||||||
.then(res => {
|
|
||||||
res.config.success(res);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
err.config.error(err);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteProject = (type, id) => (dispatch, getState) => {
|
|
||||||
const config = {
|
|
||||||
success: res => {
|
|
||||||
var projects = getState().project.projects;
|
|
||||||
var index = projects.findIndex(res => res._id === id);
|
|
||||||
projects.splice(index, 1)
|
|
||||||
dispatch({
|
|
||||||
type: GET_PROJECTS,
|
|
||||||
payload: projects
|
|
||||||
});
|
|
||||||
if (type === 'project') {
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status, 'PROJECT_DELETE_SUCCESS'));
|
|
||||||
} else {
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status, 'GALLERY_DELETE_SUCCESS'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: err => {
|
|
||||||
dispatch(returnErrors(err.response.data.message, err.response.status, 'PROJECT_DELETE_FAIL'));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
axios.delete(`${process.env.REACT_APP_BLOCKLY_API}/${type}/${id}`, config)
|
|
||||||
.then(res => {
|
|
||||||
res.config.success(res);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
if (err.response && err.response.status !== 401) {
|
|
||||||
err.config.error(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export const shareProject = (title, type, id) => (dispatch, getState) => {
|
|
||||||
var body = {
|
|
||||||
title: title
|
|
||||||
};
|
|
||||||
if (type === 'project') {
|
|
||||||
body.projectId = id;
|
|
||||||
} else {
|
|
||||||
body.xml = getState().workspace.code.xml;
|
|
||||||
}
|
|
||||||
axios.post(`${process.env.REACT_APP_BLOCKLY_API}/share`, body)
|
|
||||||
.then(res => {
|
|
||||||
var shareContent = res.data.content;
|
|
||||||
if (body.projectId) {
|
|
||||||
var projects = getState().project.projects;
|
|
||||||
var index = projects.findIndex(res => res._id === id);
|
|
||||||
projects[index].shared = shareContent.expiresAt;
|
|
||||||
dispatch({
|
|
||||||
type: GET_PROJECTS,
|
|
||||||
payload: projects
|
|
||||||
});
|
|
||||||
}
|
|
||||||
dispatch(returnSuccess(res.data.message, shareContent._id, 'SHARE_SUCCESS'));
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
if (err.response) {
|
|
||||||
dispatch(returnErrors(err.response.data.message, err.response.status, 'SHARE_FAIL'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export const resetProject = () => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: GET_PROJECTS,
|
|
||||||
payload: []
|
|
||||||
});
|
|
||||||
dispatch(setType(''));
|
|
||||||
dispatch(setDescription(''));
|
|
||||||
};
|
|
@ -1,18 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
const fetchSensorWikiSuccess = sensors => ({
|
|
||||||
type: 'FETCH_SENSORWIKI_SUCCESS',
|
|
||||||
payload: { sensors }
|
|
||||||
})
|
|
||||||
|
|
||||||
export const fetchSensors = () => {
|
|
||||||
return async dispatch => {
|
|
||||||
try {
|
|
||||||
let sensors = await axios.get('https://api.sensors.wiki/sensors/all')
|
|
||||||
dispatch(fetchSensorWikiSuccess(sensors.data))
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,345 +0,0 @@
|
|||||||
import {
|
|
||||||
TUTORIAL_PROGRESS,
|
|
||||||
GET_TUTORIAL,
|
|
||||||
GET_TUTORIALS,
|
|
||||||
TUTORIAL_SUCCESS,
|
|
||||||
TUTORIAL_ERROR,
|
|
||||||
TUTORIAL_CHANGE,
|
|
||||||
TUTORIAL_XML,
|
|
||||||
TUTORIAL_STEP,
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
import axios from "axios";
|
|
||||||
import { returnErrors, returnSuccess } from "./messageActions";
|
|
||||||
|
|
||||||
export const tutorialProgress = () => (dispatch) => {
|
|
||||||
dispatch({ type: TUTORIAL_PROGRESS });
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getTutorial = (id) => (dispatch, getState) => {
|
|
||||||
axios
|
|
||||||
.get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/${id}`)
|
|
||||||
.then((res) => {
|
|
||||||
var tutorial = res.data.tutorial;
|
|
||||||
existingTutorial(tutorial, getState().tutorial.status).then((status) => {
|
|
||||||
dispatch({
|
|
||||||
type: TUTORIAL_SUCCESS,
|
|
||||||
payload: status,
|
|
||||||
});
|
|
||||||
dispatch(updateStatus(status));
|
|
||||||
dispatch({
|
|
||||||
type: GET_TUTORIAL,
|
|
||||||
payload: tutorial,
|
|
||||||
});
|
|
||||||
dispatch({ type: TUTORIAL_PROGRESS });
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status));
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (err.response) {
|
|
||||||
dispatch(
|
|
||||||
returnErrors(
|
|
||||||
err.response.data.message,
|
|
||||||
err.response.status,
|
|
||||||
"GET_TUTORIAL_FAIL"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dispatch({ type: TUTORIAL_PROGRESS });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getTutorials = () => (dispatch, getState) => {
|
|
||||||
axios
|
|
||||||
.get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial`)
|
|
||||||
.then((res) => {
|
|
||||||
var tutorials = res.data.tutorials;
|
|
||||||
existingTutorials(tutorials, getState().tutorial.status).then(
|
|
||||||
(status) => {
|
|
||||||
dispatch({
|
|
||||||
type: TUTORIAL_SUCCESS,
|
|
||||||
payload: status,
|
|
||||||
});
|
|
||||||
dispatch(updateStatus(status));
|
|
||||||
dispatch({
|
|
||||||
type: GET_TUTORIALS,
|
|
||||||
payload: tutorials,
|
|
||||||
});
|
|
||||||
dispatch({ type: TUTORIAL_PROGRESS });
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (err.response) {
|
|
||||||
dispatch(
|
|
||||||
returnErrors(
|
|
||||||
err.response.data.message,
|
|
||||||
err.response.status,
|
|
||||||
"GET_TUTORIALS_FAIL"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dispatch({ type: TUTORIAL_PROGRESS });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getAllTutorials = () => (dispatch, getState) => {
|
|
||||||
axios
|
|
||||||
.get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/getAllTutorials`)
|
|
||||||
.then((res) => {
|
|
||||||
var tutorials = res.data.tutorials;
|
|
||||||
existingTutorials(tutorials, getState().tutorial.status).then(
|
|
||||||
(status) => {
|
|
||||||
dispatch({
|
|
||||||
type: TUTORIAL_SUCCESS,
|
|
||||||
payload: status,
|
|
||||||
});
|
|
||||||
dispatch(updateStatus(status));
|
|
||||||
dispatch({
|
|
||||||
type: GET_TUTORIALS,
|
|
||||||
payload: tutorials,
|
|
||||||
});
|
|
||||||
dispatch({ type: TUTORIAL_PROGRESS });
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (err.response) {
|
|
||||||
dispatch(
|
|
||||||
returnErrors(
|
|
||||||
err.response.data.message,
|
|
||||||
err.response.status,
|
|
||||||
"GET_TUTORIALS_FAIL"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dispatch({ type: TUTORIAL_PROGRESS });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getUserTutorials = () => (dispatch, getState) => {
|
|
||||||
axios
|
|
||||||
.get(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/getUserTutorials`)
|
|
||||||
.then((res) => {
|
|
||||||
var tutorials = res.data.tutorials;
|
|
||||||
existingTutorials(tutorials, getState().tutorial.status).then(
|
|
||||||
(status) => {
|
|
||||||
dispatch({
|
|
||||||
type: TUTORIAL_SUCCESS,
|
|
||||||
payload: status,
|
|
||||||
});
|
|
||||||
dispatch(updateStatus(status));
|
|
||||||
dispatch({
|
|
||||||
type: GET_TUTORIALS,
|
|
||||||
payload: tutorials,
|
|
||||||
});
|
|
||||||
dispatch({ type: TUTORIAL_PROGRESS });
|
|
||||||
dispatch(returnSuccess(res.data.message, res.status));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log(err);
|
|
||||||
if (err.response) {
|
|
||||||
dispatch(
|
|
||||||
returnErrors(
|
|
||||||
err.response.data.message,
|
|
||||||
err.response.status,
|
|
||||||
"GET_TUTORIALS_FAIL"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dispatch({ type: TUTORIAL_PROGRESS });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const updateStatus = (status) => (dispatch, getState) => {
|
|
||||||
if (getState().auth.isAuthenticated) {
|
|
||||||
// update user account in database - sync with redux store
|
|
||||||
axios
|
|
||||||
.put(`${process.env.REACT_APP_BLOCKLY_API}/user/status`, {
|
|
||||||
status: status,
|
|
||||||
})
|
|
||||||
.then((res) => {})
|
|
||||||
.catch((err) => {
|
|
||||||
if (err.response) {
|
|
||||||
// dispatch(returnErrors(err.response.data.message, err.response.status, 'UPDATE_STATUS_FAIL'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// update locale storage - sync with redux store
|
|
||||||
window.localStorage.setItem("status", JSON.stringify(status));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteTutorial = (id) => (dispatch, getState) => {
|
|
||||||
var tutorial = getState().tutorial;
|
|
||||||
var id = getState().builder.id;
|
|
||||||
const config = {
|
|
||||||
success: (res) => {
|
|
||||||
var tutorials = tutorial.tutorials;
|
|
||||||
var index = tutorials.findIndex((res) => res._id === id);
|
|
||||||
tutorials.splice(index, 1);
|
|
||||||
dispatch({
|
|
||||||
type: GET_TUTORIALS,
|
|
||||||
payload: tutorials,
|
|
||||||
});
|
|
||||||
dispatch(
|
|
||||||
returnSuccess(res.data.message, res.status, "TUTORIAL_DELETE_SUCCESS")
|
|
||||||
);
|
|
||||||
},
|
|
||||||
error: (err) => {
|
|
||||||
dispatch(
|
|
||||||
returnErrors(
|
|
||||||
err.response.data.message,
|
|
||||||
err.response.status,
|
|
||||||
"TUTORIAL_DELETE_FAIL"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
axios
|
|
||||||
.delete(`${process.env.REACT_APP_BLOCKLY_API}/tutorial/${id}`, config)
|
|
||||||
.then((res) => {
|
|
||||||
res.config.success(res);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (err.response && err.response.status !== 401) {
|
|
||||||
err.config.error(err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const resetTutorial = () => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: GET_TUTORIALS,
|
|
||||||
payload: [],
|
|
||||||
});
|
|
||||||
dispatch({
|
|
||||||
type: TUTORIAL_STEP,
|
|
||||||
payload: 0,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tutorialChange = () => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: TUTORIAL_CHANGE,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tutorialCheck = (status, step) => (dispatch, getState) => {
|
|
||||||
var tutorialsStatus = getState().tutorial.status;
|
|
||||||
var id = getState().tutorial.tutorials[0]._id;
|
|
||||||
var tutorialsStatusIndex = tutorialsStatus.findIndex(
|
|
||||||
(tutorialStatus) => tutorialStatus._id === id
|
|
||||||
);
|
|
||||||
var tasksIndex = tutorialsStatus[tutorialsStatusIndex].tasks.findIndex(
|
|
||||||
(task) => task._id === step._id
|
|
||||||
);
|
|
||||||
tutorialsStatus[tutorialsStatusIndex].tasks[tasksIndex] = {
|
|
||||||
...tutorialsStatus[tutorialsStatusIndex].tasks[tasksIndex],
|
|
||||||
type: status,
|
|
||||||
};
|
|
||||||
dispatch({
|
|
||||||
type: status === "success" ? TUTORIAL_SUCCESS : TUTORIAL_ERROR,
|
|
||||||
payload: tutorialsStatus,
|
|
||||||
});
|
|
||||||
dispatch(updateStatus(tutorialsStatus));
|
|
||||||
dispatch(tutorialChange());
|
|
||||||
dispatch(returnSuccess("", "", "TUTORIAL_CHECK_SUCCESS"));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const storeTutorialXml = (code) => (dispatch, getState) => {
|
|
||||||
var tutorial = getState().tutorial.tutorials[0];
|
|
||||||
if (tutorial) {
|
|
||||||
var id = tutorial._id;
|
|
||||||
var activeStep = getState().tutorial.activeStep;
|
|
||||||
var steps = tutorial.steps;
|
|
||||||
if (steps && steps[activeStep].type === "task") {
|
|
||||||
var tutorialsStatus = getState().tutorial.status;
|
|
||||||
var tutorialsStatusIndex = tutorialsStatus.findIndex(
|
|
||||||
(tutorialStatus) => tutorialStatus._id === id
|
|
||||||
);
|
|
||||||
var tasksIndex = tutorialsStatus[tutorialsStatusIndex].tasks.findIndex(
|
|
||||||
(task) => task._id === steps[activeStep]._id
|
|
||||||
);
|
|
||||||
tutorialsStatus[tutorialsStatusIndex].tasks[tasksIndex] = {
|
|
||||||
...tutorialsStatus[tutorialsStatusIndex].tasks[tasksIndex],
|
|
||||||
xml: code,
|
|
||||||
};
|
|
||||||
dispatch({
|
|
||||||
type: TUTORIAL_XML,
|
|
||||||
payload: tutorialsStatus,
|
|
||||||
});
|
|
||||||
dispatch(updateStatus(tutorialsStatus));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tutorialStep = (step) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: TUTORIAL_STEP,
|
|
||||||
payload: step,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const existingTutorials = (tutorials, status) =>
|
|
||||||
new Promise(function (resolve, reject) {
|
|
||||||
var newstatus;
|
|
||||||
new Promise(function (resolve, reject) {
|
|
||||||
var existingTutorialIds = tutorials.map((tutorial, i) => {
|
|
||||||
existingTutorial(tutorial, status).then((status) => {
|
|
||||||
newstatus = status;
|
|
||||||
});
|
|
||||||
return tutorial._id;
|
|
||||||
});
|
|
||||||
resolve(existingTutorialIds);
|
|
||||||
}).then((existingTutorialIds) => {
|
|
||||||
// deleting old tutorials which do not longer exist
|
|
||||||
if (existingTutorialIds.length > 0) {
|
|
||||||
status = newstatus.filter(
|
|
||||||
(status) => existingTutorialIds.indexOf(status._id) > -1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
resolve(status);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const existingTutorial = (tutorial, status) =>
|
|
||||||
new Promise(function (resolve, reject) {
|
|
||||||
var tutorialsId = tutorial._id;
|
|
||||||
var statusIndex = status.findIndex((status) => status._id === tutorialsId);
|
|
||||||
if (statusIndex > -1) {
|
|
||||||
var tasks = tutorial.steps.filter((step) => step.type === "task");
|
|
||||||
var existingTaskIds = tasks.map((task, j) => {
|
|
||||||
var tasksId = task._id;
|
|
||||||
if (
|
|
||||||
status[statusIndex].tasks.findIndex(
|
|
||||||
(task) => task._id === tasksId
|
|
||||||
) === -1
|
|
||||||
) {
|
|
||||||
// task does not exist
|
|
||||||
status[statusIndex].tasks.push({ _id: tasksId });
|
|
||||||
}
|
|
||||||
return tasksId;
|
|
||||||
});
|
|
||||||
// deleting old tasks which do not longer exist
|
|
||||||
if (existingTaskIds.length > 0) {
|
|
||||||
status[statusIndex].tasks = status[statusIndex].tasks.filter(
|
|
||||||
(task) => existingTaskIds.indexOf(task._id) > -1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
status.push({
|
|
||||||
_id: tutorialsId,
|
|
||||||
tasks: tutorial.steps
|
|
||||||
.filter((step) => step.type === "task")
|
|
||||||
.map((task) => {
|
|
||||||
return { _id: task._id };
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
resolve(status);
|
|
||||||
});
|
|
@ -1,354 +0,0 @@
|
|||||||
import {
|
|
||||||
PROGRESS,
|
|
||||||
JSON_STRING,
|
|
||||||
BUILDER_CHANGE,
|
|
||||||
BUILDER_ERROR,
|
|
||||||
BUILDER_TITLE,
|
|
||||||
BUILDER_PUBLIC,
|
|
||||||
BUILDER_DIFFICULTY,
|
|
||||||
BUILDER_REVIEW,
|
|
||||||
BUILDER_ID,
|
|
||||||
BUILDER_ADD_STEP,
|
|
||||||
BUILDER_DELETE_STEP,
|
|
||||||
BUILDER_CHANGE_STEP,
|
|
||||||
BUILDER_CHANGE_ORDER,
|
|
||||||
BUILDER_DELETE_PROPERTY,
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
import data from "../data/hardware.json";
|
|
||||||
|
|
||||||
export const changeTutorialBuilder = () => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_CHANGE,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const jsonString = (json) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: JSON_STRING,
|
|
||||||
payload: json,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tutorialTitle = (title) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_TITLE,
|
|
||||||
payload: title,
|
|
||||||
});
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tutorialPublic = (pub) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_PUBLIC,
|
|
||||||
payload: pub,
|
|
||||||
});
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tutorialDifficulty = (difficulty) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_DIFFICULTY,
|
|
||||||
payload: difficulty,
|
|
||||||
});
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tutorialReview = (review) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_REVIEW,
|
|
||||||
payload: review,
|
|
||||||
});
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tutorialSteps = (steps) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_ADD_STEP,
|
|
||||||
payload: steps,
|
|
||||||
});
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const tutorialId = (id) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_ID,
|
|
||||||
payload: id,
|
|
||||||
});
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addStep = (index) => (dispatch, getState) => {
|
|
||||||
var steps = getState().builder.steps;
|
|
||||||
var step = {
|
|
||||||
id: index + 1,
|
|
||||||
type: "instruction",
|
|
||||||
headline: "",
|
|
||||||
text: "",
|
|
||||||
};
|
|
||||||
steps.splice(index, 0, step);
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_ADD_STEP,
|
|
||||||
payload: steps,
|
|
||||||
});
|
|
||||||
dispatch(addErrorStep(index));
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addErrorStep = (index) => (dispatch, getState) => {
|
|
||||||
var error = getState().builder.error;
|
|
||||||
error.steps.splice(index, 0, {});
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_ERROR,
|
|
||||||
payload: error,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeStep = (index) => (dispatch, getState) => {
|
|
||||||
var steps = getState().builder.steps;
|
|
||||||
steps.splice(index, 1);
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_DELETE_STEP,
|
|
||||||
payload: steps,
|
|
||||||
});
|
|
||||||
dispatch(removeErrorStep(index));
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const removeErrorStep = (index) => (dispatch, getState) => {
|
|
||||||
var error = getState().builder.error;
|
|
||||||
error.steps.splice(index, 1);
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_ERROR,
|
|
||||||
payload: error,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const changeContent =
|
|
||||||
(content, index, property1, property2) => (dispatch, getState) => {
|
|
||||||
var steps = getState().builder.steps;
|
|
||||||
var step = steps[index];
|
|
||||||
if (property2) {
|
|
||||||
if (step[property1] && step[property1][property2]) {
|
|
||||||
step[property1][property2] = content;
|
|
||||||
} else {
|
|
||||||
step[property1] = { [property2]: content };
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
step[property1] = content;
|
|
||||||
}
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_CHANGE_STEP,
|
|
||||||
payload: steps,
|
|
||||||
});
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteProperty =
|
|
||||||
(index, property1, property2) => (dispatch, getState) => {
|
|
||||||
var steps = getState().builder.steps;
|
|
||||||
var step = steps[index];
|
|
||||||
if (property2) {
|
|
||||||
if (step[property1] && step[property1][property2]) {
|
|
||||||
delete step[property1][property2];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete step[property1];
|
|
||||||
}
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_DELETE_PROPERTY,
|
|
||||||
payload: steps,
|
|
||||||
});
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const changeStepIndex = (fromIndex, toIndex) => (dispatch, getState) => {
|
|
||||||
var steps = getState().builder.steps;
|
|
||||||
var step = steps[fromIndex];
|
|
||||||
steps.splice(fromIndex, 1);
|
|
||||||
steps.splice(toIndex, 0, step);
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_CHANGE_ORDER,
|
|
||||||
payload: steps,
|
|
||||||
});
|
|
||||||
dispatch(changeErrorStepIndex(fromIndex, toIndex));
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const changeErrorStepIndex =
|
|
||||||
(fromIndex, toIndex) => (dispatch, getState) => {
|
|
||||||
var error = getState().builder.error;
|
|
||||||
var errorStep = error.steps[fromIndex];
|
|
||||||
error.steps.splice(fromIndex, 1);
|
|
||||||
error.steps.splice(toIndex, 0, errorStep);
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_ERROR,
|
|
||||||
payload: error,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setError = (index, property) => (dispatch, getState) => {
|
|
||||||
var error = getState().builder.error;
|
|
||||||
if (index !== undefined) {
|
|
||||||
error.steps[index][property] = true;
|
|
||||||
} else {
|
|
||||||
error[property] = true;
|
|
||||||
}
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_ERROR,
|
|
||||||
payload: error,
|
|
||||||
});
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deleteError = (index, property) => (dispatch, getState) => {
|
|
||||||
var error = getState().builder.error;
|
|
||||||
if (index !== undefined) {
|
|
||||||
delete error.steps[index][property];
|
|
||||||
} else {
|
|
||||||
delete error[property];
|
|
||||||
}
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_ERROR,
|
|
||||||
payload: error,
|
|
||||||
});
|
|
||||||
dispatch(changeTutorialBuilder());
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setSubmitError = () => (dispatch, getState) => {
|
|
||||||
var builder = getState().builder;
|
|
||||||
// if(builder.id === undefined || builder.id === ''){
|
|
||||||
// dispatch(setError(undefined, 'id'));
|
|
||||||
// }
|
|
||||||
if (builder.title === "") {
|
|
||||||
dispatch(setError(undefined, "title"));
|
|
||||||
}
|
|
||||||
if (builder.title === null) {
|
|
||||||
dispatch(setError(undefined, "title"));
|
|
||||||
}
|
|
||||||
var type = builder.steps.map((step, i) => {
|
|
||||||
// media and xml are directly checked for errors in their components and
|
|
||||||
// therefore do not have to be checked again
|
|
||||||
step.id = i + 1;
|
|
||||||
if (i === 0) {
|
|
||||||
if (step.requirements && step.requirements.length > 0) {
|
|
||||||
var requirements = step.requirements.filter((requirement) =>
|
|
||||||
/^[0-9a-fA-F]{24}$/.test(requirement)
|
|
||||||
);
|
|
||||||
if (requirements.length < step.requirements.length) {
|
|
||||||
dispatch(changeContent(requirements, i, "requirements"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (step.hardware === undefined || step.hardware.length < 1) {
|
|
||||||
dispatch(setError(i, "hardware"));
|
|
||||||
} else {
|
|
||||||
var hardwareIds = data.map((hardware) => hardware.id);
|
|
||||||
var hardware = step.hardware.filter((hardware) =>
|
|
||||||
hardwareIds.includes(hardware)
|
|
||||||
);
|
|
||||||
if (hardware.length < step.hardware.length) {
|
|
||||||
dispatch(changeContent(hardware, i, "hardware"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (step.headline === undefined || step.headline === "") {
|
|
||||||
dispatch(setError(i, "headline"));
|
|
||||||
}
|
|
||||||
if (step.text === undefined || step.text === "") {
|
|
||||||
dispatch(setError(i, "text"));
|
|
||||||
}
|
|
||||||
return step.type;
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
type.filter((item) => item === "task").length > 0 &&
|
|
||||||
type.filter((item) => item === "instruction").length > 0
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
dispatch(setError(undefined, "type"));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const checkError = () => (dispatch, getState) => {
|
|
||||||
dispatch(setSubmitError());
|
|
||||||
var error = getState().builder.error;
|
|
||||||
if (error.id || error.title || error.type) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < error.steps.length; i++) {
|
|
||||||
if (Object.keys(error.steps[i]).length > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const progress = (inProgress) => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: PROGRESS,
|
|
||||||
payload: inProgress,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const resetTutorial = () => (dispatch, getState) => {
|
|
||||||
dispatch(jsonString(""));
|
|
||||||
dispatch(tutorialTitle(""));
|
|
||||||
var steps = [
|
|
||||||
{
|
|
||||||
type: "instruction",
|
|
||||||
headline: "",
|
|
||||||
text: "",
|
|
||||||
hardware: [],
|
|
||||||
requirements: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
dispatch(tutorialSteps(steps));
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_ERROR,
|
|
||||||
payload: {
|
|
||||||
steps: [{}],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const readJSON = (json) => (dispatch, getState) => {
|
|
||||||
dispatch(resetTutorial());
|
|
||||||
dispatch({
|
|
||||||
type: BUILDER_ERROR,
|
|
||||||
payload: {
|
|
||||||
steps: json.steps.map(() => {
|
|
||||||
return {};
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
// accept only valid attributes
|
|
||||||
var steps = json.steps.map((step, i) => {
|
|
||||||
var object = {
|
|
||||||
_id: step._id,
|
|
||||||
type: step.type,
|
|
||||||
headline: step.headline,
|
|
||||||
text: step.text,
|
|
||||||
};
|
|
||||||
if (i === 0) {
|
|
||||||
object.hardware = step.hardware;
|
|
||||||
object.requirements = step.requirements;
|
|
||||||
}
|
|
||||||
if (step.xml) {
|
|
||||||
object.xml = step.xml;
|
|
||||||
}
|
|
||||||
if (step.media && step.type === "instruction") {
|
|
||||||
object.media = {};
|
|
||||||
if (step.media.picture) {
|
|
||||||
object.media.picture = step.media.picture;
|
|
||||||
} else if (step.media.youtube) {
|
|
||||||
object.media.youtube = step.media.youtube;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return object;
|
|
||||||
});
|
|
||||||
dispatch(tutorialTitle(json.title));
|
|
||||||
dispatch(tutorialDifficulty(json.difficulty));
|
|
||||||
dispatch(tutorialSteps(steps));
|
|
||||||
dispatch(setSubmitError());
|
|
||||||
dispatch(progress(false));
|
|
||||||
};
|
|
@ -1,68 +1,5 @@
|
|||||||
// authentication
|
export const NEW_WORKSPACE = 'NEW_WORKSPACE';
|
||||||
export const USER_LOADING = "USER_LOADING";
|
export const CREATE_BLOCK = 'CREATE_BLOCK';
|
||||||
export const USER_LOADED = "USER_LOADED";
|
export const CHANGE_BLOCK = 'CHANGE_BLOCK';
|
||||||
export const AUTH_ERROR = "AUTH_ERROR";
|
export const DELETE_BLOCK = 'DELETE_BLOCK';
|
||||||
export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
|
export const CLEAR_STATS = 'CLEAR_STATS';
|
||||||
export const LOGIN_FAIL = "LOGIN_FAIL";
|
|
||||||
export const LOGOUT_SUCCESS = "LOGOUT_SUCCESS";
|
|
||||||
export const LOGOUT_FAIL = "LOGOUT_FAIL";
|
|
||||||
export const REFRESH_TOKEN_FAIL = "REFRESH_TOKEN_FAIL";
|
|
||||||
export const REFRESH_TOKEN_SUCCESS = "REFRESH_TOKEN_SUCCESS";
|
|
||||||
|
|
||||||
export const NEW_CODE = "NEW_CODE";
|
|
||||||
export const CHANGE_WORKSPACE = "CHANGE_WORKSPACE";
|
|
||||||
export const CREATE_BLOCK = "CREATE_BLOCK";
|
|
||||||
export const MOVE_BLOCK = "MOVE_BLOCK";
|
|
||||||
export const CHANGE_BLOCK = "CHANGE_BLOCK";
|
|
||||||
export const DELETE_BLOCK = "DELETE_BLOCK";
|
|
||||||
export const CLEAR_STATS = "CLEAR_STATS";
|
|
||||||
export const NAME = "NAME";
|
|
||||||
|
|
||||||
export const TUTORIAL_PROGRESS = "TUTORIAL_PROGRESS";
|
|
||||||
export const GET_TUTORIAL = "GET_TUTORIAL";
|
|
||||||
export const GET_TUTORIALS = "GET_TUTORIALS";
|
|
||||||
export const GET_USERTUTORIALS = "GET_USERTUTORIALS";
|
|
||||||
export const GET_STATUS = "GET_STATUS";
|
|
||||||
export const TUTORIAL_SUCCESS = "TUTORIAL_SUCCESS";
|
|
||||||
export const TUTORIAL_ERROR = "TUTORIAL_ERROR";
|
|
||||||
export const TUTORIAL_CHANGE = "TUTORIAL_CHANGE";
|
|
||||||
export const TUTORIAL_XML = "TUTORIAL_XML";
|
|
||||||
export const TUTORIAL_ID = "TUTORIAL_ID";
|
|
||||||
export const TUTORIAL_STEP = "TUTORIAL_STEP";
|
|
||||||
export const JSON_STRING = "JSON_STRING";
|
|
||||||
|
|
||||||
export const BUILDER_CHANGE = "BUILDER_CHANGE";
|
|
||||||
export const BUILDER_TITLE = "BUILDER_TITLE";
|
|
||||||
export const BUILDER_DIFFICULTY = "BUILDER_DIFFICULTY";
|
|
||||||
export const BUILDER_PUBLIC = "BUILDER_PUBLIC";
|
|
||||||
export const BUILDER_REVIEW = "BUILDER_REVIEW";
|
|
||||||
export const BUILDER_ID = "BUILDER_ID";
|
|
||||||
export const BUILDER_ADD_STEP = "BUILDER_ADD_STEP";
|
|
||||||
export const BUILDER_DELETE_STEP = "BUILDER_DELETE_STEP";
|
|
||||||
export const BUILDER_CHANGE_STEP = "BUILDER_CHANGE_STEP";
|
|
||||||
export const BUILDER_CHANGE_ORDER = "BUILDER_CHANGE_ORDER";
|
|
||||||
export const BUILDER_DELETE_PROPERTY = "BUILDER_DELETE_PROPERTY";
|
|
||||||
export const BUILDER_ERROR = "BUILDER_ERROR";
|
|
||||||
export const PROGRESS = "PROGRESS";
|
|
||||||
|
|
||||||
export const VISIT = "VISIT";
|
|
||||||
export const LANGUAGE = "LANGUAGE";
|
|
||||||
export const PLATFORM = "PLATFORM";
|
|
||||||
export const RENDERER = "RENDERER";
|
|
||||||
export const SOUNDS = "SOUNDS";
|
|
||||||
export const STATISTICS = "STATISTICS";
|
|
||||||
|
|
||||||
// messages
|
|
||||||
export const GET_ERRORS = "GET_ERRORS";
|
|
||||||
export const GET_SUCCESS = "GET_SUCCESS";
|
|
||||||
export const CLEAR_MESSAGES = "CLEAR_MESSAGES";
|
|
||||||
|
|
||||||
// projects: share, gallery, project
|
|
||||||
export const PROJECT_PROGRESS = "PROJECT_PROGRESS";
|
|
||||||
export const GET_PROJECT = "GET_PROJECT";
|
|
||||||
export const GET_PROJECTS = "GET_PROJECTS";
|
|
||||||
export const PROJECT_TYPE = "PROJECT_TYPE";
|
|
||||||
export const PROJECT_DESCRIPTION = "PROJECT_DESCRIPTION";
|
|
||||||
|
|
||||||
//board
|
|
||||||
export const BOARD = "BOARD";
|
|
||||||
|
@ -1,87 +1,43 @@
|
|||||||
import { NEW_CODE, CHANGE_WORKSPACE, CREATE_BLOCK, MOVE_BLOCK, CHANGE_BLOCK, DELETE_BLOCK, CLEAR_STATS, NAME } from './types';
|
import { NEW_WORKSPACE, CREATE_BLOCK, CHANGE_BLOCK, DELETE_BLOCK, CLEAR_STATS } from './types';
|
||||||
|
|
||||||
import * as Blockly from 'blockly/core';
|
|
||||||
|
|
||||||
import { storeTutorialXml } from './tutorialActions';
|
|
||||||
|
|
||||||
export const workspaceChange = () => (dispatch) => {
|
|
||||||
dispatch({
|
|
||||||
type: CHANGE_WORKSPACE
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const onChangeCode = () => (dispatch, getState) => {
|
|
||||||
const workspace = Blockly.getMainWorkspace();
|
|
||||||
var code = getState().workspace.code;
|
|
||||||
code.arduino = Blockly.Arduino.workspaceToCode(workspace);
|
|
||||||
var xmlDom = Blockly.Xml.workspaceToDom(workspace);
|
|
||||||
code.xml = Blockly.Xml.domToPrettyText(xmlDom);
|
|
||||||
var selectedBlock = Blockly.getSelected();
|
|
||||||
if (selectedBlock !== null) {
|
|
||||||
code.helpurl = selectedBlock.helpUrl
|
|
||||||
code.tooltip = selectedBlock.tooltip
|
|
||||||
if (selectedBlock.data) {
|
|
||||||
code.data = selectedBlock.data
|
|
||||||
} else {
|
|
||||||
code.data = null
|
|
||||||
}
|
|
||||||
} else if (selectedBlock === null) {
|
|
||||||
code.tooltip = Blockly.Msg.tooltip_hint
|
|
||||||
code.helpurl = ''
|
|
||||||
code.data = null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: NEW_CODE,
|
|
||||||
payload: code
|
|
||||||
});
|
|
||||||
return code;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const onChangeWorkspace = (event) => (dispatch, getState) => {
|
export const onChangeWorkspace = (event) => (dispatch, getState) => {
|
||||||
dispatch(workspaceChange());
|
var oldWorkspace = getState().workspace.new; // stored 'new workspace' is from now on old
|
||||||
var code = dispatch(onChangeCode());
|
var newWorkspace = window.Ardublockly.workspace;
|
||||||
dispatch(storeTutorialXml(code.xml));
|
|
||||||
var stats = getState().workspace.stats;
|
|
||||||
if (event.type === Blockly.Events.BLOCK_CREATE) {
|
|
||||||
stats.create += event.ids.length;
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CREATE_BLOCK,
|
type: NEW_WORKSPACE,
|
||||||
payload: stats
|
payload: {new: newWorkspace, old: oldWorkspace}
|
||||||
});
|
});
|
||||||
}
|
var stats = getState().workspace.stats;
|
||||||
else if (event.type === Blockly.Events.BLOCK_MOVE) {
|
if (event.type === window.Blockly.Events.CREATE){
|
||||||
stats.move += 1;
|
stats.create += event.ids.length;
|
||||||
dispatch({
|
|
||||||
type: MOVE_BLOCK,
|
|
||||||
payload: stats
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (event.type === Blockly.Events.BLOCK_CHANGE) {
|
|
||||||
stats.change += 1;
|
|
||||||
dispatch({
|
|
||||||
type: CHANGE_BLOCK,
|
|
||||||
payload: stats
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (event.type === Blockly.Events.BLOCK_DELETE) {
|
|
||||||
if (stats.create > 0) {
|
|
||||||
stats.delete += event.ids.length;
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: DELETE_BLOCK,
|
type: CREATE_BLOCK,
|
||||||
payload: stats
|
payload: stats
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
else if (event.type === window.Blockly.Events.CHANGE){
|
||||||
|
stats.change += 1;
|
||||||
|
dispatch({
|
||||||
|
type: CHANGE_BLOCK,
|
||||||
|
payload: stats
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (event.type === window.Blockly.Events.DELETE){
|
||||||
|
if(stats.create > 0){
|
||||||
|
stats.delete += event.ids.length;
|
||||||
|
dispatch({
|
||||||
|
type: DELETE_BLOCK,
|
||||||
|
payload: stats
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearStats = () => (dispatch) => {
|
export const clearStats = () => (dispatch) => {
|
||||||
var stats = {
|
var stats = {
|
||||||
create: -1, // initialXML is created automatically, Block is not part of the statistics
|
create: 0,
|
||||||
change: 0,
|
change: 0,
|
||||||
delete: 0,
|
delete: 0
|
||||||
move: -1 // initialXML is moved automatically, Block is not part of the statistics
|
|
||||||
};
|
};
|
||||||
dispatch({
|
dispatch({
|
||||||
type: CLEAR_STATS,
|
type: CLEAR_STATS,
|
||||||
@ -89,9 +45,9 @@ export const clearStats = () => (dispatch) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const workspaceName = (name) => (dispatch) => {
|
export const setWorkspace = (workspace) => (dispatch, getState) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: NAME,
|
type: NEW_WORKSPACE,
|
||||||
payload: name
|
payload: {new: workspace, old: getState().workspace.new}
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
|
|
||||||
import withStyles from '@mui/styles/withStyles';
|
|
||||||
import { alpha } from "@mui/material/styles";
|
|
||||||
|
|
||||||
import Typography from "@mui/material/Typography";
|
|
||||||
|
|
||||||
const styles = (theme) => ({
|
|
||||||
alert: {
|
|
||||||
marginBottom: "20px",
|
|
||||||
border: `1px solid ${theme.palette.primary.main}`,
|
|
||||||
padding: "10px 20px",
|
|
||||||
borderRadius: "4px",
|
|
||||||
background: alpha(theme.palette.primary.main, 0.3),
|
|
||||||
color: "rgb(70,70,70)",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export class Alert extends Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div className={this.props.classes.alert}>
|
|
||||||
<Typography>{this.props.children}</Typography>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withStyles(styles, { withTheme: true })(Alert);
|
|
6
src/components/Blockly/BlocklyComponent.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#blocklyDiv {
|
||||||
|
height: 90vH;
|
||||||
|
width: 50%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
@ -21,74 +21,55 @@
|
|||||||
* @author samelh@google.com (Sam El-Husseini)
|
* @author samelh@google.com (Sam El-Husseini)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from 'react';
|
||||||
|
import './BlocklyComponent.css';
|
||||||
|
|
||||||
import Blockly from "blockly/core";
|
import Blockly from 'blockly/core';
|
||||||
import "blockly/blocks";
|
//import locale from 'blockly/msg/en';
|
||||||
import Toolbox from "./toolbox/Toolbox";
|
import 'blockly/blocks';
|
||||||
|
|
||||||
import { Card } from "@mui/material";
|
//Blockly.setLocale(locale);
|
||||||
import {
|
|
||||||
ScrollOptions,
|
|
||||||
ScrollBlockDragger,
|
|
||||||
ScrollMetricsManager,
|
|
||||||
} from "@blockly/plugin-scroll-options";
|
|
||||||
|
|
||||||
class BlocklyComponent extends React.Component {
|
class BlocklyComponent extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.blocklyDiv = React.createRef();
|
this.blocklyDiv = React.createRef();
|
||||||
this.toolbox = React.createRef();
|
this.toolbox = React.createRef();
|
||||||
this.state = { workspace: undefined };
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { initialXml, children, ...rest } = this.props;
|
|
||||||
this.primaryWorkspace = Blockly.inject(this.blocklyDiv.current, {
|
|
||||||
toolbox: this.toolbox.current,
|
|
||||||
plugins: {
|
|
||||||
// These are both required.
|
|
||||||
blockDragger: ScrollBlockDragger,
|
|
||||||
metricsManager: ScrollMetricsManager,
|
|
||||||
},
|
|
||||||
...rest,
|
|
||||||
});
|
|
||||||
// Initialize plugin.
|
|
||||||
|
|
||||||
this.setState({ workspace: this.primaryWorkspace });
|
|
||||||
const plugin = new ScrollOptions(this.workspace);
|
|
||||||
plugin.init({ enableWheelScroll: true, enableEdgeScroll: false });
|
|
||||||
if (initialXml) {
|
|
||||||
Blockly.Xml.domToWorkspace(
|
|
||||||
Blockly.Xml.textToDom(initialXml),
|
|
||||||
this.primaryWorkspace
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
get workspace() {
|
componentDidMount() {
|
||||||
return this.primaryWorkspace;
|
const { initialXml, children, ...rest } = this.props;
|
||||||
}
|
this.primaryWorkspace = Blockly.inject(
|
||||||
|
this.blocklyDiv.current,
|
||||||
|
{
|
||||||
|
toolbox: this.toolbox.current,
|
||||||
|
...rest
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
setXml(xml) {
|
if (initialXml) {
|
||||||
Blockly.Xml.domToWorkspace(
|
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(initialXml), this.primaryWorkspace);
|
||||||
Blockly.Xml.textToDom(xml),
|
}
|
||||||
this.primaryWorkspace
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
get workspace() {
|
||||||
return (
|
return this.primaryWorkspace;
|
||||||
<React.Fragment>
|
}
|
||||||
<Card
|
|
||||||
ref={this.blocklyDiv}
|
setXml(xml) {
|
||||||
id="blocklyDiv"
|
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), this.primaryWorkspace);
|
||||||
style={this.props.style ? this.props.style : {}}
|
}
|
||||||
/>
|
|
||||||
<Toolbox toolbox={this.toolbox} workspace={this.state.workspace} />
|
render() {
|
||||||
</React.Fragment>
|
const { children } = this.props;
|
||||||
);
|
|
||||||
}
|
return <React.Fragment>
|
||||||
|
<div ref={this.blocklyDiv} id="blocklyDiv" />
|
||||||
|
<xml xmlns="https://developers.google.com/blockly/xml" is="blockly" style={{ display: 'none' }} ref={this.toolbox}>
|
||||||
|
{children}
|
||||||
|
</xml>
|
||||||
|
</React.Fragment>;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BlocklyComponent;
|
export default BlocklyComponent;
|
||||||
|
@ -1,74 +0,0 @@
|
|||||||
import React, { Component } from 'react';
|
|
||||||
|
|
||||||
import * as Blockly from 'blockly/core';
|
|
||||||
|
|
||||||
class BlocklySvg extends Component {
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
svg: ''
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.getSvg();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(props) {
|
|
||||||
if(props.initialXml !== this.props.initialXml){
|
|
||||||
this.getSvg();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSvg = () => {
|
|
||||||
const workspace = Blockly.getMainWorkspace();
|
|
||||||
workspace.clear();
|
|
||||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(this.props.initialXml), workspace);
|
|
||||||
var canvas = workspace.svgBlockCanvas_.cloneNode(true);
|
|
||||||
|
|
||||||
if (canvas.children[0] !== undefined) {
|
|
||||||
canvas.removeAttribute("transform");
|
|
||||||
|
|
||||||
// does not work in react
|
|
||||||
// var cssContent = Blockly.Css.CONTENT.join('');
|
|
||||||
var cssContent = '';
|
|
||||||
for (var i = 0; i < document.getElementsByTagName('style').length; i++) {
|
|
||||||
if(/^blockly.*$/.test(document.getElementsByTagName('style')[i].id)){
|
|
||||||
cssContent += document.getElementsByTagName('style')[i].firstChild.data.replace(/\..* \./g, '.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ensure that fill-opacity is 1, because there cannot be a replacing
|
|
||||||
// https://github.com/google/blockly/pull/3431/files#diff-00254795773903d3c0430915a68c9521R328
|
|
||||||
cssContent += `.blocklyPath {
|
|
||||||
fill-opacity: 1;
|
|
||||||
}
|
|
||||||
.blocklyPathDark {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.blocklyPathLight {
|
|
||||||
display: flex;
|
|
||||||
} `;
|
|
||||||
|
|
||||||
var css = '<defs><style type="text/css" xmlns="http://www.w3.org/1999/xhtml"><![CDATA[' + cssContent + ']]></style></defs>';
|
|
||||||
var bbox = document.getElementsByClassName("blocklyBlockCanvas")[0].getBBox();
|
|
||||||
var content = new XMLSerializer().serializeToString(canvas);
|
|
||||||
var xml = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
width="${bbox.width}" height="${bbox.height}" viewBox="${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}">
|
|
||||||
${css}">${content}</svg>`;
|
|
||||||
|
|
||||||
this.setState({svg: xml});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{display: 'inline-flex', justifyContent: 'center', transform: 'scale(0.8) translate(0, calc(100% * -0.2 / 2))'}}
|
|
||||||
dangerouslySetInnerHTML={{ __html: this.state.svg }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BlocklySvg;
|
|
@ -1,155 +0,0 @@
|
|||||||
import React, { Component } from "react";
|
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import { onChangeWorkspace, clearStats } from "../../actions/workspaceActions";
|
|
||||||
|
|
||||||
import BlocklyComponent from "./BlocklyComponent";
|
|
||||||
import BlocklySvg from "./BlocklySvg";
|
|
||||||
|
|
||||||
import * as Blockly from "blockly/core";
|
|
||||||
import "./blocks/index";
|
|
||||||
import "./generator/index";
|
|
||||||
import { ZoomToFitControl } from "@blockly/zoom-to-fit";
|
|
||||||
import { initialXml } from "./initialXml.js";
|
|
||||||
import { getMaxInstances } from "./helpers/maxInstances";
|
|
||||||
import { Backpack } from "@blockly/workspace-backpack";
|
|
||||||
|
|
||||||
class BlocklyWindow extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.simpleWorkspace = React.createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const workspace = Blockly.getMainWorkspace();
|
|
||||||
this.props.onChangeWorkspace({});
|
|
||||||
this.props.clearStats();
|
|
||||||
workspace.addChangeListener((event) => {
|
|
||||||
this.props.onChangeWorkspace(event);
|
|
||||||
|
|
||||||
// switch on that a block is displayed disabled or not depending on whether it is correctly connected
|
|
||||||
// for SVG display, a deactivated block in the display is undesirable
|
|
||||||
if (this.props.blockDisabled) {
|
|
||||||
Blockly.Events.disableOrphans(event);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Blockly.svgResize(workspace);
|
|
||||||
const zoomToFit = new ZoomToFitControl(workspace);
|
|
||||||
zoomToFit.init();
|
|
||||||
// Initialize plugin.
|
|
||||||
const backpack = new Backpack(workspace);
|
|
||||||
backpack.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(props) {
|
|
||||||
const workspace = Blockly.getMainWorkspace();
|
|
||||||
var xml = this.props.initialXml;
|
|
||||||
if (props.selectedBoard !== this.props.selectedBoard) {
|
|
||||||
xml = localStorage.getItem("autoSaveXML");
|
|
||||||
// change board
|
|
||||||
if(!xml) xml = initialXml;
|
|
||||||
var xmlDom = Blockly.Xml.textToDom(xml);
|
|
||||||
Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace);
|
|
||||||
// var toolbox = workspace.getToolbox();
|
|
||||||
// workspace.updateToolbox(toolbox.toolboxDef_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if svg is true, then the update process is done in the BlocklySvg component
|
|
||||||
if (props.initialXml !== xml && !this.props.svg) {
|
|
||||||
// guarantees that the current xml-code (this.props.initialXml) is rendered
|
|
||||||
workspace.clear();
|
|
||||||
if (!xml) xml = initialXml;
|
|
||||||
Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), workspace);
|
|
||||||
}
|
|
||||||
if (props.language !== this.props.language) {
|
|
||||||
// change language
|
|
||||||
xml = localStorage.getItem("autoSaveXML");
|
|
||||||
if (!xml) xml = initialXml;
|
|
||||||
xmlDom = Blockly.Xml.textToDom(xml);
|
|
||||||
Blockly.Xml.clearWorkspaceAndLoadFromXml(xmlDom, workspace);
|
|
||||||
// var toolbox = workspace.getToolbox();
|
|
||||||
// workspace.updateToolbox(toolbox.toolboxDef_);
|
|
||||||
}
|
|
||||||
Blockly.svgResize(workspace);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<BlocklyComponent
|
|
||||||
ref={this.simpleWorkspace}
|
|
||||||
style={this.props.svg ? { height: 0 } : this.props.blocklyCSS}
|
|
||||||
readOnly={
|
|
||||||
this.props.readOnly !== undefined ? this.props.readOnly : false
|
|
||||||
}
|
|
||||||
trashcan={
|
|
||||||
this.props.trashcan !== undefined ? this.props.trashcan : true
|
|
||||||
}
|
|
||||||
renderer={this.props.renderer}
|
|
||||||
sounds={this.props.sounds}
|
|
||||||
maxInstances={getMaxInstances()}
|
|
||||||
zoom={{
|
|
||||||
// https://developers.google.com/blockly/guides/configure/web/zoom
|
|
||||||
controls:
|
|
||||||
this.props.zoomControls !== undefined
|
|
||||||
? this.props.zoomControls
|
|
||||||
: true,
|
|
||||||
wheel: false,
|
|
||||||
startScale: 1,
|
|
||||||
maxScale: 3,
|
|
||||||
minScale: 0.3,
|
|
||||||
scaleSpeed: 1.2,
|
|
||||||
}}
|
|
||||||
grid={
|
|
||||||
this.props.grid !== undefined && !this.props.grid
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
// https://developers.google.com/blockly/guides/configure/web/grid
|
|
||||||
spacing: 20,
|
|
||||||
length: 1,
|
|
||||||
colour: "#4EAF47", // senseBox-green
|
|
||||||
snap: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
media={"/media/blockly/"}
|
|
||||||
move={
|
|
||||||
this.props.move !== undefined && !this.props.move
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
// https://developers.google.com/blockly/guides/configure/web/move
|
|
||||||
scrollbars: true,
|
|
||||||
drag: true,
|
|
||||||
wheel: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initialXml={
|
|
||||||
this.props.initialXml ? this.props.initialXml : initialXml
|
|
||||||
}
|
|
||||||
></BlocklyComponent>
|
|
||||||
{this.props.svg && this.props.initialXml ? (
|
|
||||||
<BlocklySvg initialXml={this.props.initialXml} />
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BlocklyWindow.propTypes = {
|
|
||||||
onChangeWorkspace: PropTypes.func.isRequired,
|
|
||||||
clearStats: PropTypes.func.isRequired,
|
|
||||||
renderer: PropTypes.string.isRequired,
|
|
||||||
sounds: PropTypes.bool.isRequired,
|
|
||||||
language: PropTypes.string.isRequired,
|
|
||||||
selectedBoard: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapStateToProps = (state) => ({
|
|
||||||
renderer: state.general.renderer,
|
|
||||||
sounds: state.general.sounds,
|
|
||||||
language: state.general.language,
|
|
||||||
selectedBoard: state.board.board,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, { onChangeWorkspace, clearStats })(
|
|
||||||
BlocklyWindow
|
|
||||||
);
|
|
@ -1,88 +0,0 @@
|
|||||||
import Blockly from "blockly";
|
|
||||||
import { getColour } from "../helpers/colour";
|
|
||||||
import * as Types from "../helpers/types";
|
|
||||||
import { selectedBoard } from "../helpers/board";
|
|
||||||
import { FieldGridDropdown } from "@blockly/field-grid-dropdown";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DS18B20 Temperatursonde
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
Blockly.Blocks["CleVerLab_dummy1"] = {
|
|
||||||
init: function () {
|
|
||||||
this.setColour(getColour().cleverlab);
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField("tut nichts")
|
|
||||||
this.setOutput(true, Types.NUMBER.typeName);
|
|
||||||
this.data = {name: "empty"};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
Blockly.Blocks["CleVerLab_temperature"] = {
|
|
||||||
init: function () {
|
|
||||||
this.setColour(getColour().cleverlab);
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField("Temperatur")
|
|
||||||
.appendField("Digital Port:")
|
|
||||||
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPorts), "DigitalPin");
|
|
||||||
this.setOutput(true, Types.NUMBER.typeName);
|
|
||||||
this.data = {name: "ds18b20"};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* PH Wert
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
Blockly.Blocks["CleVerLab_pH"] = {
|
|
||||||
init: function () {
|
|
||||||
|
|
||||||
this.setColour(getColour().cleverlab);
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField("pH Wert")
|
|
||||||
.appendField("Digital Port:")
|
|
||||||
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPins), "DigitalPin");
|
|
||||||
this.setOutput(true, Types.NUMBER.typeName);
|
|
||||||
this.data = {name: "phoderso"};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Blockly.Blocks["CleVerLab_cali1"] = {
|
|
||||||
init: function () {
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField("Kalibriere pH Sensor");
|
|
||||||
this.appendValueInput("VAR1", "Number")
|
|
||||||
.appendField("Referenzlösung pH 4.00 =")
|
|
||||||
.setAlign(Blockly.ALIGN_RIGHT);
|
|
||||||
this.appendValueInput("VAR2", "Number2")
|
|
||||||
.appendField("Referenzlösung pH 7.00 =")
|
|
||||||
.setAlign(Blockly.ALIGN_RIGHT);
|
|
||||||
this.setPreviousStatement(true, null);
|
|
||||||
this.setNextStatement(true, null);
|
|
||||||
this.setColour(getColour().cleverlab);
|
|
||||||
this.setOutput(true, Types.NUMBER.typeName);
|
|
||||||
this.data = {name: "dsasda"};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pump
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
Blockly.Blocks['CleVerLab_pump'] = {
|
|
||||||
init: function() {
|
|
||||||
this.setColour(getColour().cleverlab);
|
|
||||||
var dropdown = new Blockly.FieldDropdown([
|
|
||||||
[ 'START','HIGH'],
|
|
||||||
[ 'STOPP','LOW']
|
|
||||||
]);
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField(dropdown, "Mode")
|
|
||||||
.appendField(" Pumpe ")
|
|
||||||
.appendField(new Blockly.FieldDropdown(selectedBoard().digitalPins), "DigitalPin");
|
|
||||||
this.setPreviousStatement(true, null);
|
|
||||||
this.setNextStatement(true, null);
|
|
||||||
//this.setOutput(true, "Number");
|
|
||||||
this.setTooltip('');
|
|
||||||
this.setHelpUrl('');
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,61 +0,0 @@
|
|||||||
import Blockly from 'blockly/core';
|
|
||||||
import { selectedBoard } from '../helpers/board'
|
|
||||||
import * as Types from '../helpers/types'
|
|
||||||
import { getColour } from '../helpers/colour';
|
|
||||||
|
|
||||||
|
|
||||||
Blockly.Blocks['io_tone'] = {
|
|
||||||
init: function () {
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField(Blockly.Msg.ARD_SETTONE)
|
|
||||||
.appendField(new Blockly.FieldDropdown(
|
|
||||||
selectedBoard().digitalPins), "TONEPIN");
|
|
||||||
this.appendValueInput("FREQUENCY")
|
|
||||||
.setCheck(Types.getCompatibleTypes('int'))
|
|
||||||
.appendField(Blockly.Msg.ARD_TONEFREQ);
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField("Hz");
|
|
||||||
this.setInputsInline(true);
|
|
||||||
this.setPreviousStatement(true);
|
|
||||||
this.setNextStatement(true);
|
|
||||||
this.setColour(getColour().audio);
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_TONE_TIP);
|
|
||||||
this.setHelpUrl('https://www.arduino.cc/en/Reference/tone');
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Called whenever anything on the workspace changes.
|
|
||||||
* It checks frequency values and sets a warning if out of range.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
onchange: function (event) {
|
|
||||||
if (!this.workspace || event.type === Blockly.Events.MOVE ||
|
|
||||||
event.type === Blockly.Events.UI) {
|
|
||||||
return; // Block deleted or irrelevant event
|
|
||||||
}
|
|
||||||
var freq = Blockly.Arduino.valueToCode(
|
|
||||||
this, "FREQUENCY", Blockly.Arduino.ORDER_ATOMIC)
|
|
||||||
if (freq < 31 || freq > 65535) {
|
|
||||||
this.setWarningText(Blockly.Msg.ARD_TONE_WARNING, 'io_tone');
|
|
||||||
} else {
|
|
||||||
this.setWarningText(null, 'io_tone');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks['io_notone'] = {
|
|
||||||
init: function () {
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField(Blockly.Msg.ARD_NOTONE)
|
|
||||||
.appendField(new Blockly.FieldDropdown(
|
|
||||||
selectedBoard().digitalPins), "TONEPIN");
|
|
||||||
this.setPreviousStatement(true);
|
|
||||||
this.setNextStatement(true);
|
|
||||||
this.setColour(getColour().audio);
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_NOTONE_TIP);
|
|
||||||
this.setHelpUrl('https://www.arduino.cc/en/Reference/noTone');
|
|
||||||
},
|
|
||||||
/** @return {!string} The type of input value for the block, an integer. */
|
|
||||||
getBlockType: function () {
|
|
||||||
return Blockly.Types.NUMBER;
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,31 +1,4 @@
|
|||||||
import "./loops";
|
import './loops';
|
||||||
import "./sensebox";
|
import './sensebox';
|
||||||
import "./logic";
|
import './logic';
|
||||||
import "./sensebox-sensors";
|
import './sensebox-sensors';
|
||||||
import "./sensebox-telegram";
|
|
||||||
import "./sensebox-osem";
|
|
||||||
import "./sensebox-web";
|
|
||||||
import "./sensebox-display";
|
|
||||||
import "./sensebox-motors";
|
|
||||||
import "./sensebox-lora";
|
|
||||||
import "./sensebox-led";
|
|
||||||
import "./sensebox-rtc";
|
|
||||||
import "./sensebox-ntp";
|
|
||||||
import "./sensebox-ble";
|
|
||||||
import "./sensebox-sd";
|
|
||||||
import "./mqtt";
|
|
||||||
import "./text";
|
|
||||||
import "./io";
|
|
||||||
import "./audio";
|
|
||||||
import "./math";
|
|
||||||
import "./map";
|
|
||||||
import "./procedures";
|
|
||||||
import "./serial";
|
|
||||||
import "./time";
|
|
||||||
import "./variables";
|
|
||||||
import "./lists";
|
|
||||||
import "./watchdog";
|
|
||||||
import "./webserver";
|
|
||||||
import "./CleVerLab"
|
|
||||||
|
|
||||||
import "../helpers/types";
|
|
@ -1,280 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license Licensed under the Apache License, Version 2.0 (the "License"):
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview Blocks for Arduino Digital and Analogue input and output
|
|
||||||
* functions. The Arduino function syntax can be found at
|
|
||||||
* http://arduino.cc/en/Reference/HomePage
|
|
||||||
*
|
|
||||||
* TODO: maybe change this to a "PIN" BlocklyType
|
|
||||||
*/
|
|
||||||
import Blockly from "blockly/core";
|
|
||||||
import { selectedBoard } from "../helpers/board";
|
|
||||||
import * as Types from "../helpers/types";
|
|
||||||
|
|
||||||
Blockly.Blocks["io_digitalwrite"] = {
|
|
||||||
/**
|
|
||||||
* Block for creating a 'set pin' to a state.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setHelpUrl("http://arduino.cc/en/Reference/DigitalWrite");
|
|
||||||
this.setColour(250);
|
|
||||||
this.appendValueInput("STATE")
|
|
||||||
.appendField(Blockly.Msg.ARD_DIGITALWRITE)
|
|
||||||
.appendField(
|
|
||||||
new Blockly.FieldDropdown(selectedBoard().digitalPins),
|
|
||||||
"PIN"
|
|
||||||
)
|
|
||||||
.appendField(Blockly.Msg.ARD_WRITE_TO)
|
|
||||||
.setCheck(Types.BOOLEAN.checkList);
|
|
||||||
this.setInputsInline(false);
|
|
||||||
this.setPreviousStatement(true, null);
|
|
||||||
this.setNextStatement(true, null);
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_DIGITALWRITE_TIP);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Updates the content of the the pin related fields.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
updateFields: function () {
|
|
||||||
Blockly.Arduino.Boards.refreshBlockFieldDropdown(
|
|
||||||
this,
|
|
||||||
"PIN",
|
|
||||||
"digitalPins"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["io_digitalread"] = {
|
|
||||||
/**
|
|
||||||
* Block for creating a 'read pin'.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setHelpUrl("http://arduino.cc/en/Reference/DigitalRead");
|
|
||||||
this.setColour(250);
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField(Blockly.Msg.ARD_DIGITALREAD)
|
|
||||||
.appendField(
|
|
||||||
new Blockly.FieldDropdown(selectedBoard().digitalPins),
|
|
||||||
"PIN"
|
|
||||||
);
|
|
||||||
this.setOutput(true, "boolean");
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_DIGITALREAD_TIP);
|
|
||||||
},
|
|
||||||
/** @return {!string} The type of return value for the block, an integer. */
|
|
||||||
getBlockType: function () {
|
|
||||||
return Types.BOOLEAN;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Updates the content of the the pin related fields.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
updateFields: function () {
|
|
||||||
Blockly.Arduino.Boards.refreshBlockFieldDropdown(
|
|
||||||
this,
|
|
||||||
"PIN",
|
|
||||||
"digitalPins"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["io_builtin_led"] = {
|
|
||||||
/**
|
|
||||||
* Block for setting built-in LED to a state.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setHelpUrl("http://arduino.cc/en/Reference/DigitalWrite");
|
|
||||||
this.setColour(250);
|
|
||||||
this.appendValueInput("STATE")
|
|
||||||
.appendField(Blockly.Msg.ARD_BUILTIN_LED)
|
|
||||||
.appendField(
|
|
||||||
new Blockly.FieldDropdown(selectedBoard().builtinLed),
|
|
||||||
"BUILT_IN_LED"
|
|
||||||
)
|
|
||||||
.appendField(Blockly.Msg.ARD_WRITE_TO)
|
|
||||||
.setCheck(Types.BOOLEAN.compatibleTypes);
|
|
||||||
this.setInputsInline(false);
|
|
||||||
this.setPreviousStatement(true, null);
|
|
||||||
this.setNextStatement(true, null);
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_BUILTIN_LED_TIP);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Updates the content of the the pin related fields.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
updateFields: function () {
|
|
||||||
Blockly.Arduino.Boards.refreshBlockFieldDropdown(
|
|
||||||
this,
|
|
||||||
"BUILT_IN_LED",
|
|
||||||
"builtinLed"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
/** @return {!string} The type of input value for the block, an integer. */
|
|
||||||
getBlockType: function () {
|
|
||||||
return Types.BOOLEAN;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["io_analogwrite"] = {
|
|
||||||
/**
|
|
||||||
* Block for creating a 'set pin' to an analogue value.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setHelpUrl("http://arduino.cc/en/Reference/AnalogWrite");
|
|
||||||
this.setColour(250);
|
|
||||||
this.appendValueInput("NUM")
|
|
||||||
.appendField(Blockly.Msg.ARD_ANALOGWRITE)
|
|
||||||
.appendField(new Blockly.FieldDropdown(selectedBoard().pwmPins), "PIN")
|
|
||||||
.appendField(Blockly.Msg.ARD_WRITE_TO)
|
|
||||||
.setCheck(Types.NUMBER.compatibleTypes);
|
|
||||||
this.setInputsInline(false);
|
|
||||||
this.setPreviousStatement(true, null);
|
|
||||||
this.setNextStatement(true, null);
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_ANALOGWRITE_TIP);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Updates the content of the the pin related fields.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
updateFields: function () {
|
|
||||||
Blockly.Arduino.Boards.refreshBlockFieldDropdown(this, "PIN", "pwmPins");
|
|
||||||
},
|
|
||||||
/** @return {!string} The type of input value for the block, an integer. */
|
|
||||||
getBlockType: function () {
|
|
||||||
return Types.NUMBER;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["io_analogread"] = {
|
|
||||||
/**
|
|
||||||
* Block for reading an analogue input.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setHelpUrl("http://arduino.cc/en/Reference/AnalogRead");
|
|
||||||
this.setColour(250);
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField(Blockly.Msg.ARD_ANALOGREAD)
|
|
||||||
.appendField(
|
|
||||||
new Blockly.FieldDropdown(selectedBoard().analogPins),
|
|
||||||
"PIN"
|
|
||||||
);
|
|
||||||
this.setOutput(true, Types.NUMBER.typeName);
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_ANALOGREAD_TIP);
|
|
||||||
},
|
|
||||||
/** @return {!string} The type of return value for the block, an integer. */
|
|
||||||
getBlockType: function () {
|
|
||||||
return Types.NUMBER.typeName;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Updates the content of the the pin related fields.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
updateFields: function () {
|
|
||||||
Blockly.Arduino.Boards.refreshBlockFieldDropdown(this, "PIN", "analogPins");
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["io_highlow"] = {
|
|
||||||
/**
|
|
||||||
* Block for creating a pin state.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setHelpUrl("http://arduino.cc/en/Reference/Constants");
|
|
||||||
this.setColour(250);
|
|
||||||
this.appendDummyInput().appendField(
|
|
||||||
new Blockly.FieldDropdown([
|
|
||||||
[Blockly.Msg.ARD_HIGH, "HIGH"],
|
|
||||||
[Blockly.Msg.ARD_LOW, "LOW"],
|
|
||||||
]),
|
|
||||||
"STATE"
|
|
||||||
);
|
|
||||||
this.setOutput(true, Types.BOOLEAN.typeName);
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_HIGHLOW_TIP);
|
|
||||||
},
|
|
||||||
/** @return {!string} The type of return value for the block, an integer. */
|
|
||||||
getBlockType: function () {
|
|
||||||
return Types.BOOLEAN;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["io_pulsein"] = {
|
|
||||||
/**
|
|
||||||
* Block for measuring the duration of a pulse in an input pin.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.jsonInit({
|
|
||||||
type: "math_foo",
|
|
||||||
message0: Blockly.Msg.ARD_PULSE_READ,
|
|
||||||
args0: [
|
|
||||||
{
|
|
||||||
type: "input_value",
|
|
||||||
name: "PULSETYPE",
|
|
||||||
check: Types.BOOLEAN.compatibleTypes,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "field_dropdown",
|
|
||||||
name: "PULSEPIN",
|
|
||||||
options: selectedBoard().digitalPins,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
output: Types.NUMBER.typeName,
|
|
||||||
inputsInline: true,
|
|
||||||
colour: 250,
|
|
||||||
tooltip: Blockly.Msg.ARD_PULSE_TIP,
|
|
||||||
helpUrl: "https://www.arduino.cc/en/Reference/PulseIn",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/** @return {!string} The type of input value for the block, an integer. */
|
|
||||||
getBlockType: function () {
|
|
||||||
return Types.NUMBER.typeName;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["io_pulsetimeout"] = {
|
|
||||||
/**
|
|
||||||
* Block for measuring (with a time-out) the duration of a pulse in an input
|
|
||||||
* pin.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.jsonInit({
|
|
||||||
type: "math_foo",
|
|
||||||
message0: Blockly.Msg.ARD_PULSE_READ_TIMEOUT,
|
|
||||||
args0: [
|
|
||||||
{
|
|
||||||
type: "input_value",
|
|
||||||
name: "PULSETYPE",
|
|
||||||
check: Types.BOOLEAN.compatibleTypes,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "field_dropdown",
|
|
||||||
name: "PULSEPIN",
|
|
||||||
options: selectedBoard().digitalPins,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "input_value",
|
|
||||||
name: "TIMEOUT",
|
|
||||||
check: Types.NUMBER.compatibleTypes,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
output: Types.NUMBER.typeName,
|
|
||||||
inputsInline: true,
|
|
||||||
colour: 250,
|
|
||||||
tooltip: Blockly.Msg.ARD_PULSETIMEOUT_TIP,
|
|
||||||
helpUrl: "https://www.arduino.cc/en/Reference/PulseIn",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/** @return {!string} The type of input value for the block, an integer. */
|
|
||||||
getBlockType: function () {
|
|
||||||
return Types.NUMBER.typeName;
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,54 +0,0 @@
|
|||||||
import Blockly, { FieldDropdown } from "blockly/core";
|
|
||||||
import * as Types from "../helpers/types";
|
|
||||||
import { getColour } from "../helpers/colour";
|
|
||||||
|
|
||||||
Blockly.Blocks["lists_create_empty"] = {
|
|
||||||
/**
|
|
||||||
* Elapsed time in milliseconds block definition
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setHelpUrl("http://arduino.cc/en/Reference/Millis");
|
|
||||||
this.setColour(getColour().arrays);
|
|
||||||
this.appendDummyInput().appendField("create List with");
|
|
||||||
this.appendValueInput("NUMBER");
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField("Items of Type")
|
|
||||||
.appendField(new FieldDropdown(Types.VARIABLE_TYPES), "type");
|
|
||||||
this.setOutput(true, Types.ARRAY.typeName);
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_TIME_MILLIS_TIP);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["array_getIndex"] = {
|
|
||||||
/**
|
|
||||||
* Elapsed time in milliseconds block definition
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setHelpUrl("http://arduino.cc/en/Reference/Millis");
|
|
||||||
this.setColour(getColour().arrays);
|
|
||||||
this.appendDummyInput().appendField(
|
|
||||||
new Blockly.FieldVariable("X", null, ["Array"], "Array"),
|
|
||||||
"FIELDNAME"
|
|
||||||
);
|
|
||||||
this.setOutput(true, Types.ARRAY.typeName);
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_TIME_MILLIS_TIP);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["lists_length"] = {
|
|
||||||
/**
|
|
||||||
* Elapsed time in milliseconds block definition
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setHelpUrl("http://arduino.cc/en/Reference/Millis");
|
|
||||||
this.setColour(getColour().arrays);
|
|
||||||
this.appendValueInput("ARRAY")
|
|
||||||
.appendField("length of")
|
|
||||||
.setCheck(Types.ARRAY.compatibleTypes);
|
|
||||||
this.setOutput(true, Types.NUMBER.typeName);
|
|
||||||
this.setTooltip(Blockly.Msg.ARD_TIME_MILLIS_TIP);
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,651 +1,61 @@
|
|||||||
import Blockly from "blockly/core";
|
import { defineBlocksWithJsonArray } from 'blockly';
|
||||||
import { getColour } from "../helpers/colour";
|
import Blockly from 'blockly/core';
|
||||||
import * as Types from "../helpers/types";
|
|
||||||
import { getCompatibleTypes } from "../helpers/types";
|
|
||||||
|
|
||||||
Blockly.Blocks["controls_if"] = {
|
defineBlocksWithJsonArray([
|
||||||
/**
|
// If/else block that does not use a mutator.
|
||||||
* Block for if/elseif/else condition.
|
{
|
||||||
* @this Blockly.Block
|
type: 'control_if',
|
||||||
*/
|
message0: '%{BKY_CONTROLS_IF_MSG_IF} %1',
|
||||||
init: function () {
|
args0: [
|
||||||
this.setHelpUrl(Blockly.Msg.CONTROLS_IF_HELPURL);
|
{
|
||||||
this.setColour(getColour().logic);
|
type: 'input_value',
|
||||||
this.appendValueInput("IF0")
|
name: 'IF0',
|
||||||
.setCheck(Types.getCompatibleTypes("boolean"))
|
check: 'Boolean'
|
||||||
.appendField(Blockly.Msg.CONTROLS_IF_MSG_IF);
|
}
|
||||||
this.appendStatementInput("DO0").appendField(
|
|
||||||
Blockly.Msg.CONTROLS_IF_MSG_THEN
|
|
||||||
);
|
|
||||||
this.setPreviousStatement(true);
|
|
||||||
this.setNextStatement(true);
|
|
||||||
this.setMutator(
|
|
||||||
new Blockly.Mutator(["controls_if_elseif", "controls_if_else"])
|
|
||||||
);
|
|
||||||
this.setTooltip(Blockly.Msg.CONTROLS_IF_TOOLTIP_1);
|
|
||||||
this.elseifCount_ = 0;
|
|
||||||
this.elseCount_ = 0;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Create XML to represent the number of else-if and else inputs.
|
|
||||||
* @return {Element} XML storage element.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
mutationToDom: function () {
|
|
||||||
if (!this.elseifCount_ && !this.elseCount_) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var container = document.createElement("mutation");
|
|
||||||
if (this.elseifCount_) {
|
|
||||||
container.setAttribute("elseif", this.elseifCount_);
|
|
||||||
}
|
|
||||||
if (this.elseCount_) {
|
|
||||||
container.setAttribute("else", 1);
|
|
||||||
}
|
|
||||||
return container;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Parse XML to restore the else-if and else inputs.
|
|
||||||
* @param {!Element} xmlElement XML storage element.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
domToMutation: function (xmlElement) {
|
|
||||||
this.elseifCount_ = parseInt(xmlElement.getAttribute("elseif"), 10) || 0;
|
|
||||||
this.elseCount_ = parseInt(xmlElement.getAttribute("else"), 10) || 0;
|
|
||||||
this.updateShape_();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Populate the mutator's dialog with this block's components.
|
|
||||||
* @param {!Blockly.Workspace} workspace Mutator's workspace.
|
|
||||||
* @return {!Blockly.Block} Root block in mutator.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
decompose: function (workspace) {
|
|
||||||
var containerBlock = workspace.newBlock("controls_if_if");
|
|
||||||
containerBlock.initSvg();
|
|
||||||
var connection = containerBlock.nextConnection;
|
|
||||||
for (var i = 1; i <= this.elseifCount_; i++) {
|
|
||||||
var elseifBlock = workspace.newBlock("controls_if_elseif");
|
|
||||||
elseifBlock.initSvg();
|
|
||||||
connection.connect(elseifBlock.previousConnection);
|
|
||||||
connection = elseifBlock.nextConnection;
|
|
||||||
}
|
|
||||||
if (this.elseCount_) {
|
|
||||||
var elseBlock = workspace.newBlock("controls_if_else");
|
|
||||||
elseBlock.initSvg();
|
|
||||||
connection.connect(elseBlock.previousConnection);
|
|
||||||
}
|
|
||||||
return containerBlock;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Reconfigure this block based on the mutator dialog's components.
|
|
||||||
* @param {!Blockly.Block} containerBlock Root block in mutator.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
compose: function (containerBlock) {
|
|
||||||
var clauseBlock = containerBlock.nextConnection.targetBlock();
|
|
||||||
// Count number of inputs.
|
|
||||||
this.elseifCount_ = 0;
|
|
||||||
this.elseCount_ = 0;
|
|
||||||
var valueConnections = [null];
|
|
||||||
var statementConnections = [null];
|
|
||||||
var elseStatementConnection = null;
|
|
||||||
while (clauseBlock) {
|
|
||||||
switch (clauseBlock.type) {
|
|
||||||
case "controls_if_elseif":
|
|
||||||
this.elseifCount_++;
|
|
||||||
valueConnections.push(clauseBlock.valueConnection_);
|
|
||||||
statementConnections.push(clauseBlock.statementConnection_);
|
|
||||||
break;
|
|
||||||
case "controls_if_else":
|
|
||||||
this.elseCount_++;
|
|
||||||
elseStatementConnection = clauseBlock.statementConnection_;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("Unknown block type.");
|
|
||||||
}
|
|
||||||
clauseBlock =
|
|
||||||
clauseBlock.nextConnection && clauseBlock.nextConnection.targetBlock();
|
|
||||||
}
|
|
||||||
this.updateShape_();
|
|
||||||
// Reconnect any child blocks.
|
|
||||||
for (var i = 1; i <= this.elseifCount_; i++) {
|
|
||||||
Blockly.Mutator.reconnect(valueConnections[i], this, "IF" + i);
|
|
||||||
Blockly.Mutator.reconnect(statementConnections[i], this, "DO" + i);
|
|
||||||
}
|
|
||||||
Blockly.Mutator.reconnect(elseStatementConnection, this, "ELSE");
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Store pointers to any connected child blocks.
|
|
||||||
* @param {!Blockly.Block} containerBlock Root block in mutator.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
saveConnections: function (containerBlock) {
|
|
||||||
var clauseBlock = containerBlock.nextConnection.targetBlock();
|
|
||||||
var i = 1;
|
|
||||||
var inputDo;
|
|
||||||
while (clauseBlock) {
|
|
||||||
switch (clauseBlock.type) {
|
|
||||||
case "controls_if_elseif":
|
|
||||||
var inputIf = this.getInput("IF" + i);
|
|
||||||
inputDo = this.getInput("DO" + i);
|
|
||||||
clauseBlock.valueConnection_ =
|
|
||||||
inputIf && inputIf.connection.targetConnection;
|
|
||||||
clauseBlock.statementConnection_ =
|
|
||||||
inputDo && inputDo.connection.targetConnection;
|
|
||||||
i++;
|
|
||||||
break;
|
|
||||||
case "controls_if_else":
|
|
||||||
inputDo = this.getInput("ELSE");
|
|
||||||
clauseBlock.statementConnection_ =
|
|
||||||
inputDo && inputDo.connection.targetConnection;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("Unknown block type.");
|
|
||||||
}
|
|
||||||
clauseBlock =
|
|
||||||
clauseBlock.nextConnection && clauseBlock.nextConnection.targetBlock();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Modify this block to have the correct number of inputs.
|
|
||||||
* @private
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
updateShape_: function () {
|
|
||||||
// Delete everything.
|
|
||||||
if (this.getInput("ELSE")) {
|
|
||||||
this.removeInput("ELSE");
|
|
||||||
}
|
|
||||||
var j = 1;
|
|
||||||
while (this.getInput("IF" + j)) {
|
|
||||||
this.removeInput("IF" + j);
|
|
||||||
this.removeInput("DO" + j);
|
|
||||||
j++;
|
|
||||||
}
|
|
||||||
// Rebuild block.
|
|
||||||
for (var i = 1; i <= this.elseifCount_; i++) {
|
|
||||||
this.appendValueInput("IF" + i)
|
|
||||||
.setCheck(Types.getCompatibleTypes("boolean"))
|
|
||||||
.appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSEIF);
|
|
||||||
this.appendStatementInput("DO" + i).appendField(
|
|
||||||
Blockly.Msg.CONTROLS_IF_MSG_THEN
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (this.elseCount_) {
|
|
||||||
this.appendStatementInput("ELSE").appendField(
|
|
||||||
Blockly.Msg.CONTROLS_IF_MSG_ELSE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["controls_if_if"] = {
|
|
||||||
/**
|
|
||||||
* Mutator block for if container.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setColour(getColour().logic);
|
|
||||||
this.appendDummyInput().appendField(Blockly.Msg.CONTROLS_IF_IF_TITLE_IF);
|
|
||||||
this.setNextStatement(true);
|
|
||||||
this.setTooltip(Blockly.Msg.CONTROLS_IF_IF_TOOLTIP);
|
|
||||||
this.contextMenu = false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["controls_if_elseif"] = {
|
|
||||||
/**
|
|
||||||
* Mutator bolck for else-if condition.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setColour(getColour().logic);
|
|
||||||
this.appendDummyInput().appendField(
|
|
||||||
Blockly.Msg.CONTROLS_IF_ELSEIF_TITLE_ELSEIF
|
|
||||||
);
|
|
||||||
this.setPreviousStatement(true);
|
|
||||||
this.setNextStatement(true);
|
|
||||||
this.setTooltip(Blockly.Msg.CONTROLS_IF_ELSEIF_TOOLTIP);
|
|
||||||
this.contextMenu = false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["controls_if_else"] = {
|
|
||||||
/**
|
|
||||||
* Mutator block for else condition.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.setColour(getColour().logic);
|
|
||||||
this.appendDummyInput().appendField(
|
|
||||||
Blockly.Msg.CONTROLS_IF_ELSE_TITLE_ELSE
|
|
||||||
);
|
|
||||||
this.setPreviousStatement(true);
|
|
||||||
this.setTooltip(Blockly.Msg.CONTROLS_IF_ELSE_TOOLTIP);
|
|
||||||
this.contextMenu = false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.defineBlocksWithJsonArray([
|
|
||||||
// BEGIN JSON EXTRACT
|
|
||||||
// Block for boolean data type: true and false.
|
|
||||||
{
|
|
||||||
type: "logic_boolean",
|
|
||||||
message0: "%1",
|
|
||||||
args0: [
|
|
||||||
{
|
|
||||||
type: "field_dropdown",
|
|
||||||
name: "BOOL",
|
|
||||||
options: [
|
|
||||||
["%{BKY_LOGIC_BOOLEAN_TRUE}", "TRUE"],
|
|
||||||
["%{BKY_LOGIC_BOOLEAN_FALSE}", "FALSE"],
|
|
||||||
],
|
],
|
||||||
},
|
message1: '%{BKY_CONTROLS_IF_MSG_THEN} %1',
|
||||||
],
|
args1: [
|
||||||
output: Types.BOOLEAN.typeName,
|
{
|
||||||
style: "logic_blocks",
|
type: 'input_statement',
|
||||||
tooltip: "%{BKY_LOGIC_BOOLEAN_TOOLTIP}",
|
name: 'DO0'
|
||||||
helpUrl: "%{BKY_LOGIC_BOOLEAN_HELPURL}",
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "controls_ifelse",
|
|
||||||
message0: "%{BKY_CONTROLS_IF_MSG_IF} %1",
|
|
||||||
args0: [
|
|
||||||
{
|
|
||||||
type: "input_value",
|
|
||||||
name: "IF0",
|
|
||||||
check: Types.getCompatibleTypes("boolean"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
message1: "%{BKY_CONTROLS_IF_MSG_THEN} %1",
|
|
||||||
args1: [
|
|
||||||
{
|
|
||||||
type: "input_statement",
|
|
||||||
name: "DO0",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
message2: "%{BKY_CONTROLS_IF_MSG_ELSE} %1",
|
|
||||||
args2: [
|
|
||||||
{
|
|
||||||
type: "input_statement",
|
|
||||||
name: "ELSE",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
previousStatement: null,
|
|
||||||
nextStatement: null,
|
|
||||||
style: "logic_blocks",
|
|
||||||
tooltip: "%{BKYCONTROLS_IF_TOOLTIP_2}",
|
|
||||||
helpUrl: "%{BKY_CONTROLS_IF_HELPURL}",
|
|
||||||
extensions: ["controls_if_tooltip"],
|
|
||||||
},
|
|
||||||
// Block for comparison operator.
|
|
||||||
{
|
|
||||||
type: "logic_compare",
|
|
||||||
message0: "%1 %2 %3",
|
|
||||||
args0: [
|
|
||||||
{
|
|
||||||
type: "input_value",
|
|
||||||
name: "A",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "field_dropdown",
|
|
||||||
name: "OP",
|
|
||||||
options: [
|
|
||||||
["=", "EQ"],
|
|
||||||
["\u2260", "NEQ"],
|
|
||||||
["\u200F<", "LT"],
|
|
||||||
["\u200F\u2264", "LTE"],
|
|
||||||
["\u200F>", "GT"],
|
|
||||||
["\u200F\u2265", "GTE"],
|
|
||||||
],
|
],
|
||||||
},
|
previousStatement: null,
|
||||||
{
|
nextStatement: null,
|
||||||
type: "input_value",
|
colour: '#b063c5',
|
||||||
name: "B",
|
tooltip: '%{BKYCONTROLS_IF_TOOLTIP_2}',
|
||||||
},
|
helpUrl: '%{BKY_CONTROLS_IF_HELPURL}',
|
||||||
],
|
extensions: ['controls_if_tooltip']
|
||||||
inputsInline: true,
|
},
|
||||||
output: Types.BOOLEAN.typeName,
|
{
|
||||||
style: "logic_blocks",
|
type: 'controls_ifelse',
|
||||||
helpUrl: "%{BKY_LOGIC_COMPARE_HELPURL}",
|
message0: '%{BKY_CONTROLS_IF_MSG_IF} %1',
|
||||||
extensions: ["logic_compare", "logic_op_tooltip"],
|
args0: [
|
||||||
},
|
{
|
||||||
// Block for logical operations: 'and', 'or'.
|
type: 'input_value',
|
||||||
{
|
name: 'IF0',
|
||||||
type: "logic_operation",
|
check: 'Boolean'
|
||||||
message0: "%1 %2 %3",
|
}
|
||||||
args0: [
|
|
||||||
{
|
|
||||||
type: "input_value",
|
|
||||||
name: "A",
|
|
||||||
check: Types.getCompatibleTypes("boolean"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "field_dropdown",
|
|
||||||
name: "OP",
|
|
||||||
options: [
|
|
||||||
["%{BKY_LOGIC_OPERATION_AND}", "AND"],
|
|
||||||
["%{BKY_LOGIC_OPERATION_OR}", "OR"],
|
|
||||||
],
|
],
|
||||||
},
|
message1: '%{BKY_CONTROLS_IF_MSG_THEN} %1',
|
||||||
{
|
args1: [
|
||||||
type: "input_value",
|
{
|
||||||
name: "B",
|
type: 'input_statement',
|
||||||
check: Types.getCompatibleTypes("boolean"),
|
name: 'DO0'
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
inputsInline: true,
|
message2: '%{BKY_CONTROLS_IF_MSG_ELSE} %1',
|
||||||
output: Types.BOOLEAN.typeName,
|
args2: [
|
||||||
style: "logic_blocks",
|
{
|
||||||
helpUrl: "%{BKY_LOGIC_OPERATION_HELPURL}",
|
type: 'input_statement',
|
||||||
extensions: ["logic_op_tooltip"],
|
name: 'ELSE'
|
||||||
},
|
}
|
||||||
// Block for negation.
|
],
|
||||||
{
|
previousStatement: null,
|
||||||
type: "logic_negate",
|
nextStatement: null,
|
||||||
message0: "%{BKY_LOGIC_NEGATE_TITLE}",
|
colour: '#b063c5',
|
||||||
args0: [
|
tooltip: '%{BKYCONTROLS_IF_TOOLTIP_2}',
|
||||||
{
|
helpUrl: '%{BKY_CONTROLS_IF_HELPURL}',
|
||||||
type: "input_value",
|
extensions: ['controls_if_tooltip']
|
||||||
name: "BOOL",
|
|
||||||
check: Types.getCompatibleTypes("boolean"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
output: Types.BOOLEAN.typeName,
|
|
||||||
style: "logic_blocks",
|
|
||||||
tooltip: "%{BKY_LOGIC_NEGATE_TOOLTIP}",
|
|
||||||
helpUrl: "%{BKY_LOGIC_NEGATE_HELPURL}",
|
|
||||||
},
|
|
||||||
// Block for null data type.
|
|
||||||
{
|
|
||||||
type: "logic_null",
|
|
||||||
message0: "%{BKY_LOGIC_NULL}",
|
|
||||||
output: null,
|
|
||||||
style: "logic_blocks",
|
|
||||||
tooltip: "%{BKY_LOGIC_NULL_TOOLTIP}",
|
|
||||||
helpUrl: "%{BKY_LOGIC_NULL_HELPURL}",
|
|
||||||
},
|
|
||||||
// Block for ternary operator.
|
|
||||||
{
|
|
||||||
type: "logic_ternary",
|
|
||||||
message0: "%{BKY_LOGIC_TERNARY_CONDITION} %1",
|
|
||||||
args0: [
|
|
||||||
{
|
|
||||||
type: "input_value",
|
|
||||||
name: "IF",
|
|
||||||
check: Types.getCompatibleTypes("boolean"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
message1: "%{BKY_LOGIC_TERNARY_IF_TRUE} %1",
|
|
||||||
args1: [
|
|
||||||
{
|
|
||||||
type: "input_value",
|
|
||||||
name: "THEN",
|
|
||||||
check: Types.getCompatibleTypes("boolean"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
message2: "%{BKY_LOGIC_TERNARY_IF_FALSE} %1",
|
|
||||||
args2: [
|
|
||||||
{
|
|
||||||
type: "input_value",
|
|
||||||
name: "ELSE",
|
|
||||||
check: Types.getCompatibleTypes("boolean"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
output: null,
|
|
||||||
style: "logic_blocks",
|
|
||||||
tooltip: "%{BKY_LOGIC_TERNARY_TOOLTIP}",
|
|
||||||
helpUrl: "%{BKY_LOGIC_TERNARY_HELPURL}",
|
|
||||||
extensions: ["logic_ternary"],
|
|
||||||
},
|
|
||||||
]); // END JSON EXTRACT (Do not delete this comment.)
|
|
||||||
|
|
||||||
Blockly.Blocks["logic_compare"] = {
|
|
||||||
/**
|
|
||||||
* Block for comparison operator.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
var OPERATORS = this.RTL
|
|
||||||
? [
|
|
||||||
["=", "EQ"],
|
|
||||||
["\u2260", "NEQ"],
|
|
||||||
[">", "LT"],
|
|
||||||
["\u2265", "LTE"],
|
|
||||||
["<", "GT"],
|
|
||||||
["\u2264", "GTE"],
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
["=", "EQ"],
|
|
||||||
["\u2260", "NEQ"],
|
|
||||||
["<", "LT"],
|
|
||||||
["\u2264", "LTE"],
|
|
||||||
[">", "GT"],
|
|
||||||
["\u2265", "GTE"],
|
|
||||||
];
|
|
||||||
this.setHelpUrl(Blockly.Msg.LOGIC_COMPARE_HELPURL);
|
|
||||||
this.setColour(getColour().logic);
|
|
||||||
this.setOutput(true, Types.BOOLEAN.typeName);
|
|
||||||
this.appendValueInput("A");
|
|
||||||
this.appendValueInput("B").appendField(
|
|
||||||
new Blockly.FieldDropdown(OPERATORS),
|
|
||||||
"OP"
|
|
||||||
);
|
|
||||||
this.setInputsInline(true);
|
|
||||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
||||||
var thisBlock = this;
|
|
||||||
this.setTooltip(function () {
|
|
||||||
var op = thisBlock.getFieldValue("OP");
|
|
||||||
var TOOLTIPS = {
|
|
||||||
EQ: Blockly.Msg.LOGIC_COMPARE_TOOLTIP_EQ,
|
|
||||||
NEQ: Blockly.Msg.LOGIC_COMPARE_TOOLTIP_NEQ,
|
|
||||||
LT: Blockly.Msg.LOGIC_COMPARE_TOOLTIP_LT,
|
|
||||||
LTE: Blockly.Msg.LOGIC_COMPARE_TOOLTIP_LTE,
|
|
||||||
GT: Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GT,
|
|
||||||
GTE: Blockly.Msg.LOGIC_COMPARE_TOOLTIP_GTE,
|
|
||||||
};
|
|
||||||
return TOOLTIPS[op];
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Called whenever anything on the workspace changes.
|
|
||||||
* Prevent mismatched types from being compared.
|
|
||||||
* @param {!Blockly.Events.Abstract} e Change event.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
onchange: function (e) {
|
|
||||||
var blockA = this.getInputTargetBlock("A");
|
|
||||||
var blockB = this.getInputTargetBlock("B");
|
|
||||||
if (blockA === null && blockB === null) {
|
|
||||||
this.getInput("A").setCheck(null);
|
|
||||||
this.getInput("B").setCheck(null);
|
|
||||||
}
|
}
|
||||||
if (blockA !== null && blockB === null) {
|
]);
|
||||||
this.getInput("A").setCheck(
|
|
||||||
getCompatibleTypes(blockA.outputConnection.check_[0])
|
|
||||||
);
|
|
||||||
this.getInput("B").setCheck(
|
|
||||||
getCompatibleTypes(blockA.outputConnection.check_[0])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (blockB !== null && blockA === null) {
|
|
||||||
this.getInput("B").setCheck(
|
|
||||||
getCompatibleTypes(blockB.outputConnection.check_[0])
|
|
||||||
);
|
|
||||||
this.getInput("A").setCheck(
|
|
||||||
getCompatibleTypes(blockB.outputConnection.check_[0])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["switch_case"] = {
|
|
||||||
init: function () {
|
|
||||||
this.setColour(getColour().logic);
|
|
||||||
this.setPreviousStatement(true);
|
|
||||||
this.setTooltip(Blockly.Msg.cases_tooltip);
|
|
||||||
this.setNextStatement(true);
|
|
||||||
this.appendValueInput("CONDITION").appendField(Blockly.Msg.cases_switch);
|
|
||||||
this.appendValueInput("CASECONDITION0").appendField(
|
|
||||||
Blockly.Msg.cases_condition
|
|
||||||
);
|
|
||||||
this.appendStatementInput("CASE0").appendField(Blockly.Msg.cases_do);
|
|
||||||
this.setMutator(new Blockly.Mutator(["case_incaseof", "case_default"]));
|
|
||||||
this.caseCount_ = 0;
|
|
||||||
this.defaultCount_ = 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
mutationToDom: function () {
|
|
||||||
if (!this.caseCount_ && !this.defaultCount_) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var container = document.createElement("mutation");
|
|
||||||
if (this.caseCount_) {
|
|
||||||
container.setAttribute("case", this.caseCount_);
|
|
||||||
}
|
|
||||||
if (this.defaultCount_) {
|
|
||||||
container.setAttribute("default", 1);
|
|
||||||
}
|
|
||||||
return container;
|
|
||||||
},
|
|
||||||
|
|
||||||
domToMutation: function (xmlElement) {
|
|
||||||
this.caseCount_ = parseInt(xmlElement.getAttribute("case"), 10);
|
|
||||||
this.defaultCount_ = parseInt(xmlElement.getAttribute("default"), 10);
|
|
||||||
for (var x = 0; x <= this.caseCount_; x++) {
|
|
||||||
this.appendValueInput("CASECONDITION" + x).appendField(
|
|
||||||
Blockly.Msg.cases_condition
|
|
||||||
);
|
|
||||||
this.appendStatementInput("CASE" + x).appendField(Blockly.Msg.cases_do);
|
|
||||||
}
|
|
||||||
if (this.defaultCount_) {
|
|
||||||
this.appendStatementInput("ONDEFAULT").appendField("default");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
decompose: function (workspace) {
|
|
||||||
var containerBlock = workspace.newBlock("control_case");
|
|
||||||
containerBlock.initSvg();
|
|
||||||
var connection = containerBlock.getInput("STACK").connection;
|
|
||||||
for (var x = 1; x <= this.caseCount_; x++) {
|
|
||||||
var caseBlock = workspace.newBlock("case_incaseof");
|
|
||||||
caseBlock.initSvg();
|
|
||||||
connection.connect(caseBlock.previousConnection);
|
|
||||||
connection = caseBlock.nextConnection;
|
|
||||||
}
|
|
||||||
if (this.defaultCount_) {
|
|
||||||
var defaultBlock = Blockly.Block.obtain(workspace, "case_default");
|
|
||||||
defaultBlock.initSvg();
|
|
||||||
connection.connect(defaultBlock.previousConnection);
|
|
||||||
}
|
|
||||||
return containerBlock;
|
|
||||||
},
|
|
||||||
|
|
||||||
compose: function (containerBlock) {
|
|
||||||
//Disconnect all input blocks and remove all inputs.
|
|
||||||
if (this.defaultCount_) {
|
|
||||||
this.removeInput("ONDEFAULT");
|
|
||||||
}
|
|
||||||
this.defaultCount_ = 0;
|
|
||||||
for (var x = this.caseCount_; x > 0; x--) {
|
|
||||||
this.removeInput("CASECONDITION" + x);
|
|
||||||
this.removeInput("CASE" + x);
|
|
||||||
}
|
|
||||||
this.caseCount_ = 0;
|
|
||||||
var caseBlock = containerBlock.getInputTargetBlock("STACK");
|
|
||||||
while (caseBlock) {
|
|
||||||
switch (caseBlock.type) {
|
|
||||||
case "case_incaseof":
|
|
||||||
this.caseCount_++;
|
|
||||||
var caseconditionInput = this.appendValueInput(
|
|
||||||
"CASECONDITION" + this.caseCount_
|
|
||||||
).appendField(Blockly.Msg.cases_condition);
|
|
||||||
var caseInput = this.appendStatementInput(
|
|
||||||
"CASE" + this.caseCount_
|
|
||||||
).appendField(Blockly.Msg.cases_do);
|
|
||||||
if (caseBlock.valueConnection_) {
|
|
||||||
caseconditionInput.connection.connect(caseBlock.valueConnection_);
|
|
||||||
}
|
|
||||||
if (caseBlock.statementConnection_) {
|
|
||||||
caseInput.connection.connect(caseBlock.statementConnection_);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "case_default":
|
|
||||||
this.defaultCount_++;
|
|
||||||
var defaultInput =
|
|
||||||
this.appendStatementInput("ONDEFAULT").appendField("default");
|
|
||||||
if (caseBlock.statementConnection_) {
|
|
||||||
defaultInput.connection.connect(caseBlock.statementConnection_);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("Unknown block type.");
|
|
||||||
}
|
|
||||||
caseBlock =
|
|
||||||
caseBlock.nextConnection && caseBlock.nextConnection.targetBlock();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
saveConnections: function (containerBlock) {
|
|
||||||
var caseBlock = containerBlock.getInputTargetBlock("STACK");
|
|
||||||
var x = 1;
|
|
||||||
while (caseBlock) {
|
|
||||||
switch (caseBlock.type) {
|
|
||||||
case "case_incaseof":
|
|
||||||
var caseconditionInput = this.getInput("CASECONDITION" + x);
|
|
||||||
var caseInput = this.getInput("CASE" + x);
|
|
||||||
caseBlock.valueConnection_ =
|
|
||||||
caseconditionInput &&
|
|
||||||
caseconditionInput.connection.targetConnection;
|
|
||||||
caseBlock.statementConnection_ =
|
|
||||||
caseInput && caseInput.connection.targetConnection;
|
|
||||||
x++;
|
|
||||||
break;
|
|
||||||
case "case_default":
|
|
||||||
var defaultInput = this.getInput("ONDEFAULT");
|
|
||||||
caseBlock.satementConnection_ =
|
|
||||||
defaultInput && defaultInput.connection.targetConnection;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("Unknown block type");
|
|
||||||
}
|
|
||||||
caseBlock =
|
|
||||||
caseBlock.nextConnection && caseBlock.nextConnection.targetBlock();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["control_case"] = {
|
|
||||||
init: function () {
|
|
||||||
this.setColour(getColour().logic);
|
|
||||||
this.appendDummyInput().appendField(Blockly.Msg.cases_switch);
|
|
||||||
this.appendStatementInput("STACK");
|
|
||||||
this.setTooltip("--Placeholder--");
|
|
||||||
this.contextMenu = false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["case_incaseof"] = {
|
|
||||||
init: function () {
|
|
||||||
this.setColour(getColour().logic);
|
|
||||||
this.appendDummyInput().appendField(Blockly.Msg.cases_add);
|
|
||||||
this.setPreviousStatement(true);
|
|
||||||
this.setNextStatement(true);
|
|
||||||
this.setTooltip("--Placeholder--");
|
|
||||||
this.contextMenu = false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks["case_default"] = {
|
|
||||||
init: function () {
|
|
||||||
this.setColour(getColour().logic);
|
|
||||||
this.appendValueInput("default").appendField("default");
|
|
||||||
this.setPreviousStatement(true);
|
|
||||||
this.setNextStatement(false);
|
|
||||||
this.setTooltip(
|
|
||||||
"This function will run if there aren't any matching cases."
|
|
||||||
);
|
|
||||||
this.contextMenu = false;
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,213 +1,51 @@
|
|||||||
|
|
||||||
import Blockly from 'blockly';
|
import Blockly from 'blockly';
|
||||||
import { getColour } from '../helpers/colour';
|
|
||||||
import { getCompatibleTypes } from '../helpers/types'
|
|
||||||
import * as Types from '../helpers/types';
|
|
||||||
|
|
||||||
Blockly.Blocks['controls_whileUntil'] = {
|
Blockly.defineBlocksWithJsonArray([
|
||||||
/**
|
{
|
||||||
* Block for 'do while/until' loop.
|
type: 'controls_for',
|
||||||
* @this Blockly.Block
|
message0: 'count with %1 from %2 to %3 by adding %4',
|
||||||
*/
|
args0: [
|
||||||
init: function () {
|
{
|
||||||
var OPERATORS =
|
type: 'field_variable',
|
||||||
[[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_WHILE, 'WHILE'],
|
name: 'VAR',
|
||||||
[Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_UNTIL, 'UNTIL']];
|
variable: null,
|
||||||
this.setHelpUrl(Blockly.Msg.CONTROLS_WHILEUNTIL_HELPURL);
|
variableTypes: ['Number'],
|
||||||
this.setColour(getColour().loops);
|
defaultType: 'Number',
|
||||||
this.appendValueInput('BOOL')
|
createNewVariable: true,
|
||||||
.setCheck(getCompatibleTypes('boolean'))
|
showOnlyVariableAssigned: false,
|
||||||
.appendField(new Blockly.FieldDropdown(OPERATORS), 'MODE');
|
},
|
||||||
this.appendStatementInput('DO')
|
{
|
||||||
.appendField(Blockly.Msg.CONTROLS_WHILEUNTIL_INPUT_DO);
|
type: 'input_value',
|
||||||
this.setPreviousStatement(true);
|
name: 'FROM',
|
||||||
this.setNextStatement(true);
|
check: 'Number',
|
||||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
align: 'RIGHT',
|
||||||
var thisBlock = this;
|
},
|
||||||
this.setTooltip(function () {
|
{
|
||||||
var op = thisBlock.getFieldValue('MODE');
|
type: 'input_value',
|
||||||
var TOOLTIPS = {
|
name: 'TO',
|
||||||
'WHILE': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE,
|
check: 'Number',
|
||||||
'UNTIL': Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL
|
align: 'RIGHT',
|
||||||
};
|
},
|
||||||
return TOOLTIPS[op];
|
{
|
||||||
});
|
type: 'input_value',
|
||||||
}
|
name: 'BY',
|
||||||
};
|
check: 'Number',
|
||||||
|
align: 'RIGHT',
|
||||||
Blockly.Blocks['controls_for'] = {
|
},
|
||||||
/**
|
],
|
||||||
* Block for 'for' loop.
|
message1: '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1',
|
||||||
* @this Blockly.Block
|
args1: [
|
||||||
*/
|
{
|
||||||
init: function () {
|
type: 'input_statement',
|
||||||
this.jsonInit({
|
name: 'DO',
|
||||||
"message0": Blockly.Msg.CONTROLS_FOR_TITLE,
|
},
|
||||||
"args0": [
|
],
|
||||||
{
|
inputsInline: false,
|
||||||
"type": "field_variable",
|
previousStatement: null,
|
||||||
"name": "VAR",
|
nextStatement: null,
|
||||||
"defaultType": Types.NUMBER.typeName,
|
helpUrl: '%{BKY_CONTROLS_FOR_HELPURL}',
|
||||||
"variable": null
|
extensions: ['contextMenu_newGetVariableBlock', 'controls_for_tooltip'],
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "input_value",
|
|
||||||
"name": "FROM",
|
|
||||||
"check": getCompatibleTypes('int'),
|
|
||||||
"align": "RIGHT"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "input_value",
|
|
||||||
"name": "TO",
|
|
||||||
"check": getCompatibleTypes('int'),
|
|
||||||
"align": "RIGHT"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "input_value",
|
|
||||||
"name": "BY",
|
|
||||||
"check": getCompatibleTypes('int'),
|
|
||||||
"align": "RIGHT"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"inputsInline": true,
|
|
||||||
"previousStatement": null,
|
|
||||||
"nextStatement": null,
|
|
||||||
"colour": getColour().loops,
|
|
||||||
"helpUrl": Blockly.Msg.CONTROLS_FOR_HELPURL
|
|
||||||
});
|
|
||||||
this.appendStatementInput('DO')
|
|
||||||
.appendField(Blockly.Msg.CONTROLS_FOR_INPUT_DO);
|
|
||||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
||||||
var thisBlock = this;
|
|
||||||
this.setTooltip(function () {
|
|
||||||
return Blockly.Msg.CONTROLS_FOR_TOOLTIP.replace('%1',
|
|
||||||
thisBlock.getFieldValue('VAR'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks['controls_forEach'] = {
|
|
||||||
/**
|
|
||||||
* Block for 'for each' loop.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.jsonInit({
|
|
||||||
"message0": Blockly.Msg.CONTROLS_FOREACH_TITLE,
|
|
||||||
"args0": [
|
|
||||||
{
|
|
||||||
"type": "field_variable",
|
|
||||||
"name": "VAR",
|
|
||||||
"defaultType": Types.NUMBER.typeName,
|
|
||||||
"variable": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "input_value",
|
|
||||||
"name": "LIST",
|
|
||||||
"check": getCompatibleTypes('Array')
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"previousStatement": null,
|
|
||||||
"nextStatement": null,
|
|
||||||
"colour": getColour().loops,
|
|
||||||
"helpUrl": Blockly.Msg.CONTROLS_FOREACH_HELPURL
|
|
||||||
});
|
|
||||||
this.appendStatementInput('DO')
|
|
||||||
.appendField(Blockly.Msg.CONTROLS_FOREACH_INPUT_DO);
|
|
||||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
||||||
var thisBlock = this;
|
|
||||||
this.setTooltip(function () {
|
|
||||||
return Blockly.Msg.CONTROLS_FOREACH_TOOLTIP.replace('%1',
|
|
||||||
thisBlock.getFieldValue('VAR'));
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
customContextMenu: Blockly.Blocks['controls_for'].customContextMenu,
|
]);
|
||||||
/** @returns {!string} The type of the variable used in this block */
|
|
||||||
getVarType: function (varName) {
|
|
||||||
return Blockly.Types.NUMBER;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks['controls_flow_statements'] = {
|
|
||||||
/**
|
|
||||||
* Block for flow statements: continue, break.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
var OPERATORS =
|
|
||||||
[[Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK, 'BREAK'],
|
|
||||||
[Blockly.Msg.CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE, 'CONTINUE']];
|
|
||||||
this.setHelpUrl(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_HELPURL);
|
|
||||||
this.setColour(getColour().loops);
|
|
||||||
this.appendDummyInput()
|
|
||||||
.appendField(new Blockly.FieldDropdown(OPERATORS), 'FLOW');
|
|
||||||
this.setPreviousStatement(true);
|
|
||||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
||||||
var thisBlock = this;
|
|
||||||
this.setTooltip(function () {
|
|
||||||
var op = thisBlock.getFieldValue('FLOW');
|
|
||||||
var TOOLTIPS = {
|
|
||||||
'BREAK': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK,
|
|
||||||
'CONTINUE': Blockly.Msg.CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE
|
|
||||||
};
|
|
||||||
return TOOLTIPS[op];
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Called whenever anything on the workspace changes.
|
|
||||||
* Add warning if this flow block is not nested inside a loop.
|
|
||||||
* @param {!Blockly.Events.Abstract} e Change event.
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
onchange: function (e) {
|
|
||||||
var legal = false;
|
|
||||||
// Is the block nested in a loop?
|
|
||||||
var block = this;
|
|
||||||
do {
|
|
||||||
if (this.LOOP_TYPES.indexOf(block.type) !== -1) {
|
|
||||||
legal = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
block = block.getSurroundParent();
|
|
||||||
} while (block);
|
|
||||||
if (legal) {
|
|
||||||
this.setWarningText(null);
|
|
||||||
} else {
|
|
||||||
this.setWarningText(Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* List of block types that are loops and thus do not need warnings.
|
|
||||||
* To add a new loop type add this to your code:
|
|
||||||
* Blockly.Blocks['controls_flow_statements'].LOOP_TYPES.push('custom_loop');
|
|
||||||
*/
|
|
||||||
LOOP_TYPES: ['controls_repeat', 'controls_repeat_ext', 'controls_forEach',
|
|
||||||
'controls_for', 'controls_whileUntil']
|
|
||||||
};
|
|
||||||
|
|
||||||
Blockly.Blocks['controls_repeat_ext'] = {
|
|
||||||
/**
|
|
||||||
* Block for repeat n times (external number).
|
|
||||||
* @this Blockly.Block
|
|
||||||
*/
|
|
||||||
init: function () {
|
|
||||||
this.jsonInit({
|
|
||||||
"message0": Blockly.Msg.CONTROLS_REPEAT_TITLE,
|
|
||||||
"args0": [
|
|
||||||
{
|
|
||||||
"type": "input_value",
|
|
||||||
"name": "TIMES",
|
|
||||||
"check": getCompatibleTypes('int'),
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"previousStatement": null,
|
|
||||||
"nextStatement": null,
|
|
||||||
"colour": getColour().loops,
|
|
||||||
"tooltip": Blockly.Msg.CONTROLS_REPEAT_TOOLTIP,
|
|
||||||
"helpUrl": Blockly.Msg.CONTROLS_REPEAT_HELPURL
|
|
||||||
});
|
|
||||||
this.appendStatementInput('DO')
|
|
||||||
.appendField(Blockly.Msg.CONTROLS_REPEAT_INPUT_DO);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|