diff --git a/.gitattributes b/.gitattributes index 401887acf..58abc7474 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,3 +4,5 @@ eventemitter2.js linguist-vendored ros3d.js linguist-vendored three.min.js linguist-vendored aruco_pose/vendor/* linguist-vendored +blockly/* linguist-vendored +highlight/* linguist-vendored diff --git a/.gitignore b/.gitignore index c502f90f9..a50caecca 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ node_modules/ _book/ package-lock.json +clover_blocks/programs/*.* +!clover_blocks/programs/examples/* diff --git a/.travis.yml b/.travis.yml index 5ba46df94..1ed7862cf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -107,7 +107,7 @@ jobs: - wget https://github.com/okalachev/editorconfig-checker/releases/download/1.2.1-disable-spaces-amount/ec-linux-amd64 - chmod +x ec-linux-amd64 script: - - ./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae" + - ./ec-linux-amd64 -spaces-after-tabs -e "roslib.js|ros3d.js|eventemitter2.js|draw.cpp|BinUtils.swift|\.idea|apps/android/app|blockly/|clover_blocks/programs/|highlight/|python.js|Assets.xcassets|test_parser_pass.txt|test_node_failure.txt|aruco_pose/vendor|\.stl|\.dxf|\.dae" stages: - Build # More info there diff --git a/book.json b/book.json index 367115285..0cb02fde9 100644 --- a/book.json +++ b/book.json @@ -9,6 +9,7 @@ "richquotes@https://github.com/okalachev/gitbook-plugin-richquotes.git", "yametrika", "anchors", + "collapsible-menu", "validate-links", "bulk-redirect@https://github.com/okalachev/gitbook-plugin-bulk-redirect.git", "sitemap@https://github.com/okalachev/plugin-sitemap.git", diff --git a/clover/launch/clover.launch b/clover/launch/clover.launch index 196490bc5..a3e3ba8eb 100644 --- a/clover/launch/clover.launch +++ b/clover/launch/clover.launch @@ -10,6 +10,7 @@ + @@ -81,6 +82,9 @@ + + + diff --git a/clover/src/simple_offboard.cpp b/clover/src/simple_offboard.cpp index bc926b5a2..13534666c 100644 --- a/clover/src/simple_offboard.cpp +++ b/clover/src/simple_offboard.cpp @@ -46,6 +46,7 @@ #include using std::string; +using std::isnan; using namespace geometry_msgs; using namespace sensor_msgs; using namespace clover; @@ -507,25 +508,84 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl if (busy) throw std::runtime_error("Busy"); - ENSURE_FINITE(x); - ENSURE_FINITE(y); - ENSURE_FINITE(z); - ENSURE_FINITE(vx); - ENSURE_FINITE(vy); - ENSURE_FINITE(vz); - ENSURE_FINITE(pitch); - ENSURE_FINITE(roll); - ENSURE_FINITE(pitch_rate); - ENSURE_FINITE(roll_rate); - ENSURE_FINITE(lat); - ENSURE_FINITE(lon); - ENSURE_FINITE(thrust); - busy = true; // Checks checkState(); + // default frame is local frame + if (frame_id.empty()) + frame_id = local_frame; + + // look up for reference frame + auto search = reference_frames.find(frame_id); + const string& reference_frame = search == reference_frames.end() ? frame_id : search->second; + + // Serve "partial" commands + + if (!auto_arm && std::isfinite(yaw) && + isnan(x) && isnan(y) && isnan(z) && isnan(vx) && isnan(vy) && isnan(vz) && + isnan(pitch) && isnan(roll) && isnan(thrust) && + isnan(lat) && isnan(lon)) { + // change only the yaw + if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == VELOCITY) { + if (!waitTransform(setpoint_position.header.frame_id, frame_id, stamp, transform_timeout)) + throw std::runtime_error("Can't transform from " + frame_id + " to " + setpoint_position.header.frame_id); + + message = "Changing yaw only"; + + QuaternionStamped q; + q.header.frame_id = frame_id; + q.header.stamp = stamp; + q.quaternion = tf::createQuaternionMsgFromYaw(yaw); // TODO: pitch=0, roll=0 is not totally correct + setpoint_position.pose.orientation = tf_buffer.transform(q, setpoint_position.header.frame_id).quaternion; + setpoint_yaw_type = YAW; + goto publish_setpoint; + } else { + throw std::runtime_error("Setting yaw is possible only when position or velocity setpoints active"); + } + } + + if (!auto_arm && std::isfinite(yaw_rate) && + isnan(x) && isnan(y) && isnan(z) && isnan(vx) && isnan(vy) && isnan(vz) && + isnan(pitch) && isnan(roll) && isnan(yaw) && isnan(thrust) && + isnan(lat) && isnan(lon)) { + // change only the yaw rate + if (setpoint_type == POSITION || setpoint_type == NAVIGATE || setpoint_type == NAVIGATE_GLOBAL || setpoint_type == VELOCITY) { + message = "Changing yaw rate only"; + + setpoint_yaw_type = YAW_RATE; + setpoint_yaw_rate = yaw_rate; + goto publish_setpoint; + } else { + throw std::runtime_error("Setting yaw rate is possible only when position or velocity setpoints active"); + } + } + + // Serve normal commands + + if (sp_type == NAVIGATE || sp_type == POSITION) { + ENSURE_FINITE(x); + ENSURE_FINITE(y); + ENSURE_FINITE(z); + } else if (sp_type == NAVIGATE_GLOBAL) { + ENSURE_FINITE(lat); + ENSURE_FINITE(lon); + ENSURE_FINITE(z); + } else if (sp_type == VELOCITY) { + ENSURE_FINITE(vx); + ENSURE_FINITE(vy); + ENSURE_FINITE(vz); + } else if (sp_type == ATTITUDE) { + ENSURE_FINITE(pitch); + ENSURE_FINITE(roll); + ENSURE_FINITE(thrust); + } else if (sp_type == RATES) { + ENSURE_FINITE(pitch_rate); + ENSURE_FINITE(roll_rate); + ENSURE_FINITE(thrust); + } + if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL) { if (TIMEOUT(local_position, local_position_timeout)) throw std::runtime_error("No local position, check settings"); @@ -550,14 +610,6 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl throw std::runtime_error("No global position"); } - // default frame is local frame - if (frame_id.empty()) - frame_id = local_frame; - - // look up for reference frame - auto search = reference_frames.find(frame_id); - const string& reference_frame = search == reference_frames.end() ? frame_id : search->second; - if (sp_type == NAVIGATE || sp_type == NAVIGATE_GLOBAL || sp_type == POSITION || sp_type == VELOCITY || sp_type == ATTITUDE) { // make sure transform from frame_id to reference frame available if (!waitTransform(reference_frame, frame_id, stamp, transform_timeout)) @@ -625,7 +677,7 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl } else { setpoint_yaw_type = YAW; setpoint_yaw_rate = 0; - ps.pose.orientation = tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw); + ps.pose.orientation = tf::createQuaternionMsgFromYaw(yaw); } tf_buffer.transform(ps, setpoint_position, reference_frame); @@ -653,6 +705,7 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl wait_armed = auto_arm; +publish_setpoint: publish(stamp); // calculate initial transformed messages first setpoint_timer.start(); @@ -693,27 +746,27 @@ bool serve(enum setpoint_type_t sp_type, float x, float y, float z, float vx, fl } bool navigate(Navigate::Request& req, Navigate::Response& res) { - return serve(NAVIGATE, req.x, req.y, req.z, 0, 0, 0, 0, 0, req.yaw, 0, 0, req.yaw_rate, 0, 0, 0, req.speed, req.frame_id, req.auto_arm, res.success, res.message); + return serve(NAVIGATE, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message); } bool navigateGlobal(NavigateGlobal::Request& req, NavigateGlobal::Response& res) { - return serve(NAVIGATE_GLOBAL, 0, 0, req.z, 0, 0, 0, 0, 0, req.yaw, 0, 0, req.yaw_rate, req.lat, req.lon, 0, req.speed, req.frame_id, req.auto_arm, res.success, res.message); + return serve(NAVIGATE_GLOBAL, NAN, NAN, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, req.lat, req.lon, NAN, req.speed, req.frame_id, req.auto_arm, res.success, res.message); } bool setPosition(SetPosition::Request& req, SetPosition::Response& res) { - return serve(POSITION, req.x, req.y, req.z, 0, 0, 0, 0, 0, req.yaw, 0, 0, req.yaw_rate, 0, 0, 0, 0, req.frame_id, req.auto_arm, res.success, res.message); + return serve(POSITION, req.x, req.y, req.z, NAN, NAN, NAN, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message); } bool setVelocity(SetVelocity::Request& req, SetVelocity::Response& res) { - return serve(VELOCITY, 0, 0, 0, req.vx, req.vy, req.vz, 0, 0, req.yaw, 0, 0, req.yaw_rate, 0, 0, 0, 0, req.frame_id, req.auto_arm, res.success, res.message); + return serve(VELOCITY, NAN, NAN, NAN, req.vx, req.vy, req.vz, NAN, NAN, req.yaw, NAN, NAN, req.yaw_rate, NAN, NAN, NAN, NAN, req.frame_id, req.auto_arm, res.success, res.message); } bool setAttitude(SetAttitude::Request& req, SetAttitude::Response& res) { - return serve(ATTITUDE, 0, 0, 0, 0, 0, 0, req.pitch, req.roll, req.yaw, 0, 0, 0, 0, 0, req.thrust, 0, req.frame_id, req.auto_arm, res.success, res.message); + return serve(ATTITUDE, NAN, NAN, NAN, NAN, NAN, NAN, req.pitch, req.roll, req.yaw, NAN, NAN, NAN, NAN, NAN, req.thrust, NAN, req.frame_id, req.auto_arm, res.success, res.message); } bool setRates(SetRates::Request& req, SetRates::Response& res) { - return serve(RATES, 0, 0, 0, 0, 0, 0, 0, 0, 0, req.pitch_rate, req.roll_rate, req.yaw_rate, 0, 0, req.thrust, 0, "", req.auto_arm, res.success, res.message); + return serve(RATES, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, req.pitch_rate, req.roll_rate, req.yaw_rate, NAN, NAN, req.thrust, NAN, "", req.auto_arm, res.success, res.message); } bool land(std_srvs::Trigger::Request& req, std_srvs::Trigger::Response& res) diff --git a/clover/www/index.html b/clover/www/index.html index 2234aefcd..fe0a6eaa3 100644 --- a/clover/www/index.html +++ b/clover/www/index.html @@ -6,6 +6,7 @@

Clover Drone Kit Tools

  • Open web terminal (Butterfly)
  • View 3D visualization (ros3djs)
  • 3D visualization for markers map (ros3djs)
  • +
  • Blocks programming (Blockly)
  • diff --git a/clover_blocks/CMakeLists.txt b/clover_blocks/CMakeLists.txt new file mode 100644 index 000000000..437e4cf4d --- /dev/null +++ b/clover_blocks/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 2.8.3) +project(clover_blocks) + +find_package(catkin REQUIRED COMPONENTS message_generation) + +add_message_files( + FILES + Prompt.msg +) + +add_service_files( + FILES + Run.srv + Load.srv + Store.srv +) + +generate_messages( + DEPENDENCIES + # std_msgs # Or other packages containing msgs +) + +catkin_package( +# INCLUDE_DIRS include +# LIBRARIES roslaunch_editor + CATKIN_DEPENDS message_runtime +# DEPENDS system_lib +) + +############# +## Install ## +############# + +# all install targets should use catkin DESTINATION variables +# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html + +## Mark executable scripts (Python etc.) for installation +## in contrast to setup.py, you can choose the destination +# install(PROGRAMS +# scripts/my_python_script +# DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +# ) + +## Mark executables for installation +## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html +# install(TARGETS ${PROJECT_NAME}_node +# RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +# ) + +## Mark libraries for installation +## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html +# install(TARGETS ${PROJECT_NAME} +# ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} +# RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} +# ) + +## Mark cpp header files for installation +# install(DIRECTORY include/${PROJECT_NAME}/ +# DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} +# FILES_MATCHING PATTERN "*.h" +# PATTERN ".svn" EXCLUDE +# ) + +## Mark other files for installation (e.g. launch and bag files, etc.) +# install(FILES +# # myfile1 +# # myfile2 +# DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} +# ) + +catkin_install_python(PROGRAMS src/clover_blocks + DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +) + +############# +## Testing ## +############# + +## Add gtest based cpp test target and link libraries +# catkin_add_gtest(${PROJECT_NAME}-test test/test_roslaunch_editor.cpp) +# if(TARGET ${PROJECT_NAME}-test) +# target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}) +# endif() + +## Add folders to be run by python nosetests +# catkin_add_nosetests(test) diff --git a/clover_blocks/README.md b/clover_blocks/README.md new file mode 100644 index 000000000..7dbca3ca2 --- /dev/null +++ b/clover_blocks/README.md @@ -0,0 +1,52 @@ +# clover_blocks + +Blockly programming support for Clover. + + + +See user documentation at the [main Clover documentation site](https://clover.coex.tech/en/blocks.html). + +Internal package documentation is given below. + +## Frontend + +The frontend files are located in [`www`](./www/) subdirectory. The frontend application uses [`roblib.js`](http://wiki.ros.org/roslibjs) library for communicating with backend node and other ROS resources. + +## `clover_blocks` node + +`clover_blocks` is the blocks programming backend, implementing all the services and topics needed for running Blockly-generated Python script. + +### Services + +* `~run` ([*clover_blocks/Run*](srv/Run.srv)) – run Blockly-generated program (in Python). +* `~stop` ([*std_srvs/Trigger*](http://docs.ros.org/melodic/api/std_srvs/html/srv/Trigger.html)) – terminate the running program. +* `~store` ([*clover_blocks/load*](srv/Store.srv)) – store a user program (to `/programs` by default). +* `~load` ([*clover_blocks/load*](srv/Load.srv)) – load all the stored programs. + +### Parameters + +* `~programs_dir` (*string*) – directory for user programs. + +Parameters read by frontend: + +* `~navigate_tolerance` (*float*) – distance tolerance in meters, used for navigate-like blocks (default: 0.2). +* `~sleep_time` (*float*) – duration of sleep in loop cycles, used for navigate-like blocks (default: 0.2). + +These parameters also can be set as URL GET-parameters, for example: + +``` +http:///clover_blocks/?navigate_tolerance=0.5&sleep_time=0.1 +``` + +### Topics + +#### Published + +* `~running` ([*std_msgs/Bool*](http://docs.ros.org/melodic/api/std_msgs/html/msg/Bool.html)) – indicates if the program is currently running. +* `~block` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html)) – current executing block (maximum topic rate is limited). +* `~error` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html)) – user program errors and exceptions. +* `~prompt` ([*clover_blocks/Prompt*](msg/Prompt.msg)) – user input request (includes random request ID string). + +This topic is published from the frontend side: + +* `~prompt/` ([*std_msgs/String*](http://docs.ros.org/melodic/api/std_msgs/html/msg/String.html)) – user input response. diff --git a/clover_blocks/msg/Prompt.msg b/clover_blocks/msg/Prompt.msg new file mode 100644 index 000000000..8780a54c9 --- /dev/null +++ b/clover_blocks/msg/Prompt.msg @@ -0,0 +1,2 @@ +string message # message for prompt +string id # user response should be published to ~input/ topic diff --git a/clover_blocks/package.xml b/clover_blocks/package.xml new file mode 100644 index 000000000..4dbecbd96 --- /dev/null +++ b/clover_blocks/package.xml @@ -0,0 +1,48 @@ + + + clover_blocks + 0.0.0 + Blockly programming support for Clover + Oleg Kalachev + MIT + + + + https://github.com/CopterExpress/clover + + + + Oleg Kalachev + + + + + + + + + + + + + + + + + + + + + + catkin + + message_generation + message_runtime + rospy + + + + + + + diff --git a/clover_blocks/programs/examples/aruco-map.xml b/clover_blocks/programs/examples/aruco-map.xml new file mode 100644 index 000000000..b4925a420 --- /dev/null +++ b/clover_blocks/programs/examples/aruco-map.xml @@ -0,0 +1,73 @@ + + + TRUE + + + 1.5 + + + + + + + 10 + + + + + ARUCO_MAP + TRUE + + + 0 + + + + NUMBER + + + Enter X + + + + + + + 0 + + + + NUMBER + + + Enter Y + + + + + + + 1.5 + + + + + 0 + + + + + 0.5 + + + + + + + TRUE + + + + + + diff --git a/clover_blocks/programs/examples/aruco-marker.xml b/clover_blocks/programs/examples/aruco-marker.xml new file mode 100644 index 000000000..3875911ac --- /dev/null +++ b/clover_blocks/programs/examples/aruco-marker.xml @@ -0,0 +1,64 @@ + + + TRUE + + + 1.5 + + + + + + + 3 + + + + + ARUCO + TRUE + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + NUMBER + + + Enter marker ID + + + + + + + 0.5 + + + + + + + TRUE + + + + + + \ No newline at end of file diff --git a/clover_blocks/programs/examples/flight-led.xml b/clover_blocks/programs/examples/flight-led.xml new file mode 100644 index 000000000..0fa65d45c --- /dev/null +++ b/clover_blocks/programs/examples/flight-led.xml @@ -0,0 +1,97 @@ + + + TRUE + + + 1 + + + + + FILL + + + #ff0000 + + + + + BODY + TRUE + + + 1 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0.5 + + + + + FILL + + + #3333ff + + + + + BODY + TRUE + + + -1 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0.5 + + + + + TRUE + + + + + + + + + + + + \ No newline at end of file diff --git a/clover_blocks/programs/examples/flip.xml b/clover_blocks/programs/examples/flip.xml new file mode 100644 index 000000000..503418413 --- /dev/null +++ b/clover_blocks/programs/examples/flip.xml @@ -0,0 +1,308 @@ + + + start_x + start_y + start_z + + + flip + Describe this function... + + + + + + RATES + BODY + bump up + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 1 + + + + + 0 + + + + + + + 0.2 + + + + + RATES + BODY + maximum pitch rate + + + 0 + + + + + 0 + + + + + 0 + + + + + 30 + + + + + 0 + + + + + 0 + + + + + 0.2 + + + + + 0 + + + + + WHILE + + + TRUE + + + + + + + OR + + + GT + + + ABS + + + 9 + + + PITCH + + + + + + + 90 + + + + + + + GT + + + ABS + + + 9 + + + ROLL + + + + + + + 90 + + + + + + + + + BREAK + + + + + + + + + + + + + + + + + + + + + + memorize position + Describe this function... + + + start_x + + + X + MAP + + + 0 + + + + + + + start_y + + + Y + MAP + + + 0 + + + + + + + start_z + + + Z + MAP + + + 0 + + + + + + + + + + + + + navigate to memorized position + Describe this function... + + + MAP + TRUE + + + 0 + + + start_x + + + + + 0 + + + start_y + + + + + 0 + + + start_z + + + + + 0 + + + + + 5 + + + + + + + TRUE + + + 2 + + + + + + + + TRUE + + + + + + \ No newline at end of file diff --git a/clover_blocks/programs/examples/functions.xml b/clover_blocks/programs/examples/functions.xml new file mode 100644 index 000000000..6f5ddb694 --- /dev/null +++ b/clover_blocks/programs/examples/functions.xml @@ -0,0 +1,184 @@ + + + start_x + start_y + start_z + + + memorize position + Describe this function... + + + start_x + + + X + MAP + + + 0 + + + + + + + start_y + + + Y + MAP + + + 0 + + + + + + + start_z + + + Z + MAP + + + 0 + + + + + + + + + + + + + TRUE + + + 2 + + + + + + + + ARUCO_MAP + TRUE + + + 0 + + + + NUMBER + + + Input X + + + + + + + 0 + + + + NUMBER + + + Input Y + + + + + + + 2 + + + + + 0 + + + + + 0.5 + + + + + + + Point reached. Going back. + + + + + + + + TRUE + + + + + + + + + + + + + navigate to memorized position + Describe this function... + + + BODY + TRUE + + + 0 + + + start_x + + + + + 0 + + + start_y + + + + + 0 + + + start_z + + + + + 0 + + + + + 0.5 + + + + + + \ No newline at end of file diff --git a/clover_blocks/programs/examples/led-compass.xml b/clover_blocks/programs/examples/led-compass.xml new file mode 100644 index 000000000..a6c48cc34 --- /dev/null +++ b/clover_blocks/programs/examples/led-compass.xml @@ -0,0 +1,106 @@ + + + led_count + + + led_count + + + 58 + + + + + WHILE + + + TRUE + + + + + FILL + + + #000000 + + + + + + + 0 + + + MULTIPLY + + + 1 + + + DIVIDE + + + 1 + + + ADD + + + 1 + + + MAP + + + 0 + + + + + + + 180 + + + + + + + 360 + + + + + + + 1 + + + led_count + + + + + + + #3366ff + + + + + + + 0.1 + + + + + + + + + + + + diff --git a/clover_blocks/programs/examples/led.xml b/clover_blocks/programs/examples/led.xml new file mode 100644 index 000000000..1ac9d8a07 --- /dev/null +++ b/clover_blocks/programs/examples/led.xml @@ -0,0 +1,30 @@ + + + WHILE + + + TRUE + + + + + WIPE + + + #ff0000 + + + + + + + + 1 + + + + + + + + \ No newline at end of file diff --git a/clover_blocks/programs/examples/print-range.xml b/clover_blocks/programs/examples/print-range.xml new file mode 100644 index 000000000..de55e19da --- /dev/null +++ b/clover_blocks/programs/examples/print-range.xml @@ -0,0 +1,29 @@ + + + WHILE + + + TRUE + + + + + + + abc + + + + + + + + 0.5 + + + + + + + + \ No newline at end of file diff --git a/clover_blocks/programs/examples/rotation.xml b/clover_blocks/programs/examples/rotation.xml new file mode 100644 index 000000000..2520c3315 --- /dev/null +++ b/clover_blocks/programs/examples/rotation.xml @@ -0,0 +1,88 @@ + + + TRUE + + + 1 + + + + + BODY + TRUE + + + 1 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0.5 + + + + + body + TRUE + + + 180 + + + + + BODY + TRUE + + + 1 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0.5 + + + + + TRUE + + + + + + + + + + \ No newline at end of file diff --git a/clover_blocks/programs/examples/simple-flight.xml b/clover_blocks/programs/examples/simple-flight.xml new file mode 100644 index 000000000..2e6436d68 --- /dev/null +++ b/clover_blocks/programs/examples/simple-flight.xml @@ -0,0 +1,86 @@ + + + TRUE + + + 1 + + + + NUMBER + + + Enter flight altitude + + + + + + + BODY + TRUE + + + 1 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0.5 + + + + + BODY + TRUE + + + -1 + + + + + 0 + + + + + 0 + + + + + 0 + + + + + 0.5 + + + + + TRUE + + + + + + + + \ No newline at end of file diff --git a/clover_blocks/programs/examples/takeoff-land.xml b/clover_blocks/programs/examples/takeoff-land.xml new file mode 100644 index 000000000..1c4472cf4 --- /dev/null +++ b/clover_blocks/programs/examples/takeoff-land.xml @@ -0,0 +1,60 @@ + + + + + Start mission + + + + + TRUE + + + 1 + + + + + + + Take off complete, wait + + + + + + + 3 + + + + + + + Land + + + + + TRUE + + + + + Mission finished + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/clover_blocks/programs/examples/variables.xml b/clover_blocks/programs/examples/variables.xml new file mode 100644 index 000000000..aa468b4c7 --- /dev/null +++ b/clover_blocks/programs/examples/variables.xml @@ -0,0 +1,137 @@ + + + start_x + start_y + + + TRUE + + + 1.5 + + + + + start_x + + + X + ARUCO_MAP + + + 0 + + + + + + + start_y + + + Y + ARUCO_MAP + + + 0 + + + + + + + + + Fly to point 0, 0 + + + + + ARUCO_MAP + TRUE + + + 0 + + + + + 0 + + + + + 1.5 + + + + + 0 + + + + + 0.5 + + + + + + + Fly to initial point + + + + + ARUCO_MAP + TRUE + + + 0 + + + start_x + + + + + 0 + + + start_y + + + + + 1.5 + + + + + 0 + + + + + 0.5 + + + + + TRUE + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/clover_blocks/screenshot.png b/clover_blocks/screenshot.png new file mode 100644 index 000000000..b97d19528 Binary files /dev/null and b/clover_blocks/screenshot.png differ diff --git a/clover_blocks/src/clover_blocks b/clover_blocks/src/clover_blocks new file mode 100755 index 000000000..6b7df118f --- /dev/null +++ b/clover_blocks/src/clover_blocks @@ -0,0 +1,176 @@ +#!/usr/bin/env python + +from __future__ import print_function + +import rospy +import os +import threading +import re +import uuid +from std_msgs.msg import Bool, String +from std_srvs.srv import Trigger +from clover_blocks.msg import Prompt +from clover_blocks.srv import Run, Load, Store + + +rospy.init_node('clover_blocks') + +stop = None +block = '' +published_block = None +running_lock = threading.Lock() + +running_pub = rospy.Publisher('~running', Bool, queue_size=1, latch=True) +block_pub = rospy.Publisher('~block', String, queue_size=1, latch=True) +print_pub = rospy.Publisher('~print', String, queue_size=10) +prompt_pub = rospy.Publisher('~prompt', Prompt, queue_size=10) +error_pub = rospy.Publisher('~error', String, queue_size=10) + + +running_pub.publish(False) + + +class Stop(Exception): + pass + + +def publish_block(event): + global published_block, block + if published_block != block: + block_pub.publish(block) + published_block = block + + +rospy.Timer(rospy.Duration(rospy.get_param('block_rate', 0.2)), publish_block) + + +def change_block(_block): + global block + block = _block + if stop: raise Stop + + +rospy_sleep = rospy.sleep + + +def sleep(duration): + time_start = rospy.get_time() + + if isinstance(duration, rospy.Duration): + duration = duration.to_sec() + + time_until = time_start + duration + + while not rospy.is_shutdown(): + if stop: raise Stop # check stop condition every half-second + if (time_until - rospy.get_time()) > 0.5: + print('sleep 0.5') + rospy_sleep(0.5) + else: + rospy_sleep(time_until - rospy.get_time()) + return + + +rospy.sleep = sleep +rospy.init_node = lambda *args, **kwargs: None + + +def _print(s): + rospy.loginfo(str(s)) + print_pub.publish(str(s)) + + +def _input(s): + rospy.loginfo('Input with message %s', s) + prompt_id = str(uuid.uuid4()).replace('-', '') + prompt_pub.publish(message=str(s), id=prompt_id) + return rospy.wait_for_message('~input/' + prompt_id, String, timeout=30).data; + + +def run(req): + if not running_lock.acquire(False): + return {'message': 'Already running'} + + try: + rospy.loginfo('Run program') + running_pub.publish(True) + + def program_thread(): + global stop + stop = False + g = {'rospy': rospy, + '_b': change_block, + 'print': _print, + 'raw_input': _input} + try: + exec req.code in g + except Stop: + rospy.loginfo('Program forced to stop') + except Exception as e: + rospy.logerr(str(e)) + error_pub.publish(str(e)) + + rospy.loginfo('Program terminated') + running_lock.release() + running_pub.publish(False) + change_block('') + + t = threading.Thread(target=program_thread) + t.start() + + return {'success': True} + + except Exception as e: + running_lock.release() + return {'message': str(e)} + + +def stop(req): + global stop + rospy.loginfo('Stop program') + stop = True + return {'success': True} + + +programs_path = rospy.get_param('~programs_dir', os.path.dirname(os.path.abspath(__file__)) + '/../programs') + + +def load(req): + res = {'names': [], 'programs': [], 'success': True} + try: + for currentpath, folders, files in os.walk(programs_path): + for f in files: + if not f.endswith('.xml'): + continue + filename = os.path.join(currentpath, f) + res['names'].append(os.path.relpath(filename, programs_path)) + res['programs'].append(open(filename, 'r').read()) + return res + except Exception as e: + rospy.logerr(e) + return {'names': [], 'programs': [], 'message': str(e)} + + +name_regexp = re.compile(r'^[a-zA-Z-_.]{0,20}$') + +def store(req): + if not name_regexp.match(req.name): + return {'message': 'Bad program name'} + + filename = os.path.abspath(os.path.join(programs_path, req.name)) + try: + open(filename, 'w').write(req.program) + return {'success': True, 'message': 'Stored to ' + filename} + except Exception as e: + rospy.logerr(e) + return {'names': [], 'programs': [], 'message': str(e)} + + +rospy.Service('~run', Run, run) +rospy.Service('~stop', Trigger, stop) +rospy.Service('~load', Load, load) +rospy.Service('~store', Store, store) + + +rospy.loginfo('Ready') +rospy.spin() diff --git a/clover_blocks/srv/Load.srv b/clover_blocks/srv/Load.srv new file mode 100644 index 000000000..83216015e --- /dev/null +++ b/clover_blocks/srv/Load.srv @@ -0,0 +1,5 @@ +--- +bool success +string message +string[] names +string[] programs diff --git a/clover_blocks/srv/Run.srv b/clover_blocks/srv/Run.srv new file mode 100644 index 000000000..60d6b9d06 --- /dev/null +++ b/clover_blocks/srv/Run.srv @@ -0,0 +1,4 @@ +string code # code in Python +--- +bool success +string message diff --git a/clover_blocks/srv/Store.srv b/clover_blocks/srv/Store.srv new file mode 100644 index 000000000..876a0d7a0 --- /dev/null +++ b/clover_blocks/srv/Store.srv @@ -0,0 +1,5 @@ +string name +string program +--- +bool success +string message diff --git a/clover_blocks/www/blockly/LICENSE b/clover_blocks/www/blockly/LICENSE new file mode 100755 index 000000000..d64569567 --- /dev/null +++ b/clover_blocks/www/blockly/LICENSE @@ -0,0 +1,202 @@ + + 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. diff --git a/clover_blocks/www/blockly/README.md b/clover_blocks/www/blockly/README.md new file mode 100755 index 000000000..4897fa84a --- /dev/null +++ b/clover_blocks/www/blockly/README.md @@ -0,0 +1,60 @@ +# Blockly [![Build Status]( https://travis-ci.org/google/blockly.svg?branch=master)](https://travis-ci.org/google/blockly) + + +Google's Blockly is a web-based, visual programming editor. Users can drag +blocks together to build programs. All code is free and open source. + +**The project page is https://developers.google.com/blockly/** + +![](https://developers.google.com/blockly/images/sample.png) + +Blockly has an active [developer forum](https://groups.google.com/forum/#!forum/blockly). Please drop by and say hello. Show us your prototypes early; collectively we have a lot of experience and can offer hints which will save you time. We actively monitor the forums and typically respond to questions within 2 working days. + +Help us focus our development efforts by telling us [what you are doing with +Blockly](https://developers.google.com/blockly/registration). The questionnaire only takes +a few minutes and will help us better support the Blockly community. + +Cross-browser Testing Platform and Open Source <3 Provided by [Sauce Labs](https://saucelabs.com) + +We support IE11 and test it using [BrowserStack](https://browserstack.com) + +Want to contribute? Great! First, read [our guidelines for contributors](https://developers.google.com/blockly/guides/modify/contributing). + +## Releases + +The next major release will be **June 26th, 2020**. + +We release by pushing the latest code to the master branch, followed by updating our [docs](https://developers.google.com/blockly) and [demo pages](https://blockly-demo.appspot.com). We typically release a new version of Blockly once a quarter (every 3 months). If there are breaking bugs, such as a crash when performing a standard action or a rendering issue that makes Blockly unusable, we will cherry-pick fixes to master between releases to fix them. The [releases page](https://github.com/google/blockly/releases) has a list of all releases. + +Releases are tagged by the release date (YYYYMMDD) with a leading '2.' and a trailing '.0' in case we ever need a major or minor version (such as [2.20190722.1](https://github.com/google/blockly/tree/2.20190722.1)). If you're using npm, you can install the ``blockly`` package on npm: +```bash +npm install blockly +``` + +### New APIs + +Once a new API is merged into master it is considered beta until the following release. We generally try to avoid changing an API after it has been merged to master, but sometimes we need to make changes after seeing how an API is used. If an API has been around for at least two releases we'll do our best to avoid breaking it. + +Unreleased APIs may change radically. Anything that is in `develop` but not `master` is subject to change without warning. + +### Branches + +There are two main branches for Blockly. + +**[master](https://github.com/google/blockly)** - This is the (mostly) stable current release of Blockly. + +**[develop](https://github.com/google/blockly/tree/develop)** - This is where most of our work happens. Pull requests should always be made against develop. This branch will generally be usable, but may be less stable than the master branch. Once something is in develop we expect it to merge to master in the next release. + +**other branches:** - Larger changes may have their own branches until they are good enough for people to try out. These will be developed separately until we think they are almost ready for release. These branches typically get merged into develop immediately after a release to allow extra time for testing. + +## Issues and Milestones + +We typically triage all bugs within 2 working days, which includes adding any appropriate labels and assigning it to a milestone. Please keep in mind, we are a small team so even feature requests that everyone agrees on may not be prioritized. + +### Milestones + +**Upcoming release** - The upcoming release milestone is for all bugs we plan on fixing before the next release. This typically has the form of `year_quarter_release` (such as `2019_q2_release`). Some bugs will be added to this release when they are triaged, others may be added closer to a release. + +**Bug Bash Backlog** - These are bugs that we're still prioritizing. They haven't been added to a specific release yet, but we'll consider them for each release depending on relative priority and available time. + +**Icebox** - These are bugs that we do not intend to spend time on. They are either too much work or minor enough that we don't expect them to ever take priority. We are still happy to accept pull requests for these bugs. diff --git a/clover_blocks/www/blockly/blockly_compressed.js b/clover_blocks/www/blockly/blockly_compressed.js new file mode 100755 index 000000000..9a7272d60 --- /dev/null +++ b/clover_blocks/www/blockly/blockly_compressed.js @@ -0,0 +1,1409 @@ +// Do not edit this file; automatically generated by gulp. + +/* eslint-disable */ +;(function(root, factory) { + if (typeof define === 'function' && define.amd) { // AMD + define([], factory); + } else if (typeof exports === 'object') { // Node.js + module.exports = factory(); + } else { // Browser + root.Blockly = factory(); + } +}(this, function() { + 'use strict';var Blockly={constants:{},LINE_MODE_MULTIPLIER:40,PAGE_MODE_MULTIPLIER:125,DRAG_RADIUS:5,FLYOUT_DRAG_RADIUS:10,SNAP_RADIUS:28};Blockly.CONNECTING_SNAP_RADIUS=Blockly.SNAP_RADIUS;Blockly.CURRENT_CONNECTION_PREFERENCE=8;Blockly.BUMP_DELAY=250;Blockly.BUMP_RANDOMNESS=10;Blockly.COLLAPSE_CHARS=30;Blockly.LONGPRESS=750;Blockly.SOUND_LIMIT=100;Blockly.DRAG_STACK=!0;Blockly.HSV_SATURATION=.45;Blockly.HSV_VALUE=.65;Blockly.SPRITE={width:96,height:124,url:"sprites.png"};Blockly.INPUT_VALUE=1; +Blockly.OUTPUT_VALUE=2;Blockly.NEXT_STATEMENT=3;Blockly.PREVIOUS_STATEMENT=4;Blockly.DUMMY_INPUT=5;Blockly.ALIGN_LEFT=-1;Blockly.ALIGN_CENTRE=0;Blockly.ALIGN_RIGHT=1;Blockly.DRAG_NONE=0;Blockly.DRAG_STICKY=1;Blockly.DRAG_BEGIN=1;Blockly.DRAG_FREE=2;Blockly.OPPOSITE_TYPE=[];Blockly.OPPOSITE_TYPE[Blockly.INPUT_VALUE]=Blockly.OUTPUT_VALUE;Blockly.OPPOSITE_TYPE[Blockly.OUTPUT_VALUE]=Blockly.INPUT_VALUE;Blockly.OPPOSITE_TYPE[Blockly.NEXT_STATEMENT]=Blockly.PREVIOUS_STATEMENT; +Blockly.OPPOSITE_TYPE[Blockly.PREVIOUS_STATEMENT]=Blockly.NEXT_STATEMENT;Blockly.TOOLBOX_AT_TOP=0;Blockly.TOOLBOX_AT_BOTTOM=1;Blockly.TOOLBOX_AT_LEFT=2;Blockly.TOOLBOX_AT_RIGHT=3;Blockly.DELETE_AREA_NONE=null;Blockly.DELETE_AREA_TRASH=1;Blockly.DELETE_AREA_TOOLBOX=2;Blockly.VARIABLE_CATEGORY_NAME="VARIABLE";Blockly.VARIABLE_DYNAMIC_CATEGORY_NAME="VARIABLE_DYNAMIC";Blockly.PROCEDURE_CATEGORY_NAME="PROCEDURE";Blockly.RENAME_VARIABLE_ID="RENAME_VARIABLE_ID";Blockly.DELETE_VARIABLE_ID="DELETE_VARIABLE_ID";Blockly.utils={};Blockly.utils.global=function(){return"object"===typeof self?self:"object"===typeof window?window:"object"===typeof global?global:this}();Blockly.Msg={};Blockly.utils.global.Blockly||(Blockly.utils.global.Blockly={});Blockly.utils.global.Blockly.Msg||(Blockly.utils.global.Blockly.Msg=Blockly.Msg);Blockly.utils.colour={}; +Blockly.utils.colour.parse=function(a){a=String(a).toLowerCase().trim();var b=Blockly.utils.colour.names[a];if(b)return b;b="0x"==a.substring(0,2)?"#"+a.substring(2):a;b="#"==b[0]?b:"#"+b;if(/^#[0-9a-f]{6}$/.test(b))return b;if(/^#[0-9a-f]{3}$/.test(b))return["#",b[1],b[1],b[2],b[2],b[3],b[3]].join("");var c=a.match(/^(?:rgb)?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);return c&&(a=Number(c[1]),b=Number(c[2]),c=Number(c[3]),0<=a&&256>a&&0<=b&&256>b&&0<=c&&256>c)?Blockly.utils.colour.rgbToHex(a,b, +c):null};Blockly.utils.colour.rgbToHex=function(a,b,c){b=a<<16|b<<8|c;return 16>a?"#"+(16777216|b).toString(16).substr(1):"#"+b.toString(16)};Blockly.utils.colour.hexToRgb=function(a){a=Blockly.utils.colour.parse(a);if(!a)return[0,0,0];a=parseInt(a.substr(1),16);return[a>>16,a>>8&255,a&255]}; +Blockly.utils.colour.hsvToHex=function(a,b,c){var d=0,e=0,f=0;if(0==b)f=e=d=c;else{var g=Math.floor(a/60),h=a/60-g;a=c*(1-b);var k=c*(1-b*h);b=c*(1-b*(1-h));switch(g){case 1:d=k;e=c;f=a;break;case 2:d=a;e=c;f=b;break;case 3:d=a;e=k;f=c;break;case 4:d=b;e=a;f=c;break;case 5:d=c;e=a;f=k;break;case 6:case 0:d=c,e=b,f=a}}return Blockly.utils.colour.rgbToHex(Math.floor(d),Math.floor(e),Math.floor(f))}; +Blockly.utils.colour.blend=function(a,b,c){a=Blockly.utils.colour.parse(a);if(!a)return null;b=Blockly.utils.colour.parse(b);if(!b)return null;a=Blockly.utils.colour.hexToRgb(a);b=Blockly.utils.colour.hexToRgb(b);return Blockly.utils.colour.rgbToHex(Math.round(b[0]+c*(a[0]-b[0])),Math.round(b[1]+c*(a[1]-b[1])),Math.round(b[2]+c*(a[2]-b[2])))}; +Blockly.utils.colour.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00"};Blockly.utils.Coordinate=function(a,b){this.x=a;this.y=b};Blockly.utils.Coordinate.equals=function(a,b){return a==b?!0:a&&b?a.x==b.x&&a.y==b.y:!1};Blockly.utils.Coordinate.distance=function(a,b){var c=a.x-b.x;a=a.y-b.y;return Math.sqrt(c*c+a*a)};Blockly.utils.Coordinate.magnitude=function(a){return Math.sqrt(a.x*a.x+a.y*a.y)};Blockly.utils.Coordinate.difference=function(a,b){return new Blockly.utils.Coordinate(a.x-b.x,a.y-b.y)}; +Blockly.utils.Coordinate.sum=function(a,b){return new Blockly.utils.Coordinate(a.x+b.x,a.y+b.y)};Blockly.utils.Coordinate.prototype.scale=function(a){this.x*=a;this.y*=a;return this};Blockly.utils.Coordinate.prototype.translate=function(a,b){this.x+=a;this.y+=b;return this};Blockly.utils.Rect=function(a,b,c,d){this.top=a;this.bottom=b;this.left=c;this.right=d};Blockly.utils.Rect.prototype.contains=function(a,b){return a>=this.left&&a<=this.right&&b>=this.top&&b<=this.bottom};Blockly.utils.string={};Blockly.utils.string.startsWith=function(a,b){return 0==a.lastIndexOf(b,0)};Blockly.utils.string.shortestStringLength=function(a){return a.length?a.reduce(function(a,c){return a.lengthb&&(b=c[d].length);d=-Infinity;var e=1;do{var f=d;var g=a;a=[];var h=c.length/e,k=1;for(d=0;df);return g}; +Blockly.utils.string.wrapScore_=function(a,b,c){for(var d=[0],e=[],f=0;fd&&(d=h,e=g)}return e?Blockly.utils.string.wrapMutate_(a,e,c):b};Blockly.utils.string.wrapToText_=function(a,b){for(var c=[],d=0;d=h?(e=2,f=h,(h=a.join(""))&&c.push(h),a.length=0):"{"==h?e=3:(a.push("%",h),e=0):2==e?"0"<=h&&"9">=h?f+=h:(c.push(parseInt(f,10)),g--,e=0):3==e&&(""==h?(a.splice(0,0,"%{"),g--,e=0):"}"!=h?a.push(h):(e=a.join(""),/[A-Z]\w*/i.test(e)?(h=e.toUpperCase(), +(h=Blockly.utils.string.startsWith(h,"BKY_")?h.substring(4):null)&&h in Blockly.Msg?(e=Blockly.Msg[h],"string"==typeof e?Array.prototype.push.apply(c,Blockly.utils.tokenizeInterpolation_(e,b)):b?c.push(String(e)):c.push(e)):c.push("%{"+e+"}")):c.push("%{"+e+"}"),e=a.length=0))}(h=a.join(""))&&c.push(h);b=[];for(g=a.length=0;gc;c++)b[c]=Blockly.utils.genUid.soup_.charAt(Math.random()*a);return b.join("")};Blockly.utils.genUid.soup_="!#$%()*+,-./:;=?@[]^_`{|}~ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; +Blockly.utils.is3dSupported=function(){if(void 0!==Blockly.utils.is3dSupported.cached_)return Blockly.utils.is3dSupported.cached_;if(!Blockly.utils.global.getComputedStyle)return!1;var a=document.createElement("p"),b="none",c={webkitTransform:"-webkit-transform",OTransform:"-o-transform",msTransform:"-ms-transform",MozTransform:"-moz-transform",transform:"transform"};document.body.insertBefore(a,null);for(var d in c)if(void 0!==a.style[d]){a.style[d]="translate3d(1px,1px,1px)";b=Blockly.utils.global.getComputedStyle(a); +if(!b)return document.body.removeChild(a),!1;b=b.getPropertyValue(c[d])}document.body.removeChild(a);Blockly.utils.is3dSupported.cached_="none"!==b;return Blockly.utils.is3dSupported.cached_};Blockly.utils.runAfterPageLoad=function(a){if("object"!=typeof document)throw Error("Blockly.utils.runAfterPageLoad() requires browser document.");if("complete"==document.readyState)a();else var b=setInterval(function(){"complete"==document.readyState&&(clearInterval(b),a())},10)}; +Blockly.utils.getViewportBBox=function(){var a=Blockly.utils.style.getViewportPageOffset();return new Blockly.utils.Rect(a.y,document.documentElement.clientHeight+a.y,a.x,document.documentElement.clientWidth+a.x)};Blockly.utils.arrayRemove=function(a,b){b=a.indexOf(b);if(-1==b)return!1;a.splice(b,1);return!0}; +Blockly.utils.getDocumentScroll=function(){var a=document.documentElement,b=window;return Blockly.utils.userAgent.IE&&b.pageYOffset!=a.scrollTop?new Blockly.utils.Coordinate(a.scrollLeft,a.scrollTop):new Blockly.utils.Coordinate(b.pageXOffset||a.scrollLeft,b.pageYOffset||a.scrollTop)};Blockly.utils.getBlockTypeCounts=function(a,b){var c=Object.create(null),d=a.getDescendants(!0);b&&(a=a.getNextBlock())&&(a=d.indexOf(a),d.splice(a,d.length-a));for(a=0;b=d[a];a++)c[b.type]?c[b.type]++:c[b.type]=1;return c}; +Blockly.utils.screenToWsCoordinates=function(a,b){var c=b.x;b=b.y;var d=a.getInjectionDiv().getBoundingClientRect();c=new Blockly.utils.Coordinate(c-d.left,b-d.top);b=a.getOriginOffsetInPixels();return Blockly.utils.Coordinate.difference(c,b).scale(1/a.scale)}; +Blockly.utils.parseBlockColour=function(a){var b="string"==typeof a?Blockly.utils.replaceMessageReferences(a):a,c=Number(b);if(!isNaN(c)&&0<=c&&360>=c)return{hue:c,hex:Blockly.utils.colour.hsvToHex(c,Blockly.HSV_SATURATION,255*Blockly.HSV_VALUE)};if(c=Blockly.utils.colour.parse(b))return{hue:null,hex:c};c='Invalid colour: "'+b+'"';a!=b&&(c+=' (from "'+a+'")');throw Error(c);};Blockly.Events={};Blockly.Events.group_="";Blockly.Events.recordUndo=!0;Blockly.Events.disabled_=0;Blockly.Events.CREATE="create";Blockly.Events.BLOCK_CREATE=Blockly.Events.CREATE;Blockly.Events.DELETE="delete";Blockly.Events.BLOCK_DELETE=Blockly.Events.DELETE;Blockly.Events.CHANGE="change";Blockly.Events.BLOCK_CHANGE=Blockly.Events.CHANGE;Blockly.Events.MOVE="move";Blockly.Events.BLOCK_MOVE=Blockly.Events.MOVE;Blockly.Events.VAR_CREATE="var_create";Blockly.Events.VAR_DELETE="var_delete"; +Blockly.Events.VAR_RENAME="var_rename";Blockly.Events.UI="ui";Blockly.Events.COMMENT_CREATE="comment_create";Blockly.Events.COMMENT_DELETE="comment_delete";Blockly.Events.COMMENT_CHANGE="comment_change";Blockly.Events.COMMENT_MOVE="comment_move";Blockly.Events.FINISHED_LOADING="finished_loading";Blockly.Events.BUMP_EVENTS=[Blockly.Events.BLOCK_CREATE,Blockly.Events.BLOCK_MOVE,Blockly.Events.COMMENT_CREATE,Blockly.Events.COMMENT_MOVE];Blockly.Events.FIRE_QUEUE_=[]; +Blockly.Events.fire=function(a){Blockly.Events.isEnabled()&&(Blockly.Events.FIRE_QUEUE_.length||setTimeout(Blockly.Events.fireNow_,0),Blockly.Events.FIRE_QUEUE_.push(a))};Blockly.Events.fireNow_=function(){for(var a=Blockly.Events.filter(Blockly.Events.FIRE_QUEUE_,!0),b=Blockly.Events.FIRE_QUEUE_.length=0,c;c=a[b];b++)if(c.workspaceId){var d=Blockly.Workspace.getById(c.workspaceId);d&&d.fireChangeListener(c)}}; +Blockly.Events.filter=function(a,b){a=a.slice();b||a.reverse();for(var c=[],d=Object.create(null),e=0,f;f=a[e];e++)if(!f.isNull()){var g=[f.type,f.blockId,f.workspaceId].join(" "),h=d[g],k=h?h.event:null;if(!h)d[g]={event:f,index:e},c.push(f);else if(f.type==Blockly.Events.MOVE&&h.index==e-1)k.newParentId=f.newParentId,k.newInputName=f.newInputName,k.newCoordinate=f.newCoordinate,h.index=e;else if(f.type==Blockly.Events.CHANGE&&f.element==k.element&&f.name==k.name)k.newValue=f.newValue;else if(f.type!= +Blockly.Events.UI||"click"!=f.element||"commentOpen"!=k.element&&"mutatorOpen"!=k.element&&"warningOpen"!=k.element)d[g]={event:f,index:1},c.push(f)}a=c.filter(function(a){return!a.isNull()});b||a.reverse();for(e=1;f=a[e];e++)f.type==Blockly.Events.CHANGE&&"mutation"==f.element&&a.unshift(a.splice(e,1)[0]);return a};Blockly.Events.clearPendingUndo=function(){for(var a=0,b;b=Blockly.Events.FIRE_QUEUE_[a];a++)b.recordUndo=!1};Blockly.Events.disable=function(){Blockly.Events.disabled_++}; +Blockly.Events.enable=function(){Blockly.Events.disabled_--};Blockly.Events.isEnabled=function(){return 0==Blockly.Events.disabled_};Blockly.Events.getGroup=function(){return Blockly.Events.group_};Blockly.Events.setGroup=function(a){Blockly.Events.group_="boolean"==typeof a?a?Blockly.utils.genUid():"":a};Blockly.Events.getDescendantIds=function(a){var b=[];a=a.getDescendants(!1);for(var c=0,d;d=a[c];c++)b[c]=d.id;return b}; +Blockly.Events.fromJson=function(a,b){switch(a.type){case Blockly.Events.CREATE:var c=new Blockly.Events.Create(null);break;case Blockly.Events.DELETE:c=new Blockly.Events.Delete(null);break;case Blockly.Events.CHANGE:c=new Blockly.Events.Change(null,"","","","");break;case Blockly.Events.MOVE:c=new Blockly.Events.Move(null);break;case Blockly.Events.VAR_CREATE:c=new Blockly.Events.VarCreate(null);break;case Blockly.Events.VAR_DELETE:c=new Blockly.Events.VarDelete(null);break;case Blockly.Events.VAR_RENAME:c= +new Blockly.Events.VarRename(null,"");break;case Blockly.Events.UI:c=new Blockly.Events.Ui(null,"","","");break;case Blockly.Events.COMMENT_CREATE:c=new Blockly.Events.CommentCreate(null);break;case Blockly.Events.COMMENT_CHANGE:c=new Blockly.Events.CommentChange(null,"","");break;case Blockly.Events.COMMENT_MOVE:c=new Blockly.Events.CommentMove(null);break;case Blockly.Events.COMMENT_DELETE:c=new Blockly.Events.CommentDelete(null);break;case Blockly.Events.FINISHED_LOADING:c=new Blockly.Events.FinishedLoading(b); +break;default:throw Error("Unknown event type.");}c.fromJson(a);c.workspaceId=b.id;return c}; +Blockly.Events.disableOrphans=function(a){if((a.type==Blockly.Events.MOVE||a.type==Blockly.Events.CREATE)&&a.workspaceId){var b=Blockly.Workspace.getById(a.workspaceId);if(a=b.getBlockById(a.blockId)){var c=a.getParent();if(c&&c.isEnabled())for(b=a.getDescendants(!1),a=0;c=b[a];a++)c.setEnabled(!0);else if((a.outputConnection||a.previousConnection)&&!b.isDragging()){do a.setEnabled(!1),a=a.getNextBlock();while(a)}}}};Blockly.Events.Abstract=function(){this.workspaceId=void 0;this.group=Blockly.Events.getGroup();this.recordUndo=Blockly.Events.recordUndo};Blockly.Events.Abstract.prototype.toJson=function(){var a={type:this.type};this.group&&(a.group=this.group);return a};Blockly.Events.Abstract.prototype.fromJson=function(a){this.group=a.group};Blockly.Events.Abstract.prototype.isNull=function(){return!1};Blockly.Events.Abstract.prototype.run=function(a){}; +Blockly.Events.Abstract.prototype.getEventWorkspace_=function(){if(this.workspaceId)var a=Blockly.Workspace.getById(this.workspaceId);if(!a)throw Error("Workspace is null. Event must have been generated from real Blockly events.");return a};Blockly.utils.object={};Blockly.utils.object.inherits=function(a,b){a.superClass_=b.prototype;a.prototype=Object.create(b.prototype);a.prototype.constructor=a};Blockly.utils.object.mixin=function(a,b){for(var c in b)a[c]=b[c]};Blockly.utils.object.deepMerge=function(a,b){for(var c in b)a[c]=null!=b[c]&&"object"===typeof b[c]?Blockly.utils.object.deepMerge(a[c]||Object.create(null),b[c]):b[c];return a};Blockly.utils.object.values=function(a){return Object.values?Object.values(a):Object.keys(a).map(function(b){return a[b]})};Blockly.Events.Ui=function(a,b,c,d){Blockly.Events.Ui.superClass_.constructor.call(this);this.blockId=a?a.id:null;this.workspaceId=a?a.workspace.id:void 0;this.element=b;this.oldValue=c;this.newValue=d;this.recordUndo=!1};Blockly.utils.object.inherits(Blockly.Events.Ui,Blockly.Events.Abstract);Blockly.Events.Ui.prototype.type=Blockly.Events.UI; +Blockly.Events.Ui.prototype.toJson=function(){var a=Blockly.Events.Ui.superClass_.toJson.call(this);a.element=this.element;void 0!==this.newValue&&(a.newValue=this.newValue);this.blockId&&(a.blockId=this.blockId);return a};Blockly.Events.Ui.prototype.fromJson=function(a){Blockly.Events.Ui.superClass_.fromJson.call(this,a);this.element=a.element;this.newValue=a.newValue;this.blockId=a.blockId};Blockly.utils.dom={};Blockly.utils.dom.SVG_NS="http://www.w3.org/2000/svg";Blockly.utils.dom.HTML_NS="http://www.w3.org/1999/xhtml";Blockly.utils.dom.XLINK_NS="http://www.w3.org/1999/xlink";Blockly.utils.dom.NodeType={ELEMENT_NODE:1,TEXT_NODE:3,COMMENT_NODE:8,DOCUMENT_POSITION_CONTAINED_BY:16};Blockly.utils.dom.cacheWidths_=null;Blockly.utils.dom.cacheReference_=0;Blockly.utils.dom.canvasContext_=null; +Blockly.utils.dom.createSvgElement=function(a,b,c){a=document.createElementNS(Blockly.utils.dom.SVG_NS,a);for(var d in b)a.setAttribute(d,b[d]);document.body.runtimeStyle&&(a.runtimeStyle=a.currentStyle=a.style);c&&c.appendChild(a);return a};Blockly.utils.dom.addClass=function(a,b){var c=a.getAttribute("class")||"";if(-1!=(" "+c+" ").indexOf(" "+b+" "))return!1;c&&(c+=" ");a.setAttribute("class",c+b);return!0}; +Blockly.utils.dom.removeClass=function(a,b){var c=a.getAttribute("class");if(-1==(" "+c+" ").indexOf(" "+b+" "))return!1;c=c.split(/\s+/);for(var d=0;db||b>this.getChildCount())throw Error(Blockly.Component.Error.CHILD_INDEX_OUT_OF_BOUNDS);this.childIndex_[a.getId()]=a;if(a.getParent()==this){var d=this.children_.indexOf(a);-1>>/g,a),a=document.createElement("style"),a.id="blockly-common-style",c=document.createTextNode(c),a.appendChild(c),document.head.insertBefore(a,document.head.firstChild))}};Blockly.Css.setCursor=function(a){console.warn("Deprecated call to Blockly.Css.setCursor. See issue #981 for context")}; +Blockly.Css.CONTENT=[".blocklySvg {","background-color: #fff;","outline: none;","overflow: hidden;","position: absolute;","display: block;","}",".blocklyWidgetDiv {","display: none;","position: absolute;","z-index: 99999;","}",".injectionDiv {","height: 100%;","position: relative;","overflow: hidden;","touch-action: none;","}",".blocklyNonSelectable {","user-select: none;","-ms-user-select: none;","-webkit-user-select: none;","}",".blocklyWsDragSurface {","display: none;","position: absolute;","top: 0;", +"left: 0;","}",".blocklyWsDragSurface.blocklyOverflowVisible {","overflow: visible;","}",".blocklyBlockDragSurface {","display: none;","position: absolute;","top: 0;","left: 0;","right: 0;","bottom: 0;","overflow: visible !important;","z-index: 50;","}",".blocklyBlockCanvas.blocklyCanvasTransitioning,",".blocklyBubbleCanvas.blocklyCanvasTransitioning {","transition: transform .5s;","}",".blocklyTooltipDiv {","background-color: #ffffc7;","border: 1px solid #ddc;","box-shadow: 4px 4px 20px 1px rgba(0,0,0,.15);", +"color: #000;","display: none;","font: 9pt sans-serif;","opacity: .9;","padding: 2px;","position: absolute;","z-index: 100000;","}",".blocklyDropDownDiv {","position: absolute;","left: 0;","top: 0;","z-index: 1000;","display: none;","border: 1px solid;","border-color: #dadce0;","background-color: #fff;","border-radius: 2px;","padding: 4px;","box-shadow: 0 0 3px 1px rgba(0,0,0,.3);","}",".blocklyDropDownDiv.blocklyFocused {","box-shadow: 0 0 6px 1px rgba(0,0,0,.3);","}",".blocklyDropDownContent {", +"max-height: 300px;","overflow: auto;","overflow-x: hidden;","}",".blocklyDropDownArrow {","position: absolute;","left: 0;","top: 0;","width: 16px;","height: 16px;","z-index: -1;","background-color: inherit;","border-color: inherit;","}",".blocklyDropDownButton {","display: inline-block;","float: left;","padding: 0;","margin: 4px;","border-radius: 4px;","outline: none;","border: 1px solid;","transition: box-shadow .1s;","cursor: pointer;","}",".blocklyArrowTop {","border-top: 1px solid;","border-left: 1px solid;", +"border-top-left-radius: 4px;","border-color: inherit;","}",".blocklyArrowBottom {","border-bottom: 1px solid;","border-right: 1px solid;","border-bottom-right-radius: 4px;","border-color: inherit;","}",".blocklyResizeSE {","cursor: se-resize;","fill: #aaa;","}",".blocklyResizeSW {","cursor: sw-resize;","fill: #aaa;","}",".blocklyResizeLine {","stroke: #515A5A;","stroke-width: 1;","}",".blocklyHighlightedConnectionPath {","fill: none;","stroke: #fc3;","stroke-width: 4px;","}",".blocklyPathLight {", +"fill: none;","stroke-linecap: round;","stroke-width: 1;","}",".blocklySelected>.blocklyPathLight {","display: none;","}",".blocklyDraggable {",'cursor: url("<<>>/handopen.cur"), auto;',"cursor: grab;","cursor: -webkit-grab;","}",".blocklyDragging {",'cursor: url("<<>>/handclosed.cur"), auto;',"cursor: grabbing;","cursor: -webkit-grabbing;","}",".blocklyDraggable:active {",'cursor: url("<<>>/handclosed.cur"), auto;',"cursor: grabbing;","cursor: -webkit-grabbing;","}",".blocklyBlockDragSurface .blocklyDraggable {", +'cursor: url("<<>>/handclosed.cur"), auto;',"cursor: grabbing;","cursor: -webkit-grabbing;","}",".blocklyDragging.blocklyDraggingDelete {",'cursor: url("<<>>/handdelete.cur"), auto;',"}",".blocklyDragging>.blocklyPath,",".blocklyDragging>.blocklyPathLight {","fill-opacity: .8;","stroke-opacity: .8;","}",".blocklyDragging>.blocklyPathDark {","display: none;","}",".blocklyDisabled>.blocklyPath {","fill-opacity: .5;","stroke-opacity: .5;","}",".blocklyDisabled>.blocklyPathLight,",".blocklyDisabled>.blocklyPathDark {", +"display: none;","}",".blocklyInsertionMarker>.blocklyPath,",".blocklyInsertionMarker>.blocklyPathLight,",".blocklyInsertionMarker>.blocklyPathDark {","fill-opacity: .2;","stroke: none;","}",".blocklyMultilineText {","font-family: monospace;","}",".blocklyNonEditableText>text {","pointer-events: none;","}",".blocklyFlyout {","position: absolute;","z-index: 20;","}",".blocklyText text {","cursor: default;","}",".blocklySvg text,",".blocklyBlockDragSurface text {","user-select: none;","-ms-user-select: none;", +"-webkit-user-select: none;","cursor: inherit;","}",".blocklyHidden {","display: none;","}",".blocklyFieldDropdown:not(.blocklyHidden) {","display: block;","}",".blocklyIconGroup {","cursor: default;","}",".blocklyIconGroup:not(:hover),",".blocklyIconGroupReadonly {","opacity: .6;","}",".blocklyIconShape {","fill: #00f;","stroke: #fff;","stroke-width: 1px;","}",".blocklyIconSymbol {","fill: #fff;","}",".blocklyMinimalBody {","margin: 0;","padding: 0;","}",".blocklyHtmlInput {","border: none;","border-radius: 4px;", +"height: 100%;","margin: 0;","outline: none;","padding: 0;","width: 100%;","text-align: center;","display: block;","box-sizing: border-box;","}",".blocklyHtmlInput::-ms-clear {","display: none;","}",".blocklyMainBackground {","stroke-width: 1;","stroke: #c6c6c6;","}",".blocklyMutatorBackground {","fill: #fff;","stroke: #ddd;","stroke-width: 1;","}",".blocklyFlyoutBackground {","fill: #ddd;","fill-opacity: .8;","}",".blocklyMainWorkspaceScrollbar {","z-index: 20;","}",".blocklyFlyoutScrollbar {","z-index: 30;", +"}",".blocklyScrollbarHorizontal,",".blocklyScrollbarVertical {","position: absolute;","outline: none;","}",".blocklyScrollbarBackground {","opacity: 0;","}",".blocklyScrollbarHandle {","fill: #ccc;","}",".blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,",".blocklyScrollbarHandle:hover {","fill: #bbb;","}",".blocklyFlyout .blocklyScrollbarHandle {","fill: #bbb;","}",".blocklyFlyout .blocklyScrollbarBackground:hover+.blocklyScrollbarHandle,",".blocklyFlyout .blocklyScrollbarHandle:hover {", +"fill: #aaa;","}",".blocklyInvalidInput {","background: #faa;","}",".blocklyVerticalMarker {","stroke-width: 3px;","fill: rgba(255,255,255,.5);","pointer-events: none;","}",".blocklyComputeCanvas {","position: absolute;","width: 0;","height: 0;","}",".blocklyNoPointerEvents {","pointer-events: none;","}",".blocklyContextMenu {","border-radius: 4px;","max-height: 100%;","}",".blocklyDropdownMenu {","border-radius: 2px;","padding: 0 !important;","}",".blocklyDropdownMenu .blocklyMenuItem {","padding-left: 28px;", +"}",".blocklyDropdownMenu .blocklyMenuItemRtl {","padding-left: 5px;","padding-right: 28px;","}",".blocklyWidgetDiv .blocklyMenu {","background: #fff;","border: 1px solid transparent;","box-shadow: 0 0 3px 1px rgba(0,0,0,.3);","font: normal 13px Arial, sans-serif;","margin: 0;","outline: none;","padding: 4px 0;","position: absolute;","overflow-y: auto;","overflow-x: hidden;","max-height: 100%;","z-index: 20000;","}",".blocklyWidgetDiv .blocklyMenu.blocklyFocused {","box-shadow: 0 0 6px 1px rgba(0,0,0,.3);", +"}",".blocklyDropDownDiv .blocklyMenu {",'font: normal 13px "Helvetica Neue", Helvetica, sans-serif;',"outline: none;","z-index: 20000;","}",".blocklyMenuItem {","border: none;","color: #000;","cursor: pointer;","list-style: none;","margin: 0;","min-width: 7em;","padding: 6px 15px;","white-space: nowrap;","}",".blocklyMenuItemDisabled {","color: #ccc;","cursor: inherit;","}",".blocklyMenuItemHighlight {","background-color: rgba(0,0,0,.1);","}",".blocklyMenuItemCheckbox {","height: 16px;","position: absolute;", +"width: 16px;","}",".blocklyMenuItemSelected .blocklyMenuItemCheckbox {","background: url(<<>>/sprites.png) no-repeat -48px -16px;","float: left;","margin-left: -24px;","position: static;","}",".blocklyMenuItemRtl .blocklyMenuItemCheckbox {","float: right;","margin-right: -24px;","}"];Blockly.utils.math={};Blockly.utils.math.toRadians=function(a){return a*Math.PI/180};Blockly.utils.math.toDegrees=function(a){return 180*a/Math.PI};Blockly.utils.math.clamp=function(a,b,c){if(ce.top?Blockly.DropDownDiv.getPositionAboveMetrics_(c,d,e,f):b+f.heightdocument.documentElement.clientTop?Blockly.DropDownDiv.getPositionAboveMetrics_(c, +d,e,f):Blockly.DropDownDiv.getPositionTopOfPageMetrics_(a,e,f)};Blockly.DropDownDiv.getPositionBelowMetrics_=function(a,b,c,d){a=Blockly.DropDownDiv.getPositionX(a,c.left,c.right,d.width);return{initialX:a.divX,initialY:b,finalX:a.divX,finalY:b+Blockly.DropDownDiv.PADDING_Y,arrowX:a.arrowX,arrowY:-(Blockly.DropDownDiv.ARROW_SIZE/2+Blockly.DropDownDiv.BORDER_SIZE),arrowAtTop:!0,arrowVisible:!0}}; +Blockly.DropDownDiv.getPositionAboveMetrics_=function(a,b,c,d){a=Blockly.DropDownDiv.getPositionX(a,c.left,c.right,d.width);return{initialX:a.divX,initialY:b-d.height,finalX:a.divX,finalY:b-d.height-Blockly.DropDownDiv.PADDING_Y,arrowX:a.arrowX,arrowY:d.height-2*Blockly.DropDownDiv.BORDER_SIZE-Blockly.DropDownDiv.ARROW_SIZE/2,arrowAtTop:!1,arrowVisible:!0}}; +Blockly.DropDownDiv.getPositionTopOfPageMetrics_=function(a,b,c){a=Blockly.DropDownDiv.getPositionX(a,b.left,b.right,c.width);return{initialX:a.divX,initialY:0,finalX:a.divX,finalY:0,arrowAtTop:null,arrowX:null,arrowY:null,arrowVisible:!1}}; +Blockly.DropDownDiv.getPositionX=function(a,b,c,d){var e=a;a=Blockly.utils.math.clamp(b,a-d/2,c-d);e-=Blockly.DropDownDiv.ARROW_SIZE/2;b=Blockly.DropDownDiv.ARROW_HORIZONTAL_PADDING;d=Blockly.utils.math.clamp(b,e-a,d-b-Blockly.DropDownDiv.ARROW_SIZE);return{arrowX:d,divX:a}};Blockly.DropDownDiv.isVisible=function(){return!!Blockly.DropDownDiv.owner_}; +Blockly.DropDownDiv.hideIfOwner=function(a,b){return Blockly.DropDownDiv.owner_===a?(b?Blockly.DropDownDiv.hideWithoutAnimation():Blockly.DropDownDiv.hide(),!0):!1}; +Blockly.DropDownDiv.hide=function(){var a=Blockly.DropDownDiv.DIV_;a.style.transform="translate(0, 0)";a.style.opacity=0;Blockly.DropDownDiv.animateOutTimer_=setTimeout(function(){Blockly.DropDownDiv.hideWithoutAnimation()},1E3*Blockly.DropDownDiv.ANIMATION_TIME);Blockly.DropDownDiv.onHide_&&(Blockly.DropDownDiv.onHide_(),Blockly.DropDownDiv.onHide_=null)}; +Blockly.DropDownDiv.hideWithoutAnimation=function(){if(Blockly.DropDownDiv.isVisible()){Blockly.DropDownDiv.animateOutTimer_&&clearTimeout(Blockly.DropDownDiv.animateOutTimer_);var a=Blockly.DropDownDiv.DIV_;a.style.transform="";a.style.left="";a.style.top="";a.style.opacity=0;a.style.display="none";a.style.backgroundColor="";a.style.borderColor="";Blockly.DropDownDiv.onHide_&&(Blockly.DropDownDiv.onHide_(),Blockly.DropDownDiv.onHide_=null);Blockly.DropDownDiv.clearContent();Blockly.DropDownDiv.owner_= +null;Blockly.DropDownDiv.rendererClassName_&&(Blockly.utils.dom.removeClass(a,Blockly.DropDownDiv.rendererClassName_),Blockly.DropDownDiv.rendererClassName_="");Blockly.DropDownDiv.themeClassName_&&(Blockly.utils.dom.removeClass(a,Blockly.DropDownDiv.themeClassName_),Blockly.DropDownDiv.themeClassName_="");Blockly.getMainWorkspace().markFocused()}}; +Blockly.DropDownDiv.positionInternal_=function(a,b,c,d){a=Blockly.DropDownDiv.getPositionMetrics_(a,b,c,d);a.arrowVisible?(Blockly.DropDownDiv.arrow_.style.display="",Blockly.DropDownDiv.arrow_.style.transform="translate("+a.arrowX+"px,"+a.arrowY+"px) rotate(45deg)",Blockly.DropDownDiv.arrow_.setAttribute("class",a.arrowAtTop?"blocklyDropDownArrow blocklyArrowTop":"blocklyDropDownArrow blocklyArrowBottom")):Blockly.DropDownDiv.arrow_.style.display="none";b=Math.floor(a.initialX);c=Math.floor(a.initialY); +d=Math.floor(a.finalX);var e=Math.floor(a.finalY),f=Blockly.DropDownDiv.DIV_;f.style.left=b+"px";f.style.top=c+"px";f.style.display="block";f.style.opacity=1;f.style.transform="translate("+(d-b)+"px,"+(e-c)+"px)";return!!a.arrowAtTop}; +Blockly.DropDownDiv.repositionForWindowResize=function(){if(Blockly.DropDownDiv.owner_){var a=Blockly.DropDownDiv.owner_,b=a.getSourceBlock();a=Blockly.DropDownDiv.positionToField_?Blockly.DropDownDiv.getScaledBboxOfField_(a):Blockly.DropDownDiv.getScaledBboxOfBlock_(b);b=a.left+(a.right-a.left)/2;Blockly.DropDownDiv.positionInternal_(b,a.bottom,b,a.top)}else Blockly.DropDownDiv.hide()};Blockly.Grid=function(a,b){this.gridPattern_=a;this.spacing_=b.spacing;this.length_=b.length;this.line2_=(this.line1_=a.firstChild)&&this.line1_.nextSibling;this.snapToGrid_=b.snap};Blockly.Grid.prototype.scale_=1;Blockly.Grid.prototype.dispose=function(){this.gridPattern_=null};Blockly.Grid.prototype.shouldSnap=function(){return this.snapToGrid_};Blockly.Grid.prototype.getSpacing=function(){return this.spacing_};Blockly.Grid.prototype.getPatternId=function(){return this.gridPattern_.id}; +Blockly.Grid.prototype.update=function(a){this.scale_=a;var b=this.spacing_*a||100;this.gridPattern_.setAttribute("width",b);this.gridPattern_.setAttribute("height",b);b=Math.floor(this.spacing_/2)+.5;var c=b-this.length_/2,d=b+this.length_/2;b*=a;c*=a;d*=a;this.setLineAttributes_(this.line1_,a,c,d,b,b);this.setLineAttributes_(this.line2_,a,b,b,c,d)}; +Blockly.Grid.prototype.setLineAttributes_=function(a,b,c,d,e,f){a&&(a.setAttribute("stroke-width",b),a.setAttribute("x1",c),a.setAttribute("y1",e),a.setAttribute("x2",d),a.setAttribute("y2",f))};Blockly.Grid.prototype.moveTo=function(a,b){this.gridPattern_.setAttribute("x",a);this.gridPattern_.setAttribute("y",b);(Blockly.utils.userAgent.IE||Blockly.utils.userAgent.EDGE)&&this.update(this.scale_)}; +Blockly.Grid.createDom=function(a,b,c){a=Blockly.utils.dom.createSvgElement("pattern",{id:"blocklyGridPattern"+a,patternUnits:"userSpaceOnUse"},c);0b.indexOf(d))throw Error(d+" is not a valid modifier key.");};Blockly.user.keyMap.createSerializedKey=function(a,b){var c="",d=Blockly.utils.object.values(Blockly.user.keyMap.modifierKeys);Blockly.user.keyMap.checkModifiers_(b,d);for(var e=0,f;f=d[e];e++)-1");b.domToMutation(d)}Blockly.Events.fire(new Blockly.Events.Change(b,"mutation",null,c,a));break;default:console.warn("Unknown change type: "+this.element)}else console.warn("Can't change non-existent block: "+this.blockId)}; +Blockly.Events.Create=function(a){a&&(Blockly.Events.Create.superClass_.constructor.call(this,a),this.xml=a.workspace.rendered?Blockly.Xml.blockToDomWithXY(a):Blockly.Xml.blockToDom(a),this.ids=Blockly.Events.getDescendantIds(a))};Blockly.utils.object.inherits(Blockly.Events.Create,Blockly.Events.BlockBase);Blockly.Events.BlockCreate=Blockly.Events.Create;Blockly.Events.Create.prototype.type=Blockly.Events.CREATE; +Blockly.Events.Create.prototype.toJson=function(){var a=Blockly.Events.Create.superClass_.toJson.call(this);a.xml=Blockly.Xml.domToText(this.xml);a.ids=this.ids;return a};Blockly.Events.Create.prototype.fromJson=function(a){Blockly.Events.Create.superClass_.fromJson.call(this,a);this.xml=Blockly.Xml.textToDom(a.xml);this.ids=a.ids}; +Blockly.Events.Create.prototype.run=function(a){var b=this.getEventWorkspace_();if(a)a=Blockly.utils.xml.createElement("xml"),a.appendChild(this.xml),Blockly.Xml.domToWorkspace(a,b);else{a=0;for(var c;c=this.ids[a];a++){var d=b.getBlockById(c);d?d.dispose(!1):c==this.blockId&&console.warn("Can't uncreate non-existent block: "+c)}}}; +Blockly.Events.Delete=function(a){if(a){if(a.getParent())throw Error("Connected blocks cannot be deleted.");Blockly.Events.Delete.superClass_.constructor.call(this,a);this.oldXml=a.workspace.rendered?Blockly.Xml.blockToDomWithXY(a):Blockly.Xml.blockToDom(a);this.ids=Blockly.Events.getDescendantIds(a)}};Blockly.utils.object.inherits(Blockly.Events.Delete,Blockly.Events.BlockBase);Blockly.Events.BlockDelete=Blockly.Events.Delete;Blockly.Events.Delete.prototype.type=Blockly.Events.DELETE; +Blockly.Events.Delete.prototype.toJson=function(){var a=Blockly.Events.Delete.superClass_.toJson.call(this);a.ids=this.ids;return a};Blockly.Events.Delete.prototype.fromJson=function(a){Blockly.Events.Delete.superClass_.fromJson.call(this,a);this.ids=a.ids}; +Blockly.Events.Delete.prototype.run=function(a){var b=this.getEventWorkspace_();if(a){a=0;for(var c;c=this.ids[a];a++){var d=b.getBlockById(c);d?d.dispose(!1):c==this.blockId&&console.warn("Can't delete non-existent block: "+c)}}else a=Blockly.utils.xml.createElement("xml"),a.appendChild(this.oldXml),Blockly.Xml.domToWorkspace(a,b)}; +Blockly.Events.Move=function(a){a&&(Blockly.Events.Move.superClass_.constructor.call(this,a),a=this.currentLocation_(),this.oldParentId=a.parentId,this.oldInputName=a.inputName,this.oldCoordinate=a.coordinate)};Blockly.utils.object.inherits(Blockly.Events.Move,Blockly.Events.BlockBase);Blockly.Events.BlockMove=Blockly.Events.Move;Blockly.Events.Move.prototype.type=Blockly.Events.MOVE; +Blockly.Events.Move.prototype.toJson=function(){var a=Blockly.Events.Move.superClass_.toJson.call(this);this.newParentId&&(a.newParentId=this.newParentId);this.newInputName&&(a.newInputName=this.newInputName);this.newCoordinate&&(a.newCoordinate=Math.round(this.newCoordinate.x)+","+Math.round(this.newCoordinate.y));return a}; +Blockly.Events.Move.prototype.fromJson=function(a){Blockly.Events.Move.superClass_.fromJson.call(this,a);this.newParentId=a.newParentId;this.newInputName=a.newInputName;a.newCoordinate&&(a=a.newCoordinate.split(","),this.newCoordinate=new Blockly.utils.Coordinate(Number(a[0]),Number(a[1])))};Blockly.Events.Move.prototype.recordNew=function(){var a=this.currentLocation_();this.newParentId=a.parentId;this.newInputName=a.inputName;this.newCoordinate=a.coordinate}; +Blockly.Events.Move.prototype.currentLocation_=function(){var a=this.getEventWorkspace_().getBlockById(this.blockId),b={},c=a.getParent();if(c){if(b.parentId=c.id,a=c.getInputWithBlock(a))b.inputName=a.name}else b.coordinate=a.getRelativeToSurfaceXY();return b};Blockly.Events.Move.prototype.isNull=function(){return this.oldParentId==this.newParentId&&this.oldInputName==this.newInputName&&Blockly.utils.Coordinate.equals(this.oldCoordinate,this.newCoordinate)}; +Blockly.Events.Move.prototype.run=function(a){var b=this.getEventWorkspace_(),c=b.getBlockById(this.blockId);if(c){var d=a?this.newParentId:this.oldParentId,e=a?this.newInputName:this.oldInputName;a=a?this.newCoordinate:this.oldCoordinate;var f=null;if(d&&(f=b.getBlockById(d),!f)){console.warn("Can't connect to non-existent block: "+d);return}c.getParent()&&c.unplug();if(a)e=c.getRelativeToSurfaceXY(),c.moveBy(a.x-e.x,a.y-e.y);else{c=c.outputConnection||c.previousConnection;if(e){if(b=f.getInput(e))var g= +b.connection}else c.type==Blockly.PREVIOUS_STATEMENT&&(g=f.nextConnection);g?c.connect(g):console.warn("Can't connect to non-existent input: "+e)}}else console.warn("Can't move non-existent block: "+this.blockId)};Blockly.Events.FinishedLoading=function(a){this.workspaceId=a.id;this.group=Blockly.Events.getGroup();this.recordUndo=!1};Blockly.utils.object.inherits(Blockly.Events.FinishedLoading,Blockly.Events.Ui);Blockly.Events.FinishedLoading.prototype.type=Blockly.Events.FINISHED_LOADING;Blockly.Events.FinishedLoading.prototype.toJson=function(){var a={type:this.type};this.group&&(a.group=this.group);this.workspaceId&&(a.workspaceId=this.workspaceId);return a}; +Blockly.Events.FinishedLoading.prototype.fromJson=function(a){this.workspaceId=a.workspaceId;this.group=a.group};Blockly.Events.VarBase=function(a){Blockly.Events.VarBase.superClass_.constructor.call(this);this.varId=a.getId();this.workspaceId=a.workspace.id};Blockly.utils.object.inherits(Blockly.Events.VarBase,Blockly.Events.Abstract);Blockly.Events.VarBase.prototype.toJson=function(){var a=Blockly.Events.VarBase.superClass_.toJson.call(this);a.varId=this.varId;return a};Blockly.Events.VarBase.prototype.fromJson=function(a){Blockly.Events.VarBase.superClass_.toJson.call(this);this.varId=a.varId}; +Blockly.Events.VarCreate=function(a){a&&(Blockly.Events.VarCreate.superClass_.constructor.call(this,a),this.varType=a.type,this.varName=a.name)};Blockly.utils.object.inherits(Blockly.Events.VarCreate,Blockly.Events.VarBase);Blockly.Events.VarCreate.prototype.type=Blockly.Events.VAR_CREATE;Blockly.Events.VarCreate.prototype.toJson=function(){var a=Blockly.Events.VarCreate.superClass_.toJson.call(this);a.varType=this.varType;a.varName=this.varName;return a}; +Blockly.Events.VarCreate.prototype.fromJson=function(a){Blockly.Events.VarCreate.superClass_.fromJson.call(this,a);this.varType=a.varType;this.varName=a.varName};Blockly.Events.VarCreate.prototype.run=function(a){var b=this.getEventWorkspace_();a?b.createVariable(this.varName,this.varType,this.varId):b.deleteVariableById(this.varId)};Blockly.Events.VarDelete=function(a){a&&(Blockly.Events.VarDelete.superClass_.constructor.call(this,a),this.varType=a.type,this.varName=a.name)}; +Blockly.utils.object.inherits(Blockly.Events.VarDelete,Blockly.Events.VarBase);Blockly.Events.VarDelete.prototype.type=Blockly.Events.VAR_DELETE;Blockly.Events.VarDelete.prototype.toJson=function(){var a=Blockly.Events.VarDelete.superClass_.toJson.call(this);a.varType=this.varType;a.varName=this.varName;return a};Blockly.Events.VarDelete.prototype.fromJson=function(a){Blockly.Events.VarDelete.superClass_.fromJson.call(this,a);this.varType=a.varType;this.varName=a.varName}; +Blockly.Events.VarDelete.prototype.run=function(a){var b=this.getEventWorkspace_();a?b.deleteVariableById(this.varId):b.createVariable(this.varName,this.varType,this.varId)};Blockly.Events.VarRename=function(a,b){a&&(Blockly.Events.VarRename.superClass_.constructor.call(this,a),this.oldName=a.name,this.newName=b)};Blockly.utils.object.inherits(Blockly.Events.VarRename,Blockly.Events.VarBase);Blockly.Events.VarRename.prototype.type=Blockly.Events.VAR_RENAME; +Blockly.Events.VarRename.prototype.toJson=function(){var a=Blockly.Events.VarRename.superClass_.toJson.call(this);a.oldName=this.oldName;a.newName=this.newName;return a};Blockly.Events.VarRename.prototype.fromJson=function(a){Blockly.Events.VarRename.superClass_.fromJson.call(this,a);this.oldName=a.oldName;this.newName=a.newName};Blockly.Events.VarRename.prototype.run=function(a){var b=this.getEventWorkspace_();a?b.renameVariableById(this.varId,this.newName):b.renameVariableById(this.varId,this.oldName)};Blockly.Xml={};Blockly.Xml.workspaceToDom=function(a,b){var c=Blockly.utils.xml.createElement("xml"),d=Blockly.Xml.variablesToDom(Blockly.Variables.allUsedVarModels(a));d.hasChildNodes()&&c.appendChild(d);var e=a.getTopComments(!0);d=0;for(var f;f=e[d];d++)c.appendChild(f.toXmlWithXY(b));a=a.getTopBlocks(!0);for(d=0;e=a[d];d++)c.appendChild(Blockly.Xml.blockToDomWithXY(e,b));return c}; +Blockly.Xml.variablesToDom=function(a){for(var b=Blockly.utils.xml.createElement("variables"),c=0,d;d=a[c];c++){var e=Blockly.utils.xml.createElement("variable");e.appendChild(Blockly.utils.xml.createTextNode(d.name));d.type&&e.setAttribute("type",d.type);e.id=d.getId();b.appendChild(e)}return b}; +Blockly.Xml.blockToDomWithXY=function(a,b){if(a.isInsertionMarker()&&(a=a.getChildren(!1)[0],!a))return new DocumentFragment;var c;a.workspace.RTL&&(c=a.workspace.getWidth());b=Blockly.Xml.blockToDom(a,b);var d=a.getRelativeToSurfaceXY();b.setAttribute("x",Math.round(a.workspace.RTL?c-d.x:d.x));b.setAttribute("y",Math.round(d.y));return b};Blockly.Xml.fieldToDom_=function(a){if(a.isSerializable()){var b=Blockly.utils.xml.createElement("field");b.setAttribute("name",a.name||"");return a.toXml(b)}return null}; +Blockly.Xml.allFieldsToDom_=function(a,b){for(var c=0,d;d=a.inputList[c];c++)for(var e=0,f;f=d.fieldRow[e];e++)(f=Blockly.Xml.fieldToDom_(f))&&b.appendChild(f)}; +Blockly.Xml.blockToDom=function(a,b){if(a.isInsertionMarker())return(a=a.getChildren(!1)[0])?Blockly.Xml.blockToDom(a):new DocumentFragment;var c=Blockly.utils.xml.createElement(a.isShadow()?"shadow":"block");c.setAttribute("type",a.type);b||c.setAttribute("id",a.id);if(a.mutationToDom){var d=a.mutationToDom();d&&(d.hasChildNodes()||d.hasAttributes())&&c.appendChild(d)}Blockly.Xml.allFieldsToDom_(a,c);if(d=a.getCommentText()){var e=a.commentModel.size,f=a.commentModel.pinned,g=Blockly.utils.xml.createElement("comment"); +g.appendChild(Blockly.utils.xml.createTextNode(d));g.setAttribute("pinned",f);g.setAttribute("h",e.height);g.setAttribute("w",e.width);c.appendChild(g)}a.data&&(d=Blockly.utils.xml.createElement("data"),d.appendChild(Blockly.utils.xml.createTextNode(a.data)),c.appendChild(d));for(d=0;e=a.inputList[d];d++){var h;f=!0;if(e.type!=Blockly.DUMMY_INPUT){var k=e.connection.targetBlock();e.type==Blockly.INPUT_VALUE?h=Blockly.utils.xml.createElement("value"):e.type==Blockly.NEXT_STATEMENT&&(h=Blockly.utils.xml.createElement("statement")); +g=e.connection.getShadowDom();!g||k&&k.isShadow()||h.appendChild(Blockly.Xml.cloneShadow_(g,b));k&&(g=Blockly.Xml.blockToDom(k,b),g.nodeType==Blockly.utils.dom.NodeType.ELEMENT_NODE&&(h.appendChild(g),f=!1));h.setAttribute("name",e.name);f||c.appendChild(h)}}void 0!=a.inputsInline&&a.inputsInline!=a.inputsInlineDefault&&c.setAttribute("inline",a.inputsInline);a.isCollapsed()&&c.setAttribute("collapsed",!0);a.isEnabled()||c.setAttribute("disabled",!0);a.isDeletable()||a.isShadow()||c.setAttribute("deletable", +!1);a.isMovable()||a.isShadow()||c.setAttribute("movable",!1);a.isEditable()||c.setAttribute("editable",!1);if(d=a.getNextBlock())g=Blockly.Xml.blockToDom(d,b),g.nodeType==Blockly.utils.dom.NodeType.ELEMENT_NODE&&(h=Blockly.utils.xml.createElement("next"),h.appendChild(g),c.appendChild(h));g=a.nextConnection&&a.nextConnection.getShadowDom();!g||d&&d.isShadow()||h.appendChild(Blockly.Xml.cloneShadow_(g,b));return c}; +Blockly.Xml.cloneShadow_=function(a,b){for(var c=a=a.cloneNode(!0),d;c;)if(b&&"shadow"==c.nodeName&&c.removeAttribute("id"),c.firstChild)c=c.firstChild;else{for(;c&&!c.nextSibling;)d=c,c=c.parentNode,d.nodeType==Blockly.utils.dom.NodeType.TEXT_NODE&&""==d.data.trim()&&c.firstChild!=d&&Blockly.utils.dom.removeNode(d);c&&(d=c,c=c.nextSibling,d.nodeType==Blockly.utils.dom.NodeType.TEXT_NODE&&""==d.data.trim()&&Blockly.utils.dom.removeNode(d))}return a}; +Blockly.Xml.domToText=function(a){a=Blockly.utils.xml.domToText(a);var b=/(<[^/](?:[^>]*[^/])?>[^<]*)\n([^<]*<\/)/;do{var c=a;a=a.replace(b,"$1 $2")}while(a!=c);return a.replace(/<(\w+)([^<]*)\/>/g,"<$1$2>")}; +Blockly.Xml.domToPrettyText=function(a){a=Blockly.Xml.domToText(a).split("<");for(var b="",c=1;c"!=d.slice(-2)&&(b+=" ")}a=a.join("\n");a=a.replace(/(<(\w+)\b[^>]*>[^\n]*)\n *<\/\2>/g,"$1");return a.replace(/^\n/,"")}; +Blockly.Xml.textToDom=function(a){var b=Blockly.utils.xml.textToDomDocument(a);if(!b||!b.documentElement||b.getElementsByTagName("parsererror").length)throw Error("textToDom was unable to parse: "+a);return b.documentElement};Blockly.Xml.clearWorkspaceAndLoadFromXml=function(a,b){b.setResizesEnabled(!1);b.clear();a=Blockly.Xml.domToWorkspace(a,b);b.setResizesEnabled(!0);return a}; +Blockly.Xml.domToWorkspace=function(a,b){if(a instanceof Blockly.Workspace){var c=a;a=b;b=c;console.warn("Deprecated call to Blockly.Xml.domToWorkspace, swap the arguments.")}var d;b.RTL&&(d=b.getWidth());c=[];Blockly.utils.dom.startTextWidthCache();var e=Blockly.Events.getGroup();e||Blockly.Events.setGroup(!0);b.setResizesEnabled&&b.setResizesEnabled(!1);var f=!0;try{for(var g=0,h;h=a.childNodes[g];g++){var k=h.nodeName.toLowerCase(),l=h;if("block"==k||"shadow"==k&&!Blockly.Events.recordUndo){var m= +Blockly.Xml.domToBlock(l,b);c.push(m.id);var n=l.hasAttribute("x")?parseInt(l.getAttribute("x"),10):10,p=l.hasAttribute("y")?parseInt(l.getAttribute("y"),10):10;isNaN(n)||isNaN(p)||m.moveBy(b.RTL?d-n:n,p);f=!1}else{if("shadow"==k)throw TypeError("Shadow block cannot be a top-level block.");if("comment"==k)b.rendered?Blockly.WorkspaceCommentSvg?Blockly.WorkspaceCommentSvg.fromXml(l,b,d):console.warn("Missing require for Blockly.WorkspaceCommentSvg, ignoring workspace comment."):Blockly.WorkspaceComment? +Blockly.WorkspaceComment.fromXml(l,b):console.warn("Missing require for Blockly.WorkspaceComment, ignoring workspace comment.");else if("variables"==k){if(f)Blockly.Xml.domToVariables(l,b);else throw Error("'variables' tag must exist once before block and shadow tag elements in the workspace XML, but it was found in another location.");f=!1}}}}finally{e||Blockly.Events.setGroup(!1),Blockly.utils.dom.stopTextWidthCache()}b.setResizesEnabled&&b.setResizesEnabled(!0);Blockly.Events.fire(new Blockly.Events.FinishedLoading(b)); +return c};Blockly.Xml.appendDomToWorkspace=function(a,b){var c;b.hasOwnProperty("scale")&&(c=b.getBlocksBoundingBox());a=Blockly.Xml.domToWorkspace(a,b);if(c&&c.top!=c.bottom){var d=c.bottom;var e=b.RTL?c.right:c.left;var f=Infinity,g=-Infinity,h=Infinity;for(c=0;cg&&(g=k.x)}d=d-h+10;e=b.RTL?e-g:e-f;for(c=0;c document.");}else a=null;return a};Blockly.Touch={};Blockly.Touch.TOUCH_ENABLED="ontouchstart"in Blockly.utils.global||!!(Blockly.utils.global.document&&document.documentElement&&"ontouchstart"in document.documentElement)||!(!Blockly.utils.global.navigator||!Blockly.utils.global.navigator.maxTouchPoints&&!Blockly.utils.global.navigator.msMaxTouchPoints);Blockly.Touch.touchIdentifier_=null;Blockly.Touch.TOUCH_MAP={}; +Blockly.utils.global.PointerEvent?Blockly.Touch.TOUCH_MAP={mousedown:["pointerdown"],mouseenter:["pointerenter"],mouseleave:["pointerleave"],mousemove:["pointermove"],mouseout:["pointerout"],mouseover:["pointerover"],mouseup:["pointerup","pointercancel"],touchend:["pointerup"],touchcancel:["pointercancel"]}:Blockly.Touch.TOUCH_ENABLED&&(Blockly.Touch.TOUCH_MAP={mousedown:["touchstart"],mousemove:["touchmove"],mouseup:["touchend","touchcancel"]});Blockly.longPid_=0; +Blockly.longStart=function(a,b){Blockly.longStop_();a.changedTouches&&1!=a.changedTouches.length||(Blockly.longPid_=setTimeout(function(){a.changedTouches&&(a.button=2,a.clientX=a.changedTouches[0].clientX,a.clientY=a.changedTouches[0].clientY);b&&b.handleRightClick(a)},Blockly.LONGPRESS))};Blockly.longStop_=function(){Blockly.longPid_&&(clearTimeout(Blockly.longPid_),Blockly.longPid_=0)};Blockly.Touch.clearTouchIdentifier=function(){Blockly.Touch.touchIdentifier_=null}; +Blockly.Touch.shouldHandleEvent=function(a){return!Blockly.Touch.isMouseOrTouchEvent(a)||Blockly.Touch.checkTouchIdentifier(a)};Blockly.Touch.getTouchIdentifierFromEvent=function(a){return void 0!=a.pointerId?a.pointerId:a.changedTouches&&a.changedTouches[0]&&void 0!==a.changedTouches[0].identifier&&null!==a.changedTouches[0].identifier?a.changedTouches[0].identifier:"mouse"}; +Blockly.Touch.checkTouchIdentifier=function(a){var b=Blockly.Touch.getTouchIdentifierFromEvent(a);return void 0!==Blockly.Touch.touchIdentifier_&&null!==Blockly.Touch.touchIdentifier_?Blockly.Touch.touchIdentifier_==b:"mousedown"==a.type||"touchstart"==a.type||"pointerdown"==a.type?(Blockly.Touch.touchIdentifier_=b,!0):!1};Blockly.Touch.setClientFromTouch=function(a){if(Blockly.utils.string.startsWith(a.type,"touch")){var b=a.changedTouches[0];a.clientX=b.clientX;a.clientY=b.clientY}}; +Blockly.Touch.isMouseOrTouchEvent=function(a){return Blockly.utils.string.startsWith(a.type,"touch")||Blockly.utils.string.startsWith(a.type,"mouse")||Blockly.utils.string.startsWith(a.type,"pointer")};Blockly.Touch.isTouchEvent=function(a){return Blockly.utils.string.startsWith(a.type,"touch")||Blockly.utils.string.startsWith(a.type,"pointer")}; +Blockly.Touch.splitEventByTouches=function(a){var b=[];if(a.changedTouches)for(var c=0;c=c+this.handleLength_&&(d+= +e);this.setHandlePosition(this.constrainHandle_(d));this.onScroll_();a.stopPropagation();a.preventDefault()}}; +Blockly.Scrollbar.prototype.onMouseDownHandle_=function(a){this.workspace_.markFocused();this.cleanUp_();Blockly.utils.isRightButton(a)?a.stopPropagation():(this.startDragHandle=this.handlePosition_,this.workspace_.setupDragSurface(),this.startDragMouse_=this.horizontal_?a.clientX:a.clientY,Blockly.Scrollbar.onMouseUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",this,this.onMouseUpHandle_),Blockly.Scrollbar.onMouseMoveWrapper_=Blockly.bindEventWithChecks_(document,"mousemove",this,this.onMouseMoveHandle_), +a.stopPropagation(),a.preventDefault())};Blockly.Scrollbar.prototype.onMouseMoveHandle_=function(a){this.setHandlePosition(this.constrainHandle_(this.startDragHandle+((this.horizontal_?a.clientX:a.clientY)-this.startDragMouse_)));this.onScroll_()};Blockly.Scrollbar.prototype.onMouseUpHandle_=function(){this.workspace_.resetDragSurface();Blockly.Touch.clearTouchIdentifier();this.cleanUp_()}; +Blockly.Scrollbar.prototype.cleanUp_=function(){Blockly.hideChaff(!0);Blockly.Scrollbar.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Scrollbar.onMouseUpWrapper_),Blockly.Scrollbar.onMouseUpWrapper_=null);Blockly.Scrollbar.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Scrollbar.onMouseMoveWrapper_),Blockly.Scrollbar.onMouseMoveWrapper_=null)}; +Blockly.Scrollbar.prototype.constrainHandle_=function(a){return a=0>=a||isNaN(a)||this.scrollViewSize_Blockly.Tooltip.RADIUS_OK&&Blockly.Tooltip.hide()}else Blockly.Tooltip.poisonedElement_!=Blockly.Tooltip.element_&&(clearTimeout(Blockly.Tooltip.showPid_),Blockly.Tooltip.lastX_=a.pageX,Blockly.Tooltip.lastY_=a.pageY,Blockly.Tooltip.showPid_=setTimeout(Blockly.Tooltip.show_, +Blockly.Tooltip.HOVER_MS))};Blockly.Tooltip.dispose=function(){Blockly.Tooltip.element_=null;Blockly.Tooltip.poisonedElement_=null;Blockly.Tooltip.hide()};Blockly.Tooltip.hide=function(){Blockly.Tooltip.visible&&(Blockly.Tooltip.visible=!1,Blockly.Tooltip.DIV&&(Blockly.Tooltip.DIV.style.display="none"));Blockly.Tooltip.showPid_&&clearTimeout(Blockly.Tooltip.showPid_)};Blockly.Tooltip.block=function(){Blockly.Tooltip.hide();Blockly.Tooltip.blocked_=!0}; +Blockly.Tooltip.unblock=function(){Blockly.Tooltip.blocked_=!1}; +Blockly.Tooltip.show_=function(){if(!Blockly.Tooltip.blocked_&&(Blockly.Tooltip.poisonedElement_=Blockly.Tooltip.element_,Blockly.Tooltip.DIV)){Blockly.Tooltip.DIV.textContent="";for(var a=Blockly.Tooltip.element_.tooltip;"function"==typeof a;)a=a();a=Blockly.utils.string.wrap(a,Blockly.Tooltip.LIMIT);a=a.split("\n");for(var b=0;bc+window.scrollY&&(e-=Blockly.Tooltip.DIV.offsetHeight+2*Blockly.Tooltip.OFFSET_Y);a?d=Math.max(Blockly.Tooltip.MARGINS-window.scrollX, +d):d+Blockly.Tooltip.DIV.offsetWidth>b+window.scrollX-2*Blockly.Tooltip.MARGINS&&(d=b-Blockly.Tooltip.DIV.offsetWidth-2*Blockly.Tooltip.MARGINS);Blockly.Tooltip.DIV.style.top=e+"px";Blockly.Tooltip.DIV.style.left=d+"px"}};Blockly.WorkspaceDragSurfaceSvg=function(a){this.container_=a;this.createDom()};Blockly.WorkspaceDragSurfaceSvg.prototype.SVG_=null;Blockly.WorkspaceDragSurfaceSvg.prototype.container_=null; +Blockly.WorkspaceDragSurfaceSvg.prototype.createDom=function(){this.SVG_||(this.SVG_=Blockly.utils.dom.createSvgElement("svg",{xmlns:Blockly.utils.dom.SVG_NS,"xmlns:html":Blockly.utils.dom.HTML_NS,"xmlns:xlink":Blockly.utils.dom.XLINK_NS,version:"1.1","class":"blocklyWsDragSurface blocklyOverflowVisible"},null),this.container_.appendChild(this.SVG_))}; +Blockly.WorkspaceDragSurfaceSvg.prototype.translateSurface=function(a,b){a=a.toFixed(0);b=b.toFixed(0);this.SVG_.style.display="block";Blockly.utils.dom.setCssTransform(this.SVG_,"translate3d("+a+"px, "+b+"px, 0px)")};Blockly.WorkspaceDragSurfaceSvg.prototype.getSurfaceTranslation=function(){return Blockly.utils.getRelativeXY(this.SVG_)}; +Blockly.WorkspaceDragSurfaceSvg.prototype.clearAndHide=function(a){if(!a)throw Error("Couldn't clear and hide the drag surface: missing new surface.");var b=this.SVG_.childNodes[0],c=this.SVG_.childNodes[1];if(!(b&&c&&Blockly.utils.dom.hasClass(b,"blocklyBlockCanvas")&&Blockly.utils.dom.hasClass(c,"blocklyBubbleCanvas")))throw Error("Couldn't clear and hide the drag surface. A node was missing.");null!=this.previousSibling_?Blockly.utils.dom.insertAfter(b,this.previousSibling_):a.insertBefore(b,a.firstChild); +Blockly.utils.dom.insertAfter(c,b);this.SVG_.style.display="none";if(this.SVG_.childNodes.length)throw Error("Drag surface was not cleared.");Blockly.utils.dom.setCssTransform(this.SVG_,"");this.previousSibling_=null}; +Blockly.WorkspaceDragSurfaceSvg.prototype.setContentsAndShow=function(a,b,c,d,e,f){if(this.SVG_.childNodes.length)throw Error("Already dragging a block.");this.previousSibling_=c;a.setAttribute("transform","translate(0, 0) scale("+f+")");b.setAttribute("transform","translate(0, 0) scale("+f+")");this.SVG_.setAttribute("width",d);this.SVG_.setAttribute("height",e);this.SVG_.appendChild(a);this.SVG_.appendChild(b);this.SVG_.style.display="block"};Blockly.ASTNode=function(a,b,c){if(!b)throw Error("Cannot create a node without a location.");this.type_=a;this.isConnection_=Blockly.ASTNode.isConnectionType_(a);this.location_=b;this.wsCoordinate_=null;this.processParams_(c||null)};Blockly.ASTNode.types={FIELD:"field",BLOCK:"block",INPUT:"input",OUTPUT:"output",NEXT:"next",PREVIOUS:"previous",STACK:"stack",WORKSPACE:"workspace"};Blockly.ASTNode.NAVIGATE_ALL_FIELDS=!1;Blockly.ASTNode.DEFAULT_OFFSET_Y=-20;Blockly.ASTNode.isConnectionType_=function(a){switch(a){case Blockly.ASTNode.types.PREVIOUS:case Blockly.ASTNode.types.NEXT:case Blockly.ASTNode.types.INPUT:case Blockly.ASTNode.types.OUTPUT:return!0}return!1}; +Blockly.ASTNode.createFieldNode=function(a){return a?new Blockly.ASTNode(Blockly.ASTNode.types.FIELD,a):null}; +Blockly.ASTNode.createConnectionNode=function(a){return a?a.type==Blockly.INPUT_VALUE||a.type==Blockly.NEXT_STATEMENT&&a.getParentInput()?Blockly.ASTNode.createInputNode(a.getParentInput()):a.type==Blockly.NEXT_STATEMENT?new Blockly.ASTNode(Blockly.ASTNode.types.NEXT,a):a.type==Blockly.OUTPUT_VALUE?new Blockly.ASTNode(Blockly.ASTNode.types.OUTPUT,a):a.type==Blockly.PREVIOUS_STATEMENT?new Blockly.ASTNode(Blockly.ASTNode.types.PREVIOUS,a):null:null}; +Blockly.ASTNode.createInputNode=function(a){return a&&a.connection?new Blockly.ASTNode(Blockly.ASTNode.types.INPUT,a.connection):null};Blockly.ASTNode.createBlockNode=function(a){return a?new Blockly.ASTNode(Blockly.ASTNode.types.BLOCK,a):null};Blockly.ASTNode.createStackNode=function(a){return a?new Blockly.ASTNode(Blockly.ASTNode.types.STACK,a):null};Blockly.ASTNode.createWorkspaceNode=function(a,b){return b&&a?new Blockly.ASTNode(Blockly.ASTNode.types.WORKSPACE,a,{wsCoordinate:b}):null}; +Blockly.ASTNode.createTopNode=function(a){var b=a.previousConnection||a.outputConnection;return b?Blockly.ASTNode.createConnectionNode(b):Blockly.ASTNode.createBlockNode(a)};Blockly.ASTNode.prototype.processParams_=function(a){a&&a.wsCoordinate&&(this.wsCoordinate_=a.wsCoordinate)};Blockly.ASTNode.prototype.getLocation=function(){return this.location_};Blockly.ASTNode.prototype.getType=function(){return this.type_};Blockly.ASTNode.prototype.getWsCoordinate=function(){return this.wsCoordinate_}; +Blockly.ASTNode.prototype.isConnection=function(){return this.isConnection_};Blockly.ASTNode.prototype.findNextForInput_=function(){var a=this.location_.getParentInput(),b=a.getSourceBlock();a=b.inputList.indexOf(a)+1;for(var c;c=b.inputList[a];a++){for(var d=c.fieldRow,e=0,f;f=d[e];e++)if(f.isClickable()||Blockly.ASTNode.NAVIGATE_ALL_FIELDS)return Blockly.ASTNode.createFieldNode(f);if(c.connection)return Blockly.ASTNode.createInputNode(c)}return null}; +Blockly.ASTNode.prototype.findNextForField_=function(){var a=this.location_,b=a.getParentInput(),c=a.getSourceBlock(),d=c.inputList.indexOf(b);for(a=b.fieldRow.indexOf(a)+1;b=c.inputList[d];d++){for(var e=b.fieldRow;ac)){var d=b.getSvgXY(a.getSvgRoot());a.outputConnection?(d.x+=(a.RTL?3:-3)*c,d.y+=13*c):a.previousConnection&&(d.x+=(a.RTL?-23:23)*c,d.y+=3*c);a=Blockly.utils.dom.createSvgElement("circle",{cx:d.x,cy:d.y,r:0,fill:"none",stroke:"#888","stroke-width":10},b.getParentSvg());Blockly.blockAnimations.connectionUiStep_(a,new Date,c)}}; +Blockly.blockAnimations.connectionUiStep_=function(a,b,c){var d=(new Date-b)/150;1a.workspace.scale)){var b=a.getHeightWidth().height;b=Math.atan(10/b)/Math.PI*180;a.RTL||(b*=-1);Blockly.blockAnimations.disconnectUiStep_(a.getSvgRoot(),b,new Date)}}; +Blockly.blockAnimations.disconnectUiStep_=function(a,b,c){var d=(new Date-c)/200;1b-Blockly.CURRENT_CONNECTION_PREFERENCE)}if(this.localConnection_||this.closestConnection_)console.error("Only one of localConnection_ and closestConnection_ was set."); +else return!0}else return!(!this.localConnection_||!this.closestConnection_);console.error("Returning true from shouldUpdatePreviews, but it's not clear why.");return!0};Blockly.InsertionMarkerManager.prototype.getCandidate_=function(a){for(var b=this.getStartRadius_(),c=null,d=null,e=0;ethis.remainingCapacityOfType(c))return!1;b+=a[c]}return b>this.remainingCapacity()?!1:!0}; +Blockly.Workspace.prototype.hasBlockLimits=function(){return Infinity!=this.options.maxBlocks||!!this.options.maxInstances};Blockly.Workspace.prototype.undo=function(a){var b=a?this.redoStack_:this.undoStack_,c=a?this.undoStack_:this.redoStack_,d=b.pop();if(d){for(var e=[d];b.length&&d.group&&d.group==b[b.length-1].group;)e.push(b.pop());for(b=0;d=e[b];b++)c.push(d);e=Blockly.Events.filter(e,a);Blockly.Events.recordUndo=!1;try{for(b=0;d=e[b];b++)d.run(a)}finally{Blockly.Events.recordUndo=!0}}}; +Blockly.Workspace.prototype.clearUndo=function(){this.undoStack_.length=0;this.redoStack_.length=0;Blockly.Events.clearPendingUndo()};Blockly.Workspace.prototype.addChangeListener=function(a){this.listeners_.push(a);return a};Blockly.Workspace.prototype.removeChangeListener=function(a){Blockly.utils.arrayRemove(this.listeners_,a)}; +Blockly.Workspace.prototype.fireChangeListener=function(a){if(a.recordUndo)for(this.undoStack_.push(a),this.redoStack_.length=0;this.undoStack_.length>this.MAX_UNDO&&0<=this.MAX_UNDO;)this.undoStack_.shift();for(var b=0,c;c=this.listeners_[b];b++)c(a)};Blockly.Workspace.prototype.getBlockById=function(a){return this.blockDB_[a]||null};Blockly.Workspace.prototype.setBlockById=function(a,b){this.blockDB_[a]=b};Blockly.Workspace.prototype.removeBlockById=function(a){delete this.blockDB_[a]}; +Blockly.Workspace.prototype.getCommentById=function(a){return this.commentDB_[a]||null};Blockly.Workspace.prototype.allInputsFilled=function(a){for(var b=this.getTopBlocks(!1),c=0,d;d=b[c];c++)if(!d.allInputsFilled(a))return!1;return!0};Blockly.Workspace.prototype.getPotentialVariableMap=function(){return this.potentialVariableMap_};Blockly.Workspace.prototype.createPotentialVariableMap=function(){this.potentialVariableMap_=new Blockly.VariableMap(this)}; +Blockly.Workspace.prototype.getVariableMap=function(){return this.variableMap_};Blockly.Workspace.prototype.setVariableMap=function(a){this.variableMap_=a};Blockly.Workspace.WorkspaceDB_=Object.create(null);Blockly.Workspace.getById=function(a){return Blockly.Workspace.WorkspaceDB_[a]||null};Blockly.Workspace.getAll=function(){var a=[],b;for(b in Blockly.Workspace.WorkspaceDB_)a.push(Blockly.Workspace.WorkspaceDB_[b]);return a};Blockly.Bubble=function(a,b,c,d,e,f){this.workspace_=a;this.content_=b;this.shape_=c;this.onMouseDownResizeWrapper_=this.onMouseDownBubbleWrapper_=this.moveCallback_=this.resizeCallback_=null;this.disposed=!1;c=Blockly.Bubble.ARROW_ANGLE;this.workspace_.RTL&&(c=-c);this.arrow_radians_=Blockly.utils.math.toRadians(c);a.getBubbleCanvas().appendChild(this.createDom_(b,!(!e||!f)));this.setAnchorLocation(d);e&&f||(a=this.content_.getBBox(),e=a.width+2*Blockly.Bubble.BORDER_WIDTH,f=a.height+2*Blockly.Bubble.BORDER_WIDTH); +this.setBubbleSize(e,f);this.positionBubble_();this.renderArrow_();this.rendered_=!0};Blockly.Bubble.BORDER_WIDTH=6;Blockly.Bubble.ARROW_THICKNESS=5;Blockly.Bubble.ARROW_ANGLE=20;Blockly.Bubble.ARROW_BEND=4;Blockly.Bubble.ANCHOR_RADIUS=8;Blockly.Bubble.onMouseUpWrapper_=null;Blockly.Bubble.onMouseMoveWrapper_=null; +Blockly.Bubble.unbindDragEvents_=function(){Blockly.Bubble.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Bubble.onMouseUpWrapper_),Blockly.Bubble.onMouseUpWrapper_=null);Blockly.Bubble.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Bubble.onMouseMoveWrapper_),Blockly.Bubble.onMouseMoveWrapper_=null)};Blockly.Bubble.bubbleMouseUp_=function(a){Blockly.Touch.clearTouchIdentifier();Blockly.Bubble.unbindDragEvents_()};Blockly.Bubble.prototype.rendered_=!1;Blockly.Bubble.prototype.anchorXY_=null; +Blockly.Bubble.prototype.relativeLeft_=0;Blockly.Bubble.prototype.relativeTop_=0;Blockly.Bubble.prototype.width_=0;Blockly.Bubble.prototype.height_=0;Blockly.Bubble.prototype.autoLayout_=!0; +Blockly.Bubble.prototype.createDom_=function(a,b){this.bubbleGroup_=Blockly.utils.dom.createSvgElement("g",{},null);var c={filter:"url(#"+this.workspace_.getRenderer().getConstants().embossFilterId+")"};Blockly.utils.userAgent.JAVA_FX&&(c={});c=Blockly.utils.dom.createSvgElement("g",c,this.bubbleGroup_);this.bubbleArrow_=Blockly.utils.dom.createSvgElement("path",{},c);this.bubbleBack_=Blockly.utils.dom.createSvgElement("rect",{"class":"blocklyDraggable",x:0,y:0,rx:Blockly.Bubble.BORDER_WIDTH,ry:Blockly.Bubble.BORDER_WIDTH}, +c);b?(this.resizeGroup_=Blockly.utils.dom.createSvgElement("g",{"class":this.workspace_.RTL?"blocklyResizeSW":"blocklyResizeSE"},this.bubbleGroup_),b=2*Blockly.Bubble.BORDER_WIDTH,Blockly.utils.dom.createSvgElement("polygon",{points:"0,x x,x x,0".replace(/x/g,b.toString())},this.resizeGroup_),Blockly.utils.dom.createSvgElement("line",{"class":"blocklyResizeLine",x1:b/3,y1:b-1,x2:b-1,y2:b/3},this.resizeGroup_),Blockly.utils.dom.createSvgElement("line",{"class":"blocklyResizeLine",x1:2*b/3,y1:b-1,x2:b- +1,y2:2*b/3},this.resizeGroup_)):this.resizeGroup_=null;this.workspace_.options.readOnly||(this.onMouseDownBubbleWrapper_=Blockly.bindEventWithChecks_(this.bubbleBack_,"mousedown",this,this.bubbleMouseDown_),this.resizeGroup_&&(this.onMouseDownResizeWrapper_=Blockly.bindEventWithChecks_(this.resizeGroup_,"mousedown",this,this.resizeMouseDown_)));this.bubbleGroup_.appendChild(a);return this.bubbleGroup_};Blockly.Bubble.prototype.getSvgRoot=function(){return this.bubbleGroup_}; +Blockly.Bubble.prototype.setSvgId=function(a){this.bubbleGroup_.dataset&&(this.bubbleGroup_.dataset.blockId=a)};Blockly.Bubble.prototype.bubbleMouseDown_=function(a){var b=this.workspace_.getGesture(a);b&&b.handleBubbleStart(a,this)};Blockly.Bubble.prototype.showContextMenu=function(a){};Blockly.Bubble.prototype.isDeletable=function(){return!1}; +Blockly.Bubble.prototype.resizeMouseDown_=function(a){this.promote();Blockly.Bubble.unbindDragEvents_();Blockly.utils.isRightButton(a)||(this.workspace_.startDrag(a,new Blockly.utils.Coordinate(this.workspace_.RTL?-this.width_:this.width_,this.height_)),Blockly.Bubble.onMouseUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",this,Blockly.Bubble.bubbleMouseUp_),Blockly.Bubble.onMouseMoveWrapper_=Blockly.bindEventWithChecks_(document,"mousemove",this,this.resizeMouseMove_),Blockly.hideChaff()); +a.stopPropagation()};Blockly.Bubble.prototype.resizeMouseMove_=function(a){this.autoLayout_=!1;a=this.workspace_.moveDrag(a);this.setBubbleSize(this.workspace_.RTL?-a.x:a.x,a.y);this.workspace_.RTL&&this.positionBubble_()};Blockly.Bubble.prototype.registerResizeEvent=function(a){this.resizeCallback_=a};Blockly.Bubble.prototype.registerMoveEvent=function(a){this.moveCallback_=a}; +Blockly.Bubble.prototype.promote=function(){var a=this.bubbleGroup_.parentNode;return a.lastChild!==this.bubbleGroup_?(a.appendChild(this.bubbleGroup_),!0):!1};Blockly.Bubble.prototype.setAnchorLocation=function(a){this.anchorXY_=a;this.rendered_&&this.positionBubble_()}; +Blockly.Bubble.prototype.layoutBubble_=function(){var a=this.workspace_.getMetrics();a.viewLeft/=this.workspace_.scale;a.viewWidth/=this.workspace_.scale;a.viewTop/=this.workspace_.scale;a.viewHeight/=this.workspace_.scale;var b=this.getOptimalRelativeLeft_(a),c=this.getOptimalRelativeTop_(a),d=this.shape_.getBBox(),e={x:b,y:-this.height_-this.workspace_.getRenderer().getConstants().MIN_BLOCK_HEIGHT},f={x:-this.width_-30,y:c};c={x:d.width,y:c};var g={x:b,y:d.height};b=d.widtha.viewWidth)return b;if(this.workspace_.RTL)var c=this.anchorXY_.x-b,d=c-this.width_,e=a.viewLeft+a.viewWidth,f=a.viewLeft+Blockly.Scrollbar.scrollbarThickness/this.workspace_.scale;else d=b+this.anchorXY_.x,c=d+this.width_,f=a.viewLeft,e=a.viewLeft+a.viewWidth-Blockly.Scrollbar.scrollbarThickness/this.workspace_.scale;this.workspace_.RTL?de&&(b=-(e-this.anchorXY_.x)): +de&&(b=e-this.anchorXY_.x-this.width_);return b};Blockly.Bubble.prototype.getOptimalRelativeTop_=function(a){var b=-this.height_/4;if(this.height_>a.viewHeight)return b;var c=this.anchorXY_.y+b,d=c+this.height_,e=a.viewTop;a=a.viewTop+a.viewHeight-Blockly.Scrollbar.scrollbarThickness/this.workspace_.scale;var f=this.anchorXY_.y;ca&&(b=a-f-this.height_);return b}; +Blockly.Bubble.prototype.positionBubble_=function(){var a=this.anchorXY_.x;a=this.workspace_.RTL?a-(this.relativeLeft_+this.width_):a+this.relativeLeft_;this.moveTo(a,this.relativeTop_+this.anchorXY_.y)};Blockly.Bubble.prototype.moveTo=function(a,b){this.bubbleGroup_.setAttribute("transform","translate("+a+","+b+")")};Blockly.Bubble.prototype.setDragging=function(a){!a&&this.moveCallback_&&this.moveCallback_()}; +Blockly.Bubble.prototype.getBubbleSize=function(){return new Blockly.utils.Size(this.width_,this.height_)}; +Blockly.Bubble.prototype.setBubbleSize=function(a,b){var c=2*Blockly.Bubble.BORDER_WIDTH;a=Math.max(a,c+45);b=Math.max(b,c+20);this.width_=a;this.height_=b;this.bubbleBack_.setAttribute("width",a);this.bubbleBack_.setAttribute("height",b);this.resizeGroup_&&(this.workspace_.RTL?this.resizeGroup_.setAttribute("transform","translate("+2*Blockly.Bubble.BORDER_WIDTH+","+(b-c)+") scale(-1 1)"):this.resizeGroup_.setAttribute("transform","translate("+(a-c)+","+(b-c)+")"));this.autoLayout_&&this.layoutBubble_(); +this.positionBubble_();this.renderArrow_();this.resizeCallback_&&this.resizeCallback_()}; +Blockly.Bubble.prototype.renderArrow_=function(){var a=[],b=this.width_/2,c=this.height_/2,d=-this.relativeLeft_,e=-this.relativeTop_;if(b==d&&c==e)a.push("M "+b+","+c);else{e-=c;d-=b;this.workspace_.RTL&&(d*=-1);var f=Math.sqrt(e*e+d*d),g=Math.acos(d/f);0>e&&(g=2*Math.PI-g);var h=g+Math.PI/2;h>2*Math.PI&&(h-=2*Math.PI);var k=Math.sin(h),l=Math.cos(h),m=this.getBubbleSize();h=(m.width+m.height)/Blockly.Bubble.ARROW_THICKNESS;h=Math.min(h,m.width,m.height)/4;m=1-Blockly.Bubble.ANCHOR_RADIUS/f;d=b+ +m*d;e=c+m*e;m=b+h*l;var n=c+h*k;b-=h*l;c-=h*k;k=g+this.arrow_radians_;k>2*Math.PI&&(k-=2*Math.PI);g=Math.sin(k)*f/Blockly.Bubble.ARROW_BEND;f=Math.cos(k)*f/Blockly.Bubble.ARROW_BEND;a.push("M"+m+","+n);a.push("C"+(m+f)+","+(n+g)+" "+d+","+e+" "+d+","+e);a.push("C"+d+","+e+" "+(b+f)+","+(c+g)+" "+b+","+c)}a.push("z");this.bubbleArrow_.setAttribute("d",a.join(" "))};Blockly.Bubble.prototype.setColour=function(a){this.bubbleBack_.setAttribute("fill",a);this.bubbleArrow_.setAttribute("fill",a)}; +Blockly.Bubble.prototype.dispose=function(){this.onMouseDownBubbleWrapper_&&Blockly.unbindEvent_(this.onMouseDownBubbleWrapper_);this.onMouseDownResizeWrapper_&&Blockly.unbindEvent_(this.onMouseDownResizeWrapper_);Blockly.Bubble.unbindDragEvents_();Blockly.utils.dom.removeNode(this.bubbleGroup_);this.disposed=!0}; +Blockly.Bubble.prototype.moveDuringDrag=function(a,b){a?a.translateSurface(b.x,b.y):this.moveTo(b.x,b.y);this.relativeLeft_=this.workspace_.RTL?this.anchorXY_.x-b.x-this.width_:b.x-this.anchorXY_.x;this.relativeTop_=b.y-this.anchorXY_.y;this.renderArrow_()};Blockly.Bubble.prototype.getRelativeToSurfaceXY=function(){return new Blockly.utils.Coordinate(this.workspace_.RTL?-this.relativeLeft_+this.anchorXY_.x-this.width_:this.anchorXY_.x+this.relativeLeft_,this.anchorXY_.y+this.relativeTop_)}; +Blockly.Bubble.prototype.setAutoLayout=function(a){this.autoLayout_=a};Blockly.Events.CommentBase=function(a){this.commentId=a.id;this.workspaceId=a.workspace.id;this.group=Blockly.Events.getGroup();this.recordUndo=Blockly.Events.recordUndo};Blockly.utils.object.inherits(Blockly.Events.CommentBase,Blockly.Events.Abstract);Blockly.Events.CommentBase.prototype.toJson=function(){var a=Blockly.Events.CommentBase.superClass_.toJson.call(this);this.commentId&&(a.commentId=this.commentId);return a}; +Blockly.Events.CommentBase.prototype.fromJson=function(a){Blockly.Events.CommentBase.superClass_.fromJson.call(this,a);this.commentId=a.commentId};Blockly.Events.CommentChange=function(a,b,c){a&&(Blockly.Events.CommentChange.superClass_.constructor.call(this,a),this.oldContents_=b,this.newContents_=c)};Blockly.utils.object.inherits(Blockly.Events.CommentChange,Blockly.Events.CommentBase);Blockly.Events.CommentChange.prototype.type=Blockly.Events.COMMENT_CHANGE; +Blockly.Events.CommentChange.prototype.toJson=function(){var a=Blockly.Events.CommentChange.superClass_.toJson.call(this);a.newContents=this.newContents_;return a};Blockly.Events.CommentChange.prototype.fromJson=function(a){Blockly.Events.CommentChange.superClass_.fromJson.call(this,a);this.newContents_=a.newValue};Blockly.Events.CommentChange.prototype.isNull=function(){return this.oldContents_==this.newContents_}; +Blockly.Events.CommentChange.prototype.run=function(a){var b=this.getEventWorkspace_().getCommentById(this.commentId);b?b.setContent(a?this.newContents_:this.oldContents_):console.warn("Can't change non-existent comment: "+this.commentId)};Blockly.Events.CommentCreate=function(a){a&&(Blockly.Events.CommentCreate.superClass_.constructor.call(this,a),this.xml=a.toXmlWithXY())};Blockly.utils.object.inherits(Blockly.Events.CommentCreate,Blockly.Events.CommentBase); +Blockly.Events.CommentCreate.prototype.type=Blockly.Events.COMMENT_CREATE;Blockly.Events.CommentCreate.prototype.toJson=function(){var a=Blockly.Events.CommentCreate.superClass_.toJson.call(this);a.xml=Blockly.Xml.domToText(this.xml);return a};Blockly.Events.CommentCreate.prototype.fromJson=function(a){Blockly.Events.CommentCreate.superClass_.fromJson.call(this,a);this.xml=Blockly.Xml.textToDom(a.xml)}; +Blockly.Events.CommentCreate.prototype.run=function(a){Blockly.Events.CommentCreateDeleteHelper(this,a)};Blockly.Events.CommentCreateDeleteHelper=function(a,b){var c=a.getEventWorkspace_();b?(b=Blockly.utils.xml.createElement("xml"),b.appendChild(a.xml),Blockly.Xml.domToWorkspace(b,c)):(c=c.getCommentById(a.commentId))?c.dispose(!1,!1):console.warn("Can't uncreate non-existent comment: "+a.commentId)}; +Blockly.Events.CommentDelete=function(a){a&&(Blockly.Events.CommentDelete.superClass_.constructor.call(this,a),this.xml=a.toXmlWithXY())};Blockly.utils.object.inherits(Blockly.Events.CommentDelete,Blockly.Events.CommentBase);Blockly.Events.CommentDelete.prototype.type=Blockly.Events.COMMENT_DELETE;Blockly.Events.CommentDelete.prototype.toJson=function(){return Blockly.Events.CommentDelete.superClass_.toJson.call(this)}; +Blockly.Events.CommentDelete.prototype.fromJson=function(a){Blockly.Events.CommentDelete.superClass_.fromJson.call(this,a)};Blockly.Events.CommentDelete.prototype.run=function(a){Blockly.Events.CommentCreateDeleteHelper(this,!a)};Blockly.Events.CommentMove=function(a){a&&(Blockly.Events.CommentMove.superClass_.constructor.call(this,a),this.comment_=a,this.oldCoordinate_=a.getXY(),this.newCoordinate_=null)};Blockly.utils.object.inherits(Blockly.Events.CommentMove,Blockly.Events.CommentBase); +Blockly.Events.CommentMove.prototype.recordNew=function(){if(!this.comment_)throw Error("Tried to record the new position of a comment on the same event twice.");this.newCoordinate_=this.comment_.getXY();this.comment_=null};Blockly.Events.CommentMove.prototype.type=Blockly.Events.COMMENT_MOVE;Blockly.Events.CommentMove.prototype.setOldCoordinate=function(a){this.oldCoordinate_=a}; +Blockly.Events.CommentMove.prototype.toJson=function(){var a=Blockly.Events.CommentMove.superClass_.toJson.call(this);this.newCoordinate_&&(a.newCoordinate=Math.round(this.newCoordinate_.x)+","+Math.round(this.newCoordinate_.y));return a};Blockly.Events.CommentMove.prototype.fromJson=function(a){Blockly.Events.CommentMove.superClass_.fromJson.call(this,a);a.newCoordinate&&(a=a.newCoordinate.split(","),this.newCoordinate_=new Blockly.utils.Coordinate(Number(a[0]),Number(a[1])))}; +Blockly.Events.CommentMove.prototype.isNull=function(){return Blockly.utils.Coordinate.equals(this.oldCoordinate_,this.newCoordinate_)};Blockly.Events.CommentMove.prototype.run=function(a){var b=this.getEventWorkspace_().getCommentById(this.commentId);if(b){a=a?this.newCoordinate_:this.oldCoordinate_;var c=b.getXY();b.moveBy(a.x-c.x,a.y-c.y)}else console.warn("Can't move non-existent comment: "+this.commentId)};Blockly.BubbleDragger=function(a,b){this.draggingBubble_=a;this.workspace_=b;this.deleteArea_=null;this.wouldDeleteBubble_=!1;this.startXY_=this.draggingBubble_.getRelativeToSurfaceXY();this.dragSurface_=Blockly.utils.is3dSupported()&&b.getBlockDragSurface()?b.getBlockDragSurface():null};Blockly.BubbleDragger.prototype.dispose=function(){this.dragSurface_=this.workspace_=this.draggingBubble_=null}; +Blockly.BubbleDragger.prototype.startBubbleDrag=function(){Blockly.Events.getGroup()||Blockly.Events.setGroup(!0);this.workspace_.setResizesEnabled(!1);this.draggingBubble_.setAutoLayout(!1);this.dragSurface_&&this.moveToDragSurface_();this.draggingBubble_.setDragging&&this.draggingBubble_.setDragging(!0);var a=this.workspace_.getToolbox();if(a&&"function"==typeof a.addStyle){var b=this.draggingBubble_.isDeletable()?"blocklyToolboxDelete":"blocklyToolboxGrab";a.addStyle(b)}}; +Blockly.BubbleDragger.prototype.dragBubble=function(a,b){b=this.pixelsToWorkspaceUnits_(b);b=Blockly.utils.Coordinate.sum(this.startXY_,b);this.draggingBubble_.moveDuringDrag(this.dragSurface_,b);this.draggingBubble_.isDeletable()&&(this.deleteArea_=this.workspace_.isDeleteArea(a),this.updateCursorDuringBubbleDrag_())}; +Blockly.BubbleDragger.prototype.maybeDeleteBubble_=function(){var a=this.workspace_.trashcan;this.wouldDeleteBubble_?(a&&setTimeout(a.close.bind(a),100),this.fireMoveEvent_(),this.draggingBubble_.dispose(!1,!0)):a&&a.close();return this.wouldDeleteBubble_}; +Blockly.BubbleDragger.prototype.updateCursorDuringBubbleDrag_=function(){this.wouldDeleteBubble_=this.deleteArea_!=Blockly.DELETE_AREA_NONE;var a=this.workspace_.trashcan;this.wouldDeleteBubble_?(this.draggingBubble_.setDeleteStyle(!0),this.deleteArea_==Blockly.DELETE_AREA_TRASH&&a&&a.setOpen(!0)):(this.draggingBubble_.setDeleteStyle(!1),a&&a.setOpen(!1))}; +Blockly.BubbleDragger.prototype.endBubbleDrag=function(a,b){this.dragBubble(a,b);a=this.pixelsToWorkspaceUnits_(b);a=Blockly.utils.Coordinate.sum(this.startXY_,a);this.draggingBubble_.moveTo(a.x,a.y);this.maybeDeleteBubble_()||(this.dragSurface_&&this.dragSurface_.clearAndHide(this.workspace_.getBubbleCanvas()),this.draggingBubble_.setDragging&&this.draggingBubble_.setDragging(!1),this.fireMoveEvent_());this.workspace_.setResizesEnabled(!0);(a=this.workspace_.getToolbox())&&"function"==typeof a.removeStyle&& +(b=this.draggingBubble_.isDeletable()?"blocklyToolboxDelete":"blocklyToolboxGrab",a.removeStyle(b));Blockly.Events.setGroup(!1)};Blockly.BubbleDragger.prototype.fireMoveEvent_=function(){if(this.draggingBubble_.isComment){var a=new Blockly.Events.CommentMove(this.draggingBubble_);a.setOldCoordinate(this.startXY_);a.recordNew();Blockly.Events.fire(a)}}; +Blockly.BubbleDragger.prototype.pixelsToWorkspaceUnits_=function(a){a=new Blockly.utils.Coordinate(a.x/this.workspace_.scale,a.y/this.workspace_.scale);this.workspace_.isMutator&&a.scale(1/this.workspace_.options.parentWorkspace.scale);return a};Blockly.BubbleDragger.prototype.moveToDragSurface_=function(){this.draggingBubble_.moveTo(0,0);this.dragSurface_.translateSurface(this.startXY_.x,this.startXY_.y);this.dragSurface_.setBlocksAndShow(this.draggingBubble_.getSvgRoot())};Blockly.WorkspaceDragger=function(a){this.workspace_=a;this.startScrollXY_=new Blockly.utils.Coordinate(a.scrollX,a.scrollY)};Blockly.WorkspaceDragger.prototype.dispose=function(){this.workspace_=null};Blockly.WorkspaceDragger.prototype.startDrag=function(){Blockly.selected&&Blockly.selected.unselect();this.workspace_.setupDragSurface()};Blockly.WorkspaceDragger.prototype.endDrag=function(a){this.drag(a);this.workspace_.resetDragSurface()}; +Blockly.WorkspaceDragger.prototype.drag=function(a){a=Blockly.utils.Coordinate.sum(this.startScrollXY_,a);this.workspace_.scroll(a.x,a.y)};Blockly.FlyoutDragger=function(a){Blockly.FlyoutDragger.superClass_.constructor.call(this,a.getWorkspace());this.scrollbar_=a.scrollbar;this.horizontalLayout_=a.horizontalLayout};Blockly.utils.object.inherits(Blockly.FlyoutDragger,Blockly.WorkspaceDragger);Blockly.FlyoutDragger.prototype.drag=function(a){a=Blockly.utils.Coordinate.sum(this.startScrollXY_,a);this.horizontalLayout_?this.scrollbar_.set(-a.x):this.scrollbar_.set(-a.y)};Blockly.Action=function(a,b){this.name=a;this.desc=b};Blockly.navigation={};Blockly.navigation.loggingCallback=null;Blockly.navigation.STATE_FLYOUT=1;Blockly.navigation.STATE_WS=2;Blockly.navigation.STATE_TOOLBOX=3;Blockly.navigation.WS_MOVE_DISTANCE=40;Blockly.navigation.currentState_=Blockly.navigation.STATE_WS; +Blockly.navigation.actionNames={PREVIOUS:"previous",NEXT:"next",IN:"in",OUT:"out",INSERT:"insert",MARK:"mark",DISCONNECT:"disconnect",TOOLBOX:"toolbox",EXIT:"exit",TOGGLE_KEYBOARD_NAV:"toggle_keyboard_nav",MOVE_WS_CURSOR_UP:"move workspace cursor up",MOVE_WS_CURSOR_DOWN:"move workspace cursor down",MOVE_WS_CURSOR_LEFT:"move workspace cursor left",MOVE_WS_CURSOR_RIGHT:"move workspace cursor right"};Blockly.navigation.MARKER_NAME="local_marker_1";Blockly.navigation.getMarker=function(){return Blockly.navigation.getNavigationWorkspace().getMarker(Blockly.navigation.MARKER_NAME)}; +Blockly.navigation.getNavigationWorkspace=function(){return Blockly.getMainWorkspace()};Blockly.navigation.focusToolbox_=function(){var a=Blockly.navigation.getNavigationWorkspace().getToolbox();a&&(Blockly.navigation.currentState_=Blockly.navigation.STATE_TOOLBOX,Blockly.navigation.resetFlyout_(!1),Blockly.navigation.getMarker().getCurNode()||Blockly.navigation.markAtCursor_(),a.selectFirstCategory())}; +Blockly.navigation.focusFlyout_=function(){Blockly.navigation.currentState_=Blockly.navigation.STATE_FLYOUT;var a=Blockly.navigation.getNavigationWorkspace();var b=a.getToolbox();a=b?b.getFlyout():a.getFlyout();Blockly.navigation.getMarker().getCurNode()||Blockly.navigation.markAtCursor_();a&&a.getWorkspace()&&(a=a.getWorkspace().getTopBlocks(!0),0(this.flyout_?Blockly.FLYOUT_DRAG_RADIUS:Blockly.DRAG_RADIUS)}; +Blockly.Gesture.prototype.updateIsDraggingFromFlyout_=function(){return this.targetBlock_&&this.flyout_.isBlockCreatable_(this.targetBlock_)?!this.flyout_.isScrollable()||this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)?(this.startWorkspace_=this.flyout_.targetWorkspace,this.startWorkspace_.updateScreenCalculationsIfScrolled(),Blockly.Events.getGroup()||Blockly.Events.setGroup(!0),this.startBlock_=null,this.targetBlock_=this.flyout_.createBlock(this.targetBlock_),this.targetBlock_.select(), +!0):!1:!1};Blockly.Gesture.prototype.updateIsDraggingBubble_=function(){if(!this.startBubble_)return!1;this.isDraggingBubble_=!0;this.startDraggingBubble_();return!0};Blockly.Gesture.prototype.updateIsDraggingBlock_=function(){if(!this.targetBlock_)return!1;this.flyout_?this.isDraggingBlock_=this.updateIsDraggingFromFlyout_():this.targetBlock_.isMovable()&&(this.isDraggingBlock_=!0);return this.isDraggingBlock_?(this.startDraggingBlock_(),!0):!1}; +Blockly.Gesture.prototype.updateIsDraggingWorkspace_=function(){if(this.flyout_?this.flyout_.isScrollable():this.startWorkspace_&&this.startWorkspace_.isDraggable())this.workspaceDragger_=this.flyout_?new Blockly.FlyoutDragger(this.flyout_):new Blockly.WorkspaceDragger(this.startWorkspace_),this.isDraggingWorkspace_=!0,this.workspaceDragger_.startDrag()}; +Blockly.Gesture.prototype.updateIsDragging_=function(){if(this.calledUpdateIsDragging_)throw Error("updateIsDragging_ should only be called once per gesture.");this.calledUpdateIsDragging_=!0;this.updateIsDraggingBubble_()||this.updateIsDraggingBlock_()||this.updateIsDraggingWorkspace_()}; +Blockly.Gesture.prototype.startDraggingBlock_=function(){this.blockDragger_=new Blockly.BlockDragger(this.targetBlock_,this.startWorkspace_);this.blockDragger_.startBlockDrag(this.currentDragDeltaXY_,this.healStack_);this.blockDragger_.dragBlock(this.mostRecentEvent_,this.currentDragDeltaXY_)}; +Blockly.Gesture.prototype.startDraggingBubble_=function(){this.bubbleDragger_=new Blockly.BubbleDragger(this.startBubble_,this.startWorkspace_);this.bubbleDragger_.startBubbleDrag();this.bubbleDragger_.dragBubble(this.mostRecentEvent_,this.currentDragDeltaXY_)}; +Blockly.Gesture.prototype.doStart=function(a){Blockly.utils.isTargetInput(a)?this.cancel():(this.hasStarted_=!0,Blockly.blockAnimations.disconnectUiStop(),this.startWorkspace_.updateScreenCalculationsIfScrolled(),this.startWorkspace_.isMutator&&this.startWorkspace_.resize(),Blockly.hideChaff(!!this.flyout_),this.startWorkspace_.markFocused(),this.mostRecentEvent_=a,Blockly.Tooltip.block(),this.targetBlock_&&(!this.targetBlock_.isInFlyout&&a.shiftKey&&this.targetBlock_.workspace.keyboardAccessibilityMode? +this.creatorWorkspace_.getCursor().setCurNode(Blockly.ASTNode.createTopNode(this.targetBlock_)):this.targetBlock_.select()),Blockly.utils.isRightButton(a)?this.handleRightClick(a):("touchstart"!=a.type.toLowerCase()&&"pointerdown"!=a.type.toLowerCase()||"mouse"==a.pointerType||Blockly.longStart(a,this),this.mouseDownXY_=new Blockly.utils.Coordinate(a.clientX,a.clientY),this.healStack_=a.altKey||a.ctrlKey||a.metaKey,this.bindMouseEvents(a)))}; +Blockly.Gesture.prototype.bindMouseEvents=function(a){this.onMoveWrapper_=Blockly.bindEventWithChecks_(document,"mousemove",null,this.handleMove.bind(this));this.onUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",null,this.handleUp.bind(this));a.preventDefault();a.stopPropagation()}; +Blockly.Gesture.prototype.handleMove=function(a){this.updateFromEvent_(a);this.isDraggingWorkspace_?this.workspaceDragger_.drag(this.currentDragDeltaXY_):this.isDraggingBlock_?this.blockDragger_.dragBlock(this.mostRecentEvent_,this.currentDragDeltaXY_):this.isDraggingBubble_&&this.bubbleDragger_.dragBubble(this.mostRecentEvent_,this.currentDragDeltaXY_);a.preventDefault();a.stopPropagation()}; +Blockly.Gesture.prototype.handleUp=function(a){this.updateFromEvent_(a);Blockly.longStop_();this.isEnding_?console.log("Trying to end a gesture recursively."):(this.isEnding_=!0,this.isDraggingBubble_?this.bubbleDragger_.endBubbleDrag(a,this.currentDragDeltaXY_):this.isDraggingBlock_?this.blockDragger_.endBlockDrag(a,this.currentDragDeltaXY_):this.isDraggingWorkspace_?this.workspaceDragger_.endDrag(this.currentDragDeltaXY_):this.isBubbleClick_()?this.doBubbleClick_():this.isFieldClick_()?this.doFieldClick_(): +this.isBlockClick_()?this.doBlockClick_():this.isWorkspaceClick_()&&this.doWorkspaceClick_(a),a.preventDefault(),a.stopPropagation(),this.dispose())}; +Blockly.Gesture.prototype.cancel=function(){this.isEnding_||(Blockly.longStop_(),this.isDraggingBubble_?this.bubbleDragger_.endBubbleDrag(this.mostRecentEvent_,this.currentDragDeltaXY_):this.isDraggingBlock_?this.blockDragger_.endBlockDrag(this.mostRecentEvent_,this.currentDragDeltaXY_):this.isDraggingWorkspace_&&this.workspaceDragger_.endDrag(this.currentDragDeltaXY_),this.dispose())}; +Blockly.Gesture.prototype.handleRightClick=function(a){this.targetBlock_?(this.bringBlockToFront_(),Blockly.hideChaff(!!this.flyout_),this.targetBlock_.showContextMenu(a)):this.startBubble_?this.startBubble_.showContextMenu(a):this.startWorkspace_&&!this.flyout_&&(Blockly.hideChaff(),this.startWorkspace_.showContextMenu(a));a.preventDefault();a.stopPropagation();this.dispose()}; +Blockly.Gesture.prototype.handleWsStart=function(a,b){if(this.hasStarted_)throw Error("Tried to call gesture.handleWsStart, but the gesture had already been started.");this.setStartWorkspace_(b);this.mostRecentEvent_=a;this.doStart(a);this.startWorkspace_.keyboardAccessibilityMode&&Blockly.navigation.setState(Blockly.navigation.STATE_WS)};Blockly.Gesture.prototype.fireWorkspaceClick_=function(a){var b=new Blockly.Events.Ui(null,"workspaceClick",null,null);b.workspaceId=a.id;Blockly.Events.fire(b)}; +Blockly.Gesture.prototype.handleFlyoutStart=function(a,b){if(this.hasStarted_)throw Error("Tried to call gesture.handleFlyoutStart, but the gesture had already been started.");this.setStartFlyout_(b);this.handleWsStart(a,b.getWorkspace())};Blockly.Gesture.prototype.handleBlockStart=function(a,b){if(this.hasStarted_)throw Error("Tried to call gesture.handleBlockStart, but the gesture had already been started.");this.setStartBlock(b);this.mostRecentEvent_=a}; +Blockly.Gesture.prototype.handleBubbleStart=function(a,b){if(this.hasStarted_)throw Error("Tried to call gesture.handleBubbleStart, but the gesture had already been started.");this.setStartBubble(b);this.mostRecentEvent_=a};Blockly.Gesture.prototype.doBubbleClick_=function(){this.startBubble_.setFocus&&this.startBubble_.setFocus();this.startBubble_.select&&this.startBubble_.select()};Blockly.Gesture.prototype.doFieldClick_=function(){this.startField_.showEditor(this.mostRecentEvent_);this.bringBlockToFront_()}; +Blockly.Gesture.prototype.doBlockClick_=function(){this.flyout_&&this.flyout_.autoClose?this.targetBlock_.isEnabled()&&(Blockly.Events.getGroup()||Blockly.Events.setGroup(!0),this.flyout_.createBlock(this.targetBlock_).scheduleSnapAndBump()):Blockly.Events.fire(new Blockly.Events.Ui(this.startBlock_,"click",void 0,void 0));this.bringBlockToFront_();Blockly.Events.setGroup(!1)}; +Blockly.Gesture.prototype.doWorkspaceClick_=function(a){var b=this.creatorWorkspace_;a.shiftKey&&b.keyboardAccessibilityMode?(a=new Blockly.utils.Coordinate(a.clientX,a.clientY),a=Blockly.utils.screenToWsCoordinates(b,a),a=Blockly.ASTNode.createWorkspaceNode(b,a),b.getCursor().setCurNode(a)):Blockly.selected&&Blockly.selected.unselect();this.fireWorkspaceClick_(b)};Blockly.Gesture.prototype.bringBlockToFront_=function(){this.targetBlock_&&!this.flyout_&&this.targetBlock_.bringToFront()}; +Blockly.Gesture.prototype.setStartField=function(a){if(this.hasStarted_)throw Error("Tried to call gesture.setStartField, but the gesture had already been started.");this.startField_||(this.startField_=a)};Blockly.Gesture.prototype.setStartBubble=function(a){this.startBubble_||(this.startBubble_=a)};Blockly.Gesture.prototype.setStartBlock=function(a){this.startBlock_||this.startBubble_||(this.startBlock_=a,a.isInFlyout&&a!=a.getRootBlock()?this.setTargetBlock_(a.getRootBlock()):this.setTargetBlock_(a))}; +Blockly.Gesture.prototype.setTargetBlock_=function(a){a.isShadow()?this.setTargetBlock_(a.getParent()):this.targetBlock_=a};Blockly.Gesture.prototype.setStartWorkspace_=function(a){this.startWorkspace_||(this.startWorkspace_=a)};Blockly.Gesture.prototype.setStartFlyout_=function(a){this.flyout_||(this.flyout_=a)};Blockly.Gesture.prototype.isBubbleClick_=function(){return!!this.startBubble_&&!this.hasExceededDragRadius_}; +Blockly.Gesture.prototype.isBlockClick_=function(){return!!this.startBlock_&&!this.hasExceededDragRadius_&&!this.isFieldClick_()};Blockly.Gesture.prototype.isFieldClick_=function(){return(this.startField_?this.startField_.isClickable():!1)&&!this.hasExceededDragRadius_&&(!this.flyout_||!this.flyout_.autoClose)};Blockly.Gesture.prototype.isWorkspaceClick_=function(){return!this.startBlock_&&!this.startBubble_&&!this.startField_&&!this.hasExceededDragRadius_}; +Blockly.Gesture.prototype.isDragging=function(){return this.isDraggingWorkspace_||this.isDraggingBlock_||this.isDraggingBubble_};Blockly.Gesture.prototype.hasStarted=function(){return this.hasStarted_};Blockly.Gesture.prototype.getInsertionMarkers=function(){return this.blockDragger_?this.blockDragger_.getInsertionMarkers():[]};Blockly.Gesture.inProgress=function(){for(var a=Blockly.Workspace.getAll(),b=0,c;c=a[b];b++)if(c.currentGesture_)return!0;return!1};Blockly.Field=function(a,b,c){this.value_=this.DEFAULT_VALUE;this.tooltip_=this.validator_=null;this.size_=new Blockly.utils.Size(0,0);this.constants_=this.mouseDownWrapper_=this.textContent_=this.textElement_=this.borderRect_=this.fieldGroup_=this.markerSvg_=this.cursorSvg_=null;c&&this.configure_(c);this.setValue(a);b&&this.setValidator(b)};Blockly.Field.prototype.DEFAULT_VALUE=null;Blockly.Field.prototype.name=void 0;Blockly.Field.prototype.disposed=!1; +Blockly.Field.prototype.maxDisplayLength=50;Blockly.Field.prototype.sourceBlock_=null;Blockly.Field.prototype.isDirty_=!0;Blockly.Field.prototype.visible_=!0;Blockly.Field.prototype.clickTarget_=null;Blockly.Field.NBSP="\u00a0";Blockly.Field.prototype.EDITABLE=!0;Blockly.Field.prototype.SERIALIZABLE=!1;Blockly.Field.prototype.configure_=function(a){var b=a.tooltip;"string"==typeof b&&(b=Blockly.utils.replaceMessageReferences(a.tooltip));b&&this.setTooltip(b)}; +Blockly.Field.prototype.setSourceBlock=function(a){if(this.sourceBlock_)throw Error("Field already bound to a block.");this.sourceBlock_=a};Blockly.Field.prototype.getConstants=function(){!this.constants_&&this.sourceBlock_&&this.sourceBlock_.workspace&&this.sourceBlock_.workspace.rendered&&(this.constants_=this.sourceBlock_.workspace.getRenderer().getConstants());return this.constants_};Blockly.Field.prototype.getSourceBlock=function(){return this.sourceBlock_}; +Blockly.Field.prototype.init=function(){this.fieldGroup_||(this.fieldGroup_=Blockly.utils.dom.createSvgElement("g",{},null),this.isVisible()||(this.fieldGroup_.style.display="none"),this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_),this.initView(),this.updateEditable(),this.setTooltip(this.tooltip_),this.bindEvents_(),this.initModel())};Blockly.Field.prototype.initView=function(){this.createBorderRect_();this.createTextElement_()};Blockly.Field.prototype.initModel=function(){}; +Blockly.Field.prototype.createBorderRect_=function(){this.borderRect_=Blockly.utils.dom.createSvgElement("rect",{rx:this.getConstants().FIELD_BORDER_RECT_RADIUS,ry:this.getConstants().FIELD_BORDER_RECT_RADIUS,x:0,y:0,height:this.size_.height,width:this.size_.width,"class":"blocklyFieldRect"},this.fieldGroup_)}; +Blockly.Field.prototype.createTextElement_=function(){this.textElement_=Blockly.utils.dom.createSvgElement("text",{"class":"blocklyText"},this.fieldGroup_);this.getConstants().FIELD_TEXT_BASELINE_CENTER&&this.textElement_.setAttribute("dominant-baseline","central");this.textContent_=document.createTextNode("");this.textElement_.appendChild(this.textContent_)}; +Blockly.Field.prototype.bindEvents_=function(){Blockly.Tooltip.bindMouseEvents(this.getClickTarget_());this.mouseDownWrapper_=Blockly.bindEventWithChecks_(this.getClickTarget_(),"mousedown",this,this.onMouseDown_)};Blockly.Field.prototype.fromXml=function(a){this.setValue(a.textContent)};Blockly.Field.prototype.toXml=function(a){a.textContent=this.getValue();return a}; +Blockly.Field.prototype.dispose=function(){Blockly.DropDownDiv.hideIfOwner(this);Blockly.WidgetDiv.hideIfOwner(this);Blockly.Tooltip.unbindMouseEvents(this.getClickTarget_());this.mouseDownWrapper_&&Blockly.unbindEvent_(this.mouseDownWrapper_);Blockly.utils.dom.removeNode(this.fieldGroup_);this.disposed=!0}; +Blockly.Field.prototype.updateEditable=function(){var a=this.fieldGroup_;this.EDITABLE&&a&&(this.sourceBlock_.isEditable()?(Blockly.utils.dom.addClass(a,"blocklyEditableText"),Blockly.utils.dom.removeClass(a,"blocklyNonEditableText"),a.style.cursor=this.CURSOR):(Blockly.utils.dom.addClass(a,"blocklyNonEditableText"),Blockly.utils.dom.removeClass(a,"blocklyEditableText"),a.style.cursor=""))}; +Blockly.Field.prototype.isClickable=function(){return!!this.sourceBlock_&&this.sourceBlock_.isEditable()&&!!this.showEditor_&&"function"===typeof this.showEditor_};Blockly.Field.prototype.isCurrentlyEditable=function(){return this.EDITABLE&&!!this.sourceBlock_&&this.sourceBlock_.isEditable()}; +Blockly.Field.prototype.isSerializable=function(){var a=!1;this.name&&(this.SERIALIZABLE?a=!0:this.EDITABLE&&(console.warn("Detected an editable field that was not serializable. Please define SERIALIZABLE property as true on all editable custom fields. Proceeding with serialization."),a=!0));return a};Blockly.Field.prototype.isVisible=function(){return this.visible_}; +Blockly.Field.prototype.setVisible=function(a){if(this.visible_!=a){this.visible_=a;var b=this.getSvgRoot();b&&(b.style.display=a?"block":"none")}};Blockly.Field.prototype.setValidator=function(a){this.validator_=a};Blockly.Field.prototype.getValidator=function(){return this.validator_};Blockly.Field.prototype.classValidator=function(a){return a}; +Blockly.Field.prototype.callValidator=function(a){var b=this.classValidator(a);if(null===b)return null;void 0!==b&&(a=b);if(b=this.getValidator()){b=b.call(this,a);if(null===b)return null;void 0!==b&&(a=b)}return a};Blockly.Field.prototype.getSvgRoot=function(){return this.fieldGroup_};Blockly.Field.prototype.applyColour=function(){};Blockly.Field.prototype.render_=function(){this.textContent_&&(this.textContent_.nodeValue=this.getDisplayText_());this.updateSize_()}; +Blockly.Field.prototype.showEditor=function(a){this.isClickable()&&this.showEditor_(a)};Blockly.Field.prototype.updateWidth=function(){console.warn("Deprecated call to updateWidth, call Blockly.Field.updateSize_ to force an update to the size of the field, or Blockly.utils.dom.getTextWidth() to check the size of the field.");this.updateSize_()}; +Blockly.Field.prototype.updateSize_=function(a){var b=this.getConstants();a=void 0!=a?a:this.borderRect_?this.getConstants().FIELD_BORDER_RECT_X_PADDING:0;var c=2*a,d=b.FIELD_TEXT_HEIGHT,e=0;this.textElement_&&(e=Blockly.utils.dom.getFastTextWidth(this.textElement_,b.FIELD_TEXT_FONTSIZE,b.FIELD_TEXT_FONTWEIGHT,b.FIELD_TEXT_FONTFAMILY),c+=e);this.borderRect_&&(d=Math.max(d,b.FIELD_BORDER_RECT_HEIGHT));this.size_.height=d;this.size_.width=c;this.positionTextElement_(a,e);this.positionBorderRect_()}; +Blockly.Field.prototype.positionTextElement_=function(a,b){if(this.textElement_){var c=this.getConstants(),d=this.size_.height/2;this.textElement_.setAttribute("x",this.sourceBlock_.RTL?this.size_.width-b-a:a);this.textElement_.setAttribute("y",c.FIELD_TEXT_BASELINE_CENTER?d:d-c.FIELD_TEXT_HEIGHT/2+c.FIELD_TEXT_BASELINE)}}; +Blockly.Field.prototype.positionBorderRect_=function(){this.borderRect_&&(this.borderRect_.setAttribute("width",this.size_.width),this.borderRect_.setAttribute("height",this.size_.height),this.borderRect_.setAttribute("rx",this.getConstants().FIELD_BORDER_RECT_RADIUS),this.borderRect_.setAttribute("ry",this.getConstants().FIELD_BORDER_RECT_RADIUS))}; +Blockly.Field.prototype.getSize=function(){if(!this.isVisible())return new Blockly.utils.Size(0,0);this.isDirty_?(this.render_(),this.isDirty_=!1):this.visible_&&0==this.size_.width&&(console.warn("Deprecated use of setting size_.width to 0 to rerender a field. Set field.isDirty_ to true instead."),this.render_());return this.size_}; +Blockly.Field.prototype.getScaledBBox=function(){if(this.borderRect_)a=this.borderRect_.getBoundingClientRect(),c=Blockly.utils.style.getPageOffset(this.borderRect_),d=a.width,a=a.height;else{var a=this.sourceBlock_.getHeightWidth(),b=this.sourceBlock_.workspace.scale,c=this.getAbsoluteXY_(),d=a.width*b;a=a.height*b;Blockly.utils.userAgent.GECKO?(c.x+=1.5*b,c.y+=1.5*b):Blockly.utils.userAgent.EDGE||Blockly.utils.userAgent.IE||(c.x-=.5*b,c.y-=.5*b);d+=1*b;a+=1*b}return new Blockly.utils.Rect(c.y,c.y+ +a,c.x,c.x+d)};Blockly.Field.prototype.getDisplayText_=function(){var a=this.getText();if(!a)return Blockly.Field.NBSP;a.length>this.maxDisplayLength&&(a=a.substring(0,this.maxDisplayLength-2)+"\u2026");a=a.replace(/\s/g,Blockly.Field.NBSP);this.sourceBlock_&&this.sourceBlock_.RTL&&(a+="\u200f");return a};Blockly.Field.prototype.getText=function(){if(this.getText_){var a=this.getText_.call(this);if(null!==a)return String(a)}return String(this.getValue())}; +Blockly.Field.prototype.setText=function(a){throw Error("setText method is deprecated");};Blockly.Field.prototype.markDirty=function(){this.isDirty_=!0;this.constants_=null};Blockly.Field.prototype.forceRerender=function(){this.isDirty_=!0;this.sourceBlock_&&this.sourceBlock_.rendered&&(this.sourceBlock_.render(),this.sourceBlock_.bumpNeighbours(),this.updateMarkers_())}; +Blockly.Field.prototype.setValue=function(a){if(null!==a){var b=this.doClassValidation_(a);a=this.processValidation_(a,b);if(!(a instanceof Error)){if(b=this.getValidator())if(b=b.call(this,a),a=this.processValidation_(a,b),a instanceof Error)return;b=this.sourceBlock_;if(!b||!b.disposed){var c=this.getValue();c!==a&&(b&&Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.BlockChange(b,"field",this.name||null,c,a)),this.doValueUpdate_(a),this.isDirty_&&this.forceRerender())}}}}; +Blockly.Field.prototype.processValidation_=function(a,b){if(null===b)return this.doValueInvalid_(a),this.isDirty_&&this.forceRerender(),Error();void 0!==b&&(a=b);return a};Blockly.Field.prototype.getValue=function(){return this.value_};Blockly.Field.prototype.doClassValidation_=function(a){return null===a||void 0===a?null:a=this.classValidator(a)};Blockly.Field.prototype.doValueUpdate_=function(a){this.value_=a;this.isDirty_=!0};Blockly.Field.prototype.doValueInvalid_=function(a){}; +Blockly.Field.prototype.onMouseDown_=function(a){this.sourceBlock_&&this.sourceBlock_.workspace&&(a=this.sourceBlock_.workspace.getGesture(a))&&a.setStartField(this)};Blockly.Field.prototype.setTooltip=function(a){var b=this.getClickTarget_();b?b.tooltip=a||""===a?a:this.sourceBlock_:this.tooltip_=a};Blockly.Field.prototype.getClickTarget_=function(){return this.clickTarget_||this.getSvgRoot()};Blockly.Field.prototype.getAbsoluteXY_=function(){return Blockly.utils.style.getPageOffset(this.getClickTarget_())}; +Blockly.Field.prototype.referencesVariables=function(){return!1};Blockly.Field.prototype.getParentInput=function(){for(var a=null,b=this.sourceBlock_,c=b.inputList,d=0;da||a>this.fieldRow.length)throw Error("index "+a+" out of bounds.");if(!(b||""==b&&c))return a;"string"==typeof b&&(b=new Blockly.FieldLabel(b));b.setSourceBlock(this.sourceBlock_);this.sourceBlock_.rendered&&b.init();b.name=c;b.setVisible(this.isVisible());c=b;c.prefixField&&(a=this.insertFieldAt(a,c.prefixField));this.fieldRow.splice(a,0,b);++a;c.suffixField&&(a=this.insertFieldAt(a,c.suffixField));this.sourceBlock_.rendered&&(this.sourceBlock_= +this.sourceBlock_,this.sourceBlock_.render(),this.sourceBlock_.bumpNeighbours());return a};Blockly.Input.prototype.removeField=function(a,b){for(var c=0,d;d=this.fieldRow[c];c++)if(d.name===a)return d.dispose(),this.fieldRow.splice(c,1),this.sourceBlock_.rendered&&(this.sourceBlock_=this.sourceBlock_,this.sourceBlock_.render(),this.sourceBlock_.bumpNeighbours()),!0;if(b)return!1;throw Error('Field "'+a+'" not found.');};Blockly.Input.prototype.isVisible=function(){return this.visible_}; +Blockly.Input.prototype.setVisible=function(a){var b=[];if(this.visible_==a)return b;this.visible_=a;for(var c=0,d;d=this.fieldRow[c];c++)d.setVisible(a);this.connection&&(this.connection=this.connection,a?b=this.connection.startTrackingAll():this.connection.stopTrackingAll(),c=this.connection.targetBlock())&&(c.getSvgRoot().style.display=a?"block":"none");return b};Blockly.Input.prototype.markDirty=function(){for(var a=0,b;b=this.fieldRow[a];a++)b.markDirty()}; +Blockly.Input.prototype.setCheck=function(a){if(!this.connection)throw Error("This input does not have a connection.");this.connection.setCheck(a);return this};Blockly.Input.prototype.setAlign=function(a){this.align=a;this.sourceBlock_.rendered&&(this.sourceBlock_=this.sourceBlock_,this.sourceBlock_.render());return this};Blockly.Input.prototype.init=function(){if(this.sourceBlock_.workspace.rendered)for(var a=0;aa&&(e=e.substring(0,a-3)+"...");return e};Blockly.Block.prototype.appendValueInput=function(a){return this.appendInput_(Blockly.INPUT_VALUE,a)};Blockly.Block.prototype.appendStatementInput=function(a){return this.appendInput_(Blockly.NEXT_STATEMENT,a)};Blockly.Block.prototype.appendDummyInput=function(a){return this.appendInput_(Blockly.DUMMY_INPUT,a||"")}; +Blockly.Block.prototype.jsonInit=function(a){var b=a.type?'Block "'+a.type+'": ':"";if(a.output&&a.previousStatement)throw Error(b+"Must not have both an output and a previousStatement.");a.style&&a.style.hat&&(this.hat=a.style.hat,a.style=null);if(a.style&&a.colour)throw Error(b+"Must not have both a colour and a style.");a.style?this.jsonInitStyle_(a,b):this.jsonInitColour_(a,b);for(var c=0;void 0!==a["message"+c];)this.interpolate_(a["message"+c],a["args"+c]||[],a["lastDummyAlign"+c],b),c++;void 0!== +a.inputsInline&&this.setInputsInline(a.inputsInline);void 0!==a.output&&this.setOutput(!0,a.output);void 0!==a.outputShape&&this.setOutputShape(a.outputShape);void 0!==a.previousStatement&&this.setPreviousStatement(!0,a.previousStatement);void 0!==a.nextStatement&&this.setNextStatement(!0,a.nextStatement);void 0!==a.tooltip&&(c=a.tooltip,c=Blockly.utils.replaceMessageReferences(c),this.setTooltip(c));void 0!==a.enableContextMenu&&(c=a.enableContextMenu,this.contextMenu=!!c);void 0!==a.helpUrl&&(c= +a.helpUrl,c=Blockly.utils.replaceMessageReferences(c),this.setHelpUrl(c));"string"==typeof a.extensions&&(console.warn(b+"JSON attribute 'extensions' should be an array of strings. Found raw string in JSON for '"+a.type+"' block."),a.extensions=[a.extensions]);void 0!==a.mutator&&Blockly.Extensions.apply(a.mutator,this,!0);if(Array.isArray(a.extensions))for(a=a.extensions,b=0;b=k||k>b.length)throw Error('Block "'+this.type+'": Message index %'+k+" out of range.");if(f[k])throw Error('Block "'+this.type+'": Message index %'+k+" duplicated.");f[k]=!0;g++;a.push(b[k-1])}else(k=k.trim())&&a.push(k)}if(g!=b.length)throw Error('Block "'+this.type+'": Message does not reference all '+b.length+" arg(s)."); +a.length&&("string"==typeof a[a.length-1]||Blockly.utils.string.startsWith(a[a.length-1].type,"field_"))&&(h={type:"input_dummy"},c&&(h.align=c),a.push(h));c={LEFT:Blockly.ALIGN_LEFT,RIGHT:Blockly.ALIGN_RIGHT,CENTRE:Blockly.ALIGN_CENTRE,CENTER:Blockly.ALIGN_CENTRE};b=[];for(h=0;h=this.inputList.length)throw RangeError("Input index "+a+" out of bounds.");if(b>this.inputList.length)throw RangeError("Reference input "+b+" out of bounds.");var c=this.inputList[a];this.inputList.splice(a,1);aa?this.menuItems_.length:a,-1)};Blockly.Menu.prototype.highlightFirst_=function(){this.highlightHelper_(-1,1)};Blockly.Menu.prototype.highlightLast_=function(){this.highlightHelper_(this.menuItems_.length,-1)};Blockly.Menu.prototype.highlightHelper_=function(a,b){a+=b;for(var c;c=this.menuItems_[a];){if(c.isEnabled()){this.setHighlighted(c);break}a+=b}}; +Blockly.Menu.prototype.handleMouseOver_=function(a){(a=this.getMenuItem_(a.target))&&(a.isEnabled()?this.highlightedItem_!=a&&this.setHighlighted(a):this.setHighlighted(null))};Blockly.Menu.prototype.handleClick_=function(a){var b=this.openingCoords;this.openingCoords=null;if(b&&"number"==typeof a.clientX){var c=new Blockly.utils.Coordinate(a.clientX,a.clientY);if(1>Blockly.utils.Coordinate.distance(b,c))return}(a=this.getMenuItem_(a.target))&&a.performAction()}; +Blockly.Menu.prototype.handleMouseEnter_=function(a){this.focus()};Blockly.Menu.prototype.handleMouseLeave_=function(a){this.getElement()&&(this.blur_(),this.setHighlighted(null))}; +Blockly.Menu.prototype.handleKeyEvent_=function(a){if(this.menuItems_.length&&!(a.shiftKey||a.ctrlKey||a.metaKey||a.altKey)){var b=this.highlightedItem_;switch(a.keyCode){case Blockly.utils.KeyCodes.ENTER:case Blockly.utils.KeyCodes.SPACE:b&&b.performAction();break;case Blockly.utils.KeyCodes.UP:this.highlightPrevious();break;case Blockly.utils.KeyCodes.DOWN:this.highlightNext();break;case Blockly.utils.KeyCodes.PAGE_UP:case Blockly.utils.KeyCodes.HOME:this.highlightFirst_();break;case Blockly.utils.KeyCodes.PAGE_DOWN:case Blockly.utils.KeyCodes.END:this.highlightLast_(); +break;default:return}a.preventDefault();a.stopPropagation()}};Blockly.Menu.prototype.getSize=function(){var a=this.getElement(),b=Blockly.utils.style.getSize(a);b.height=a.scrollHeight;return b};Blockly.MenuItem=function(a,b){this.content_=a;this.value_=b;this.enabled_=!0;this.element_=null;this.rightToLeft_=!1;this.roleName_=null;this.highlight_=this.checked_=this.checkable_=!1;this.actionHandler_=null}; +Blockly.MenuItem.prototype.createDom=function(){var a=document.createElement("div");a.id=Blockly.utils.IdGenerator.getNextUniqueId();this.element_=a;a.className="blocklyMenuItem goog-menuitem "+(this.enabled_?"":"blocklyMenuItemDisabled goog-menuitem-disabled ")+(this.checked_?"blocklyMenuItemSelected goog-option-selected ":"")+(this.highlight_?"blocklyMenuItemHighlight goog-menuitem-highlight ":"")+(this.rightToLeft_?"blocklyMenuItemRtl goog-menuitem-rtl ":"");var b=document.createElement("div"); +b.className="blocklyMenuItemContent goog-menuitem-content";if(this.checkable_){var c=document.createElement("div");c.className="blocklyMenuItemCheckbox goog-menuitem-checkbox";b.appendChild(c)}c=this.content_;"string"==typeof this.content_&&(c=document.createTextNode(this.content_));b.appendChild(c);a.appendChild(b);this.roleName_&&Blockly.utils.aria.setRole(a,this.roleName_);Blockly.utils.aria.setState(a,Blockly.utils.aria.State.SELECTED,this.checkable_&&this.checked_||!1);Blockly.utils.aria.setState(a, +Blockly.utils.aria.State.DISABLED,!this.enabled_);return a};Blockly.MenuItem.prototype.dispose=function(){this.element_=null};Blockly.MenuItem.prototype.getElement=function(){return this.element_};Blockly.MenuItem.prototype.getId=function(){return this.element_.id};Blockly.MenuItem.prototype.getValue=function(){return this.value_};Blockly.MenuItem.prototype.setRightToLeft=function(a){this.rightToLeft_=a};Blockly.MenuItem.prototype.setRole=function(a){this.roleName_=a}; +Blockly.MenuItem.prototype.setCheckable=function(a){this.checkable_=a};Blockly.MenuItem.prototype.setChecked=function(a){this.checked_=a};Blockly.MenuItem.prototype.setHighlighted=function(a){this.highlight_=a;var b=this.getElement();b&&this.isEnabled()&&(a?(Blockly.utils.dom.addClass(b,"blocklyMenuItemHighlight"),Blockly.utils.dom.addClass(b,"goog-menuitem-highlight")):(Blockly.utils.dom.removeClass(b,"blocklyMenuItemHighlight"),Blockly.utils.dom.removeClass(b,"goog-menuitem-highlight")))}; +Blockly.MenuItem.prototype.isEnabled=function(){return this.enabled_};Blockly.MenuItem.prototype.setEnabled=function(a){this.enabled_=a};Blockly.MenuItem.prototype.performAction=function(){this.isEnabled()&&this.actionHandler_&&this.actionHandler_(this)};Blockly.MenuItem.prototype.onAction=function(a,b){this.actionHandler_=a.bind(b)};Blockly.ContextMenu={};Blockly.ContextMenu.currentBlock=null;Blockly.ContextMenu.menu_=null;Blockly.ContextMenu.show=function(a,b,c){Blockly.WidgetDiv.show(Blockly.ContextMenu,c,Blockly.ContextMenu.dispose);if(b.length){var d=Blockly.ContextMenu.populate_(b,c);Blockly.ContextMenu.menu_=d;Blockly.ContextMenu.position_(d,a,c);setTimeout(function(){d.focus()},1);Blockly.ContextMenu.currentBlock=null}else Blockly.ContextMenu.hide()}; +Blockly.ContextMenu.populate_=function(a,b){var c=new Blockly.Menu;c.setRole(Blockly.utils.aria.Role.MENU);for(var d=0,e;e=a[d];d++){var f=new Blockly.MenuItem(e.text);f.setRightToLeft(b);f.setRole(Blockly.utils.aria.Role.MENUITEM);c.addChild(f);f.setEnabled(e.enabled);if(e.enabled)f.onAction(function(a){Blockly.ContextMenu.hide();this.callback()},e)}return c}; +Blockly.ContextMenu.position_=function(a,b,c){var d=Blockly.utils.getViewportBBox();b=new Blockly.utils.Rect(b.clientY+d.top,b.clientY+d.top,b.clientX+d.left,b.clientX+d.left);Blockly.ContextMenu.createWidget_(a);var e=a.getSize();c&&(b.left+=e.width,b.right+=e.width,d.left+=e.width,d.right+=e.width);Blockly.WidgetDiv.positionWithAnchor(d,b,e,c);a.focus()}; +Blockly.ContextMenu.createWidget_=function(a){a.render(Blockly.WidgetDiv.DIV);var b=a.getElement();Blockly.utils.dom.addClass(b,"blocklyContextMenu");Blockly.bindEventWithChecks_(b,"contextmenu",null,Blockly.utils.noEvent);a.focus()};Blockly.ContextMenu.hide=function(){Blockly.WidgetDiv.hideIfOwner(Blockly.ContextMenu);Blockly.ContextMenu.currentBlock=null};Blockly.ContextMenu.dispose=function(){Blockly.ContextMenu.menu_&&(Blockly.ContextMenu.menu_.dispose(),Blockly.ContextMenu.menu_=null)}; +Blockly.ContextMenu.callbackFactory=function(a,b){return function(){Blockly.Events.disable();try{var c=Blockly.Xml.domToBlock(b,a.workspace),d=a.getRelativeToSurfaceXY();d.x=a.RTL?d.x-Blockly.SNAP_RADIUS:d.x+Blockly.SNAP_RADIUS;d.y+=2*Blockly.SNAP_RADIUS;c.moveBy(d.x,d.y)}finally{Blockly.Events.enable()}Blockly.Events.isEnabled()&&!c.isShadow()&&Blockly.Events.fire(new Blockly.Events.BlockCreate(c));c.select()}}; +Blockly.ContextMenu.blockDeleteOption=function(a){var b=a.getDescendants(!1).length,c=a.getNextBlock();c&&(b-=c.getDescendants(!1).length);return{text:1==b?Blockly.Msg.DELETE_BLOCK:Blockly.Msg.DELETE_X_BLOCKS.replace("%1",String(b)),enabled:!0,callback:function(){Blockly.Events.setGroup(!0);a.dispose(!0,!0);Blockly.Events.setGroup(!1)}}};Blockly.ContextMenu.blockHelpOption=function(a){return{enabled:!("function"==typeof a.helpUrl?!a.helpUrl():!a.helpUrl),text:Blockly.Msg.HELP,callback:function(){a.showHelp()}}}; +Blockly.ContextMenu.blockDuplicateOption=function(a){var b=a.isDuplicatable();return{text:Blockly.Msg.DUPLICATE_BLOCK,enabled:b,callback:function(){Blockly.duplicate(a)}}};Blockly.ContextMenu.blockCommentOption=function(a){var b={enabled:!Blockly.utils.userAgent.IE};a.getCommentIcon()?(b.text=Blockly.Msg.REMOVE_COMMENT,b.callback=function(){a.setCommentText(null)}):(b.text=Blockly.Msg.ADD_COMMENT,b.callback=function(){a.setCommentText("")});return b}; +Blockly.ContextMenu.commentDeleteOption=function(a){return{text:Blockly.Msg.REMOVE_COMMENT,enabled:!0,callback:function(){Blockly.Events.setGroup(!0);a.dispose(!0,!0);Blockly.Events.setGroup(!1)}}};Blockly.ContextMenu.commentDuplicateOption=function(a){return{text:Blockly.Msg.DUPLICATE_COMMENT,enabled:!0,callback:function(){Blockly.duplicate(a)}}}; +Blockly.ContextMenu.workspaceCommentOption=function(a,b){if(!Blockly.WorkspaceCommentSvg)throw Error("Missing require for Blockly.WorkspaceCommentSvg");var c={enabled:!Blockly.utils.userAgent.IE};c.text=Blockly.Msg.ADD_COMMENT;c.callback=function(){var c=new Blockly.WorkspaceCommentSvg(a,Blockly.Msg.WORKSPACE_COMMENT_DEFAULT_TEXT,Blockly.WorkspaceCommentSvg.DEFAULT_SIZE,Blockly.WorkspaceCommentSvg.DEFAULT_SIZE),e=a.getInjectionDiv().getBoundingClientRect();e=new Blockly.utils.Coordinate(b.clientX- +e.left,b.clientY-e.top);var f=a.getOriginOffsetInPixels();e=Blockly.utils.Coordinate.difference(e,f);e.scale(1/a.scale);c.moveBy(e.x,e.y);a.rendered&&(c.initSvg(),c.render(),c.select())};return c};Blockly.RenderedConnection=function(a,b){Blockly.RenderedConnection.superClass_.constructor.call(this,a,b);this.db_=a.workspace.connectionDBList[b];this.dbOpposite_=a.workspace.connectionDBList[Blockly.OPPOSITE_TYPE[b]];this.offsetInBlock_=new Blockly.utils.Coordinate(0,0);this.trackedState_=Blockly.RenderedConnection.TrackedState.WILL_TRACK;this.targetConnection=null};Blockly.utils.object.inherits(Blockly.RenderedConnection,Blockly.Connection); +Blockly.RenderedConnection.TrackedState={WILL_TRACK:-1,UNTRACKED:0,TRACKED:1};Blockly.RenderedConnection.prototype.dispose=function(){Blockly.RenderedConnection.superClass_.dispose.call(this);this.trackedState_==Blockly.RenderedConnection.TrackedState.TRACKED&&this.db_.removeConnection(this,this.y)};Blockly.RenderedConnection.prototype.getSourceBlock=function(){return Blockly.RenderedConnection.superClass_.getSourceBlock.call(this)};Blockly.RenderedConnection.prototype.targetBlock=function(){return Blockly.RenderedConnection.superClass_.targetBlock.call(this)}; +Blockly.RenderedConnection.prototype.distanceFrom=function(a){var b=this.x-a.x;a=this.y-a.y;return Math.sqrt(b*b+a*a)}; +Blockly.RenderedConnection.prototype.bumpAwayFrom=function(a){if(!this.sourceBlock_.workspace.isDragging()){var b=this.sourceBlock_.getRootBlock();if(!b.isInFlyout){var c=!1;if(!b.isMovable()){b=a.getSourceBlock().getRootBlock();if(!b.isMovable())return;a=this;c=!0}var d=Blockly.selected==b;d||b.addSelect();var e=a.x+Blockly.SNAP_RADIUS+Math.floor(Math.random()*Blockly.BUMP_RANDOMNESS)-this.x,f=a.y+Blockly.SNAP_RADIUS+Math.floor(Math.random()*Blockly.BUMP_RANDOMNESS)-this.y;c&&(f=-f);b.RTL&&(e=a.x- +Blockly.SNAP_RADIUS-Math.floor(Math.random()*Blockly.BUMP_RANDOMNESS)-this.x);b.moveBy(e,f);d||b.removeSelect()}}}; +Blockly.RenderedConnection.prototype.moveTo=function(a,b){this.trackedState_==Blockly.RenderedConnection.TrackedState.WILL_TRACK?(this.db_.addConnection(this,b),this.trackedState_=Blockly.RenderedConnection.TrackedState.TRACKED):this.trackedState_==Blockly.RenderedConnection.TrackedState.TRACKED&&(this.db_.removeConnection(this,this.y),this.db_.addConnection(this,b));this.x=a;this.y=b};Blockly.RenderedConnection.prototype.moveBy=function(a,b){this.moveTo(this.x+a,this.y+b)}; +Blockly.RenderedConnection.prototype.moveToOffset=function(a){this.moveTo(a.x+this.offsetInBlock_.x,a.y+this.offsetInBlock_.y)};Blockly.RenderedConnection.prototype.setOffsetInBlock=function(a,b){this.offsetInBlock_.x=a;this.offsetInBlock_.y=b};Blockly.RenderedConnection.prototype.getOffsetInBlock=function(){return this.offsetInBlock_}; +Blockly.RenderedConnection.prototype.tighten=function(){var a=this.targetConnection.x-this.x,b=this.targetConnection.y-this.y;if(0!=a||0!=b){var c=this.targetBlock(),d=c.getSvgRoot();if(!d)throw Error("block is not rendered.");d=Blockly.utils.getRelativeXY(d);c.getSvgRoot().setAttribute("transform","translate("+(d.x-a)+","+(d.y-b)+")");c.moveConnections(-a,-b)}};Blockly.RenderedConnection.prototype.closest=function(a,b){return this.dbOpposite_.searchForClosest(this,a,b)}; +Blockly.RenderedConnection.prototype.highlight=function(){var a=this.sourceBlock_.workspace.getRenderer().getConstants();var b=a.shapeFor(this);this.type==Blockly.INPUT_VALUE||this.type==Blockly.OUTPUT_VALUE?(a=a.TAB_OFFSET_FROM_TOP,b=Blockly.utils.svgPaths.moveBy(0,-a)+Blockly.utils.svgPaths.lineOnAxis("v",a)+b.pathDown+Blockly.utils.svgPaths.lineOnAxis("v",a)):(a=a.NOTCH_OFFSET_LEFT-a.CORNER_RADIUS,b=Blockly.utils.svgPaths.moveBy(-a,0)+Blockly.utils.svgPaths.lineOnAxis("h",a)+b.pathLeft+Blockly.utils.svgPaths.lineOnAxis("h", +a));a=this.sourceBlock_.getRelativeToSurfaceXY();Blockly.Connection.highlightedPath_=Blockly.utils.dom.createSvgElement("path",{"class":"blocklyHighlightedConnectionPath",d:b,transform:"translate("+(this.x-a.x)+","+(this.y-a.y)+")"+(this.sourceBlock_.RTL?" scale(-1 1)":"")},this.sourceBlock_.getSvgRoot())};Blockly.RenderedConnection.prototype.unhighlight=function(){Blockly.utils.dom.removeNode(Blockly.Connection.highlightedPath_);delete Blockly.Connection.highlightedPath_}; +Blockly.RenderedConnection.prototype.setTracking=function(a){a&&this.trackedState_==Blockly.RenderedConnection.TrackedState.TRACKED||!a&&this.trackedState_==Blockly.RenderedConnection.TrackedState.UNTRACKED||this.sourceBlock_.isInFlyout||(a?(this.db_.addConnection(this,this.y),this.trackedState_=Blockly.RenderedConnection.TrackedState.TRACKED):(this.trackedState_==Blockly.RenderedConnection.TrackedState.TRACKED&&this.db_.removeConnection(this,this.y),this.trackedState_=Blockly.RenderedConnection.TrackedState.UNTRACKED))}; +Blockly.RenderedConnection.prototype.stopTrackingAll=function(){this.setTracking(!1);if(this.targetConnection)for(var a=this.targetBlock().getDescendants(!1),b=0;bb?!1:Blockly.RenderedConnection.superClass_.isConnectionAllowed.call(this,a)};Blockly.RenderedConnection.prototype.onFailedConnect=function(a){this.bumpAwayFrom(a)}; +Blockly.RenderedConnection.prototype.disconnectInternal_=function(a,b){Blockly.RenderedConnection.superClass_.disconnectInternal_.call(this,a,b);a.rendered&&a.render();b.rendered&&(b.updateDisabled(),b.render(),b.getSvgRoot().style.display="block")}; +Blockly.RenderedConnection.prototype.respawnShadow_=function(){var a=this.getSourceBlock(),b=this.getShadowDom();if(a.workspace&&b&&Blockly.Events.recordUndo){Blockly.RenderedConnection.superClass_.respawnShadow_.call(this);b=this.targetBlock();if(!b)throw Error("Couldn't respawn the shadow block that should exist here.");b.initSvg();b.render(!1);a.rendered&&a.render()}};Blockly.RenderedConnection.prototype.neighbours=function(a){return this.dbOpposite_.getNeighbours(this,a)}; +Blockly.RenderedConnection.prototype.connect_=function(a){Blockly.RenderedConnection.superClass_.connect_.call(this,a);var b=this.getSourceBlock();a=a.getSourceBlock();var c=b.rendered,d=a.rendered;c&&b.updateDisabled();d&&a.updateDisabled();c&&d&&(this.type==Blockly.NEXT_STATEMENT||this.type==Blockly.PREVIOUS_STATEMENT?a.render():b.render());if(b=b.getInputWithBlock(a))b=b.isVisible(),a.getSvgRoot().style.display=b?"block":"none"}; +Blockly.RenderedConnection.prototype.onCheckChanged_=function(){!this.isConnected()||this.targetConnection&&this.checkType(this.targetConnection)||((this.isSuperior()?this.targetBlock():this.sourceBlock_).unplug(),this.sourceBlock_.bumpNeighbours())};Blockly.Marker=function(){this.drawer_=this.curNode_=this.colour=null;this.type="marker"};Blockly.Marker.prototype.setDrawer=function(a){this.drawer_=a};Blockly.Marker.prototype.getDrawer=function(){return this.drawer_};Blockly.Marker.prototype.getCurNode=function(){return this.curNode_};Blockly.Marker.prototype.setCurNode=function(a){var b=this.curNode_;this.curNode_=a;this.drawer_&&this.drawer_.draw(b,this.curNode_)}; +Blockly.Marker.prototype.draw=function(){this.drawer_&&this.drawer_.draw(this.curNode_,this.curNode_)};Blockly.Marker.prototype.hide=function(){this.drawer_&&this.drawer_.hide()};Blockly.Marker.prototype.dispose=function(){this.getDrawer()&&this.getDrawer().dispose()};Blockly.Cursor=function(){Blockly.Cursor.superClass_.constructor.call(this);this.type="cursor"};Blockly.utils.object.inherits(Blockly.Cursor,Blockly.Marker);Blockly.Cursor.prototype.next=function(){var a=this.getCurNode();if(!a)return null;for(a=a.next();a&&a.next()&&(a.getType()==Blockly.ASTNode.types.NEXT||a.getType()==Blockly.ASTNode.types.BLOCK);)a=a.next();a&&this.setCurNode(a);return a}; +Blockly.Cursor.prototype.in=function(){var a=this.getCurNode();if(!a)return null;if(a.getType()==Blockly.ASTNode.types.PREVIOUS||a.getType()==Blockly.ASTNode.types.OUTPUT)a=a.next();(a=a.in())&&this.setCurNode(a);return a};Blockly.Cursor.prototype.prev=function(){var a=this.getCurNode();if(!a)return null;for(a=a.prev();a&&a.prev()&&(a.getType()==Blockly.ASTNode.types.NEXT||a.getType()==Blockly.ASTNode.types.BLOCK);)a=a.prev();a&&this.setCurNode(a);return a}; +Blockly.Cursor.prototype.out=function(){var a=this.getCurNode();if(!a)return null;(a=a.out())&&a.getType()==Blockly.ASTNode.types.BLOCK&&(a=a.prev()||a);a&&this.setCurNode(a);return a}; +Blockly.Cursor.prototype.onBlocklyAction=function(a){if(this.getCurNode()&&this.getCurNode().getType()===Blockly.ASTNode.types.FIELD&&this.getCurNode().getLocation().onBlocklyAction(a))return!0;switch(a.name){case Blockly.navigation.actionNames.PREVIOUS:return this.prev(),!0;case Blockly.navigation.actionNames.OUT:return this.out(),!0;case Blockly.navigation.actionNames.NEXT:return this.next(),!0;case Blockly.navigation.actionNames.IN:return this.in(),!0;default:return!1}};Blockly.BasicCursor=function(){Blockly.BasicCursor.superClass_.constructor.call(this)};Blockly.utils.object.inherits(Blockly.BasicCursor,Blockly.Cursor);Blockly.BasicCursor.prototype.next=function(){var a=this.getCurNode();if(!a)return null;(a=this.getNextNode_(a,this.validNode_))&&this.setCurNode(a);return a};Blockly.BasicCursor.prototype.in=function(){return this.next()}; +Blockly.BasicCursor.prototype.prev=function(){var a=this.getCurNode();if(!a)return null;(a=this.getPreviousNode_(a,this.validNode_))&&this.setCurNode(a);return a};Blockly.BasicCursor.prototype.out=function(){return this.prev()};Blockly.BasicCursor.prototype.getNextNode_=function(a,b){if(!a)return null;var c=a.in()||a.next();if(b(c))return c;if(c)return this.getNextNode_(c,b);a=this.findSiblingOrParent_(a.out());return b(a)?a:a?this.getNextNode_(a,b):null}; +Blockly.BasicCursor.prototype.getPreviousNode_=function(a,b){if(!a)return null;var c=a.prev();c=c?this.getRightMostChild_(c):a.out();return b(c)?c:c?this.getPreviousNode_(c,b):null};Blockly.BasicCursor.prototype.validNode_=function(a){var b=!1;a=a&&a.getType();if(a==Blockly.ASTNode.types.OUTPUT||a==Blockly.ASTNode.types.INPUT||a==Blockly.ASTNode.types.FIELD||a==Blockly.ASTNode.types.NEXT||a==Blockly.ASTNode.types.PREVIOUS||a==Blockly.ASTNode.types.WORKSPACE)b=!0;return b}; +Blockly.BasicCursor.prototype.findSiblingOrParent_=function(a){if(!a)return null;var b=a.next();return b?b:this.findSiblingOrParent_(a.out())};Blockly.BasicCursor.prototype.getRightMostChild_=function(a){if(!a.in())return a;for(a=a.in();a.next();)a=a.next();return this.getRightMostChild_(a)};Blockly.TabNavigateCursor=function(){Blockly.TabNavigateCursor.superClass_.constructor.call(this)};Blockly.utils.object.inherits(Blockly.TabNavigateCursor,Blockly.BasicCursor);Blockly.TabNavigateCursor.prototype.validNode_=function(a){var b=!1,c=a&&a.getType();a&&(a=a.getLocation(),c==Blockly.ASTNode.types.FIELD&&a&&a.isTabNavigable()&&a.isClickable()&&(b=!0));return b};Blockly.BlockSvg=function(a,b,c){this.svgGroup_=Blockly.utils.dom.createSvgElement("g",{},null);this.svgGroup_.translate_="";this.style=a.getRenderer().getConstants().getBlockStyle(null);this.pathObject=a.getRenderer().makePathObject(this.svgGroup_,this.style);this.renderIsInProgress_=this.rendered=!1;this.workspace=a;this.previousConnection=this.nextConnection=this.outputConnection=null;this.useDragSurface_=Blockly.utils.is3dSupported()&&!!a.getBlockDragSurface();var d=this.pathObject.svgPath;d.tooltip= +this;Blockly.Tooltip.bindMouseEvents(d);Blockly.BlockSvg.superClass_.constructor.call(this,a,b,c);this.svgGroup_.dataset&&(this.svgGroup_.dataset.id=this.id)};Blockly.utils.object.inherits(Blockly.BlockSvg,Blockly.Block);Blockly.BlockSvg.prototype.height=0;Blockly.BlockSvg.prototype.width=0;Blockly.BlockSvg.prototype.warningTextDb_=null;Blockly.BlockSvg.INLINE=-1;Blockly.BlockSvg.COLLAPSED_WARNING_ID="TEMP_COLLAPSED_WARNING_"; +Blockly.BlockSvg.prototype.initSvg=function(){if(!this.workspace.rendered)throw TypeError("Workspace is headless.");for(var a=0,b;b=this.inputList[a];a++)b.init();b=this.getIcons();for(a=0;a=this.connections_.length)return-1;b=a.y;for(var d=c;0<=d&&this.connections_[d].y==b;){if(this.connections_[d]==a)return d;d--}for(;ca)c=d;else{b=d;break}}return b};Blockly.ConnectionDB.prototype.removeConnection=function(a,b){a=this.findIndexOfConnection_(a,b);if(-1==a)throw Error("Unable to find connection in connectionDB.");this.connections_.splice(a,1)}; +Blockly.ConnectionDB.prototype.getNeighbours=function(a,b){function c(a){var c=e-d[a].x,g=f-d[a].y;Math.sqrt(c*c+g*g)<=b&&k.push(d[a]);return ga)throw Error("Cannot unsubscribe a workspace that hasn't been subscribed.");this.subscribedWorkspaces_.splice(a,1)}; +Blockly.ThemeManager.prototype.subscribe=function(a,b,c){this.componentDB_[b]||(this.componentDB_[b]=[]);this.componentDB_[b].push({element:a,propertyName:c});b=this.theme_&&this.theme_.getComponentStyle(b);a.style[c]=b||""};Blockly.ThemeManager.prototype.unsubscribe=function(a){if(a)for(var b=Object.keys(this.componentDB_),c=0,d;d=b[c];c++){for(var e=this.componentDB_[d],f=e.length-1;0<=f;f--)e[f].element===a&&e.splice(f,1);this.componentDB_[d].length||delete this.componentDB_[d]}}; +Blockly.ThemeManager.prototype.dispose=function(){this.componentDB_=this.subscribedWorkspaces_=this.theme_=this.owner_=null};Blockly.TouchGesture=function(a,b){Blockly.TouchGesture.superClass_.constructor.call(this,a,b);this.isMultiTouch_=!1;this.cachedPoints_=Object.create(null);this.startDistance_=this.previousScale_=0;this.isPinchZoomEnabled_=this.onStartWrapper_=null};Blockly.utils.object.inherits(Blockly.TouchGesture,Blockly.Gesture);Blockly.TouchGesture.ZOOM_IN_MULTIPLIER=5;Blockly.TouchGesture.ZOOM_OUT_MULTIPLIER=6; +Blockly.TouchGesture.prototype.doStart=function(a){this.isPinchZoomEnabled_=this.startWorkspace_.options.zoomOptions&&this.startWorkspace_.options.zoomOptions.pinch;Blockly.TouchGesture.superClass_.doStart.call(this,a);!this.isEnding_&&Blockly.Touch.isTouchEvent(a)&&this.handleTouchStart(a)}; +Blockly.TouchGesture.prototype.bindMouseEvents=function(a){this.onStartWrapper_=Blockly.bindEventWithChecks_(document,"mousedown",null,this.handleStart.bind(this),!0);this.onMoveWrapper_=Blockly.bindEventWithChecks_(document,"mousemove",null,this.handleMove.bind(this),!0);this.onUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",null,this.handleUp.bind(this),!0);a.preventDefault();a.stopPropagation()}; +Blockly.TouchGesture.prototype.handleStart=function(a){!this.isDragging()&&Blockly.Touch.isTouchEvent(a)&&(this.handleTouchStart(a),this.isMultiTouch()&&Blockly.longStop_())};Blockly.TouchGesture.prototype.handleMove=function(a){this.isDragging()?Blockly.Touch.shouldHandleEvent(a)&&Blockly.TouchGesture.superClass_.handleMove.call(this,a):this.isMultiTouch()?(Blockly.Touch.isTouchEvent(a)&&this.handleTouchMove(a),Blockly.longStop_()):Blockly.TouchGesture.superClass_.handleMove.call(this,a)}; +Blockly.TouchGesture.prototype.handleUp=function(a){Blockly.Touch.isTouchEvent(a)&&!this.isDragging()&&this.handleTouchEnd(a);!this.isMultiTouch()||this.isDragging()?Blockly.Touch.shouldHandleEvent(a)&&Blockly.TouchGesture.superClass_.handleUp.call(this,a):(a.preventDefault(),a.stopPropagation(),this.dispose())};Blockly.TouchGesture.prototype.isMultiTouch=function(){return this.isMultiTouch_}; +Blockly.TouchGesture.prototype.dispose=function(){Blockly.TouchGesture.superClass_.dispose.call(this);this.onStartWrapper_&&Blockly.unbindEvent_(this.onStartWrapper_)};Blockly.TouchGesture.prototype.handleTouchStart=function(a){var b=Blockly.Touch.getTouchIdentifierFromEvent(a);this.cachedPoints_[b]=this.getTouchPoint(a);b=Object.keys(this.cachedPoints_);2==b.length&&(this.startDistance_=Blockly.utils.Coordinate.distance(this.cachedPoints_[b[0]],this.cachedPoints_[b[1]]),this.isMultiTouch_=!0,a.preventDefault())}; +Blockly.TouchGesture.prototype.handleTouchMove=function(a){var b=Blockly.Touch.getTouchIdentifierFromEvent(a);this.cachedPoints_[b]=this.getTouchPoint(a);b=Object.keys(this.cachedPoints_);this.isPinchZoomEnabled_&&2===b.length?this.handlePinch_(a):Blockly.TouchGesture.superClass_.handleMove.call(this,a)}; +Blockly.TouchGesture.prototype.handlePinch_=function(a){var b=Object.keys(this.cachedPoints_);b=Blockly.utils.Coordinate.distance(this.cachedPoints_[b[0]],this.cachedPoints_[b[1]])/this.startDistance_;if(0this.previousScale_){var c=b-this.previousScale_;c=0Object.keys(this.cachedPoints_).length&&(this.cachedPoints_=Object.create(null),this.previousScale_=0)};Blockly.TouchGesture.prototype.getTouchPoint=function(a){return this.startWorkspace_?new Blockly.utils.Coordinate(a.pageX?a.pageX:a.changedTouches[0].pageX,a.pageY?a.pageY:a.changedTouches[0].pageY):null};Blockly.WorkspaceAudio=function(a){this.parentWorkspace_=a;this.SOUNDS_=Object.create(null)};Blockly.WorkspaceAudio.prototype.lastSound_=null;Blockly.WorkspaceAudio.prototype.dispose=function(){this.SOUNDS_=this.parentWorkspace_=null}; +Blockly.WorkspaceAudio.prototype.load=function(a,b){if(a.length){try{var c=new Blockly.utils.global.Audio}catch(h){return}for(var d,e=0;e=this.remainingCapacity()||(this.currentGesture_&&this.currentGesture_.cancel(),"comment"==a.tagName.toLowerCase()?this.pasteWorkspaceComment_(a):this.pasteBlock_(a))}; +Blockly.WorkspaceSvg.prototype.pasteBlock_=function(a){Blockly.Events.disable();try{var b=Blockly.Xml.domToBlock(a,this),c=this.getMarker(Blockly.navigation.MARKER_NAME).getCurNode();if(this.keyboardAccessibilityMode&&c&&c.isConnection()){var d=c.getLocation();Blockly.navigation.insertBlock(b,d);return}var e=parseInt(a.getAttribute("x"),10),f=parseInt(a.getAttribute("y"),10);if(!isNaN(e)&&!isNaN(f)){this.RTL&&(e=-e);do{a=!1;var g=this.getAllBlocks(!1);c=0;for(var h;h=g[c];c++){var k=h.getRelativeToSurfaceXY(); +if(1>=Math.abs(e-k.x)&&1>=Math.abs(f-k.y)){a=!0;break}}if(!a){var l=b.getConnections_(!1);c=0;for(var m;m=l[c];c++)if(m.closest(Blockly.SNAP_RADIUS,new Blockly.utils.Coordinate(e,f)).connection){a=!0;break}}a&&(e=this.RTL?e-Blockly.SNAP_RADIUS:e+Blockly.SNAP_RADIUS,f+=2*Blockly.SNAP_RADIUS)}while(a);b.moveBy(e,f)}}finally{Blockly.Events.enable()}Blockly.Events.isEnabled()&&!b.isShadow()&&Blockly.Events.fire(new Blockly.Events.BlockCreate(b));b.select()}; +Blockly.WorkspaceSvg.prototype.pasteWorkspaceComment_=function(a){Blockly.Events.disable();try{var b=Blockly.WorkspaceCommentSvg.fromXml(a,this),c=parseInt(a.getAttribute("x"),10),d=parseInt(a.getAttribute("y"),10);isNaN(c)||isNaN(d)||(this.RTL&&(c=-c),b.moveBy(c+50,d+50))}finally{Blockly.Events.enable()}Blockly.Events.isEnabled();b.select()}; +Blockly.WorkspaceSvg.prototype.refreshToolboxSelection=function(){var a=this.isFlyout?this.targetWorkspace:this;a&&!a.currentGesture_&&a.toolbox_&&a.toolbox_.getFlyout()&&a.toolbox_.refreshSelection()};Blockly.WorkspaceSvg.prototype.renameVariableById=function(a,b){Blockly.WorkspaceSvg.superClass_.renameVariableById.call(this,a,b);this.refreshToolboxSelection()};Blockly.WorkspaceSvg.prototype.deleteVariableById=function(a){Blockly.WorkspaceSvg.superClass_.deleteVariableById.call(this,a);this.refreshToolboxSelection()}; +Blockly.WorkspaceSvg.prototype.createVariable=function(a,b,c){a=Blockly.WorkspaceSvg.superClass_.createVariable.call(this,a,b,c);this.refreshToolboxSelection();return a};Blockly.WorkspaceSvg.prototype.recordDeleteAreas=function(){this.deleteAreaTrash_=this.trashcan&&this.svgGroup_.parentNode?this.trashcan.getClientRect():null;this.deleteAreaToolbox_=this.flyout_?this.flyout_.getClientRect():this.toolbox_&&"function"==typeof this.toolbox_.getClientRect?this.toolbox_.getClientRect():null}; +Blockly.WorkspaceSvg.prototype.isDeleteArea=function(a){return this.deleteAreaTrash_&&this.deleteAreaTrash_.contains(a.clientX,a.clientY)?Blockly.DELETE_AREA_TRASH:this.deleteAreaToolbox_&&this.deleteAreaToolbox_.contains(a.clientX,a.clientY)?Blockly.DELETE_AREA_TOOLBOX:Blockly.DELETE_AREA_NONE};Blockly.WorkspaceSvg.prototype.onMouseDown_=function(a){var b=this.getGesture(a);b&&b.handleWsStart(a,this)}; +Blockly.WorkspaceSvg.prototype.startDrag=function(a,b){a=Blockly.utils.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM());a.x/=this.scale;a.y/=this.scale;this.dragDeltaXY_=Blockly.utils.Coordinate.difference(b,a)};Blockly.WorkspaceSvg.prototype.moveDrag=function(a){a=Blockly.utils.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM());a.x/=this.scale;a.y/=this.scale;return Blockly.utils.Coordinate.sum(this.dragDeltaXY_,a)}; +Blockly.WorkspaceSvg.prototype.isDragging=function(){return null!=this.currentGesture_&&this.currentGesture_.isDragging()};Blockly.WorkspaceSvg.prototype.isDraggable=function(){return this.options.moveOptions&&this.options.moveOptions.drag}; +Blockly.WorkspaceSvg.prototype.isContentBounded=function(){return this.options.moveOptions&&this.options.moveOptions.scrollbars||this.options.moveOptions&&this.options.moveOptions.wheel||this.options.moveOptions&&this.options.moveOptions.drag||this.options.zoomOptions&&this.options.zoomOptions.controls||this.options.zoomOptions&&this.options.zoomOptions.wheel||this.options.zoomOptions&&this.options.zoomOptions.pinch}; +Blockly.WorkspaceSvg.prototype.isMovable=function(){return this.options.moveOptions&&this.options.moveOptions.scrollbars||this.options.moveOptions&&this.options.moveOptions.wheel||this.options.moveOptions&&this.options.moveOptions.drag||this.options.zoomOptions&&this.options.zoomOptions.wheel||this.options.zoomOptions&&this.options.zoomOptions.pinch}; +Blockly.WorkspaceSvg.prototype.onMouseWheel_=function(a){if(Blockly.Gesture.inProgress())a.preventDefault(),a.stopPropagation();else{var b=this.options.zoomOptions&&this.options.zoomOptions.wheel,c=this.options.moveOptions&&this.options.moveOptions.wheel;if(b||c){var d=Blockly.utils.getScrollDeltaPixels(a);!b||!a.ctrlKey&&c?(b=this.scrollX-d.x,c=this.scrollY-d.y,a.shiftKey&&!d.x&&(b=this.scrollX-d.y,c=this.scrollY),this.scroll(b,c)):(d=-d.y/50,b=Blockly.utils.mouseToSvg(a,this.getParentSvg(),this.getInverseScreenCTM()), +this.zoom(b.x,b.y,d));a.preventDefault()}}};Blockly.WorkspaceSvg.prototype.getBlocksBoundingBox=function(){var a=this.getTopBoundedElements();if(!a.length)return new Blockly.utils.Rect(0,0,0,0);for(var b=a[0].getBoundingRectangle(),c=1;cb.bottom&&(b.bottom=d.bottom);d.leftb.right&&(b.right=d.right)}return b}; +Blockly.WorkspaceSvg.prototype.cleanUp=function(){this.setResizesEnabled(!1);Blockly.Events.setGroup(!0);for(var a=this.getTopBlocks(!0),b=0,c=0,d;d=a[c];c++)if(d.isMovable()){var e=d.getRelativeToSurfaceXY();d.moveBy(-e.x,b-e.y);d.snapToGrid();b=d.getRelativeToSurfaceXY().y+d.getHeightWidth().height+this.renderer_.getConstants().MIN_BLOCK_HEIGHT}Blockly.Events.setGroup(!1);this.setResizesEnabled(!0)}; +Blockly.WorkspaceSvg.prototype.showContextMenu=function(a){function b(a){if(a.isDeletable())p=p.concat(a.getDescendants(!1));else{a=a.getChildren(!1);for(var c=0;cp.length?c():Blockly.confirm(Blockly.Msg.DELETE_ALL_BLOCKS.replace("%1",p.length),function(a){a&& +c()})}};d.push(h);this.configureContextMenu&&this.configureContextMenu(d,a);Blockly.ContextMenu.show(a,d,this.RTL)}}; +Blockly.WorkspaceSvg.prototype.updateToolbox=function(a){Array.isArray(a)||(a=Blockly.Options.parseToolboxTree(a));if(a=Blockly.utils.toolbox.convertToolboxToJSON(a)){if(!this.options.languageTree)throw Error("Existing toolbox is null. Can't create new toolbox.");if(Blockly.utils.toolbox.hasCategories(a)){if(!this.toolbox_)throw Error("Existing toolbox has no categories. Can't change mode.");this.options.languageTree=a;this.toolbox_.render(a)}else{if(!this.flyout_)throw Error("Existing toolbox has categories. Can't change mode."); +this.options.languageTree=a;this.flyout_.show(a)}}else if(this.options.languageTree)throw Error("Can't nullify an existing toolbox.");};Blockly.WorkspaceSvg.prototype.markFocused=function(){this.options.parentWorkspace?this.options.parentWorkspace.markFocused():(Blockly.mainWorkspace=this,this.setBrowserFocus())};Blockly.WorkspaceSvg.prototype.setBrowserFocus=function(){document.activeElement&&document.activeElement.blur();try{this.getParentSvg().focus({preventScroll:!0})}catch(a){try{this.getParentSvg().parentNode.setActive()}catch(b){this.getParentSvg().parentNode.focus({preventScroll:!0})}}}; +Blockly.WorkspaceSvg.prototype.zoom=function(a,b,c){c=Math.pow(this.options.zoomOptions.scaleSpeed,c);var d=this.scale*c;if(this.scale!=d){d>this.options.zoomOptions.maxScale?c=this.options.zoomOptions.maxScale/this.scale:dthis.options.zoomOptions.maxScale?a=this.options.zoomOptions.maxScale:this.options.zoomOptions.minScale&&ab.viewBottom||b.contentLeftb.viewRight){c=null;a&&(c=Blockly.Events.getGroup(),Blockly.Events.setGroup(a.group));switch(a.type){case Blockly.Events.BLOCK_CREATE:case Blockly.Events.BLOCK_MOVE:var f= +e.getBlockById(a.blockId);f&&(f=f.getRootBlock());break;case Blockly.Events.COMMENT_CREATE:case Blockly.Events.COMMENT_MOVE:f=e.getCommentById(a.commentId)}if(f){d=f.getBoundingRectangle();d.height=d.bottom-d.top;d.width=d.right-d.left;var m=b.viewTop,n=b.viewBottom-d.height;n=Math.max(m,n);m=Blockly.utils.math.clamp(m,d.top,n)-d.top;n=b.viewLeft;var p=b.viewRight-d.width;b.RTL?n=Math.min(p,n):p=Math.max(n,p);b=Blockly.utils.math.clamp(n,d.left,p)-d.left;f.moveBy(b,m)}a&&(!a.group&&f&&console.log("WARNING: Moved object in bounds but there was no event group. This may break undo."), +null!==c&&Blockly.Events.setGroup(c))}}});Blockly.svgResize(e);Blockly.WidgetDiv.createDom();Blockly.DropDownDiv.createDom();Blockly.Tooltip.createDom();return e}; +Blockly.init_=function(a){var b=a.options,c=a.getParentSvg();Blockly.bindEventWithChecks_(c.parentNode,"contextmenu",null,function(a){Blockly.utils.isTargetInput(a)||a.preventDefault()});c=Blockly.bindEventWithChecks_(window,"resize",null,function(){Blockly.hideChaff(!0);Blockly.svgResize(a)});a.setResizeHandlerWrapper(c);Blockly.inject.bindDocumentEvents_();if(b.languageTree){c=a.getToolbox();var d=a.getFlyout(!0);c?c.init():d&&(d.init(a),d.show(b.languageTree),d.scrollToStart())}c=Blockly.Scrollbar.scrollbarThickness; +b.hasTrashcan&&(c=a.trashcan.init(c));b.zoomOptions&&b.zoomOptions.controls&&a.zoomControls_.init(c);b.moveOptions&&b.moveOptions.scrollbars?(a.scrollbar=new Blockly.ScrollbarPair(a),a.scrollbar.resize()):a.setMetrics({x:.5,y:.5});b.hasSounds&&Blockly.inject.loadSounds_(b.pathToMedia,a)}; +Blockly.inject.bindDocumentEvents_=function(){Blockly.documentEventsBound_||(Blockly.bindEventWithChecks_(document,"scroll",null,function(){for(var a=Blockly.Workspace.getAll(),b=0,c;c=a[b];b++)c.updateInverseScreenCTM&&c.updateInverseScreenCTM()}),Blockly.bindEventWithChecks_(document,"keydown",null,Blockly.onKeyDown),Blockly.bindEvent_(document,"touchend",null,Blockly.longStop_),Blockly.bindEvent_(document,"touchcancel",null,Blockly.longStop_),Blockly.utils.userAgent.IPAD&&Blockly.bindEventWithChecks_(window, +"orientationchange",document,function(){Blockly.svgResize(Blockly.getMainWorkspace())}));Blockly.documentEventsBound_=!0}; +Blockly.inject.loadSounds_=function(a,b){var c=b.getAudioManager();c.load([a+"click.mp3",a+"click.wav",a+"click.ogg"],"click");c.load([a+"disconnect.wav",a+"disconnect.mp3",a+"disconnect.ogg"],"disconnect");c.load([a+"delete.mp3",a+"delete.ogg",a+"delete.wav"],"delete");var d=[];a=function(){for(;d.length;)Blockly.unbindEvent_(d.pop());c.preload()};d.push(Blockly.bindEventWithChecks_(document,"mousemove",null,a,!0));d.push(Blockly.bindEventWithChecks_(document,"touchstart",null,a,!0))};Blockly.Names=function(a,b){this.variablePrefix_=b||"";this.reservedDict_=Object.create(null);if(a)for(a=a.split(","),b=0;b1'),d.appendChild(c),b.push(d));if(Blockly.Blocks.variables_get){a.sort(Blockly.VariableModel.compareByName);c=0;for(var e;e=a[c];c++)d=Blockly.utils.xml.createElement("block"),d.setAttribute("type","variables_get"),d.setAttribute("gap",8),d.appendChild(Blockly.Variables.generateVariableFieldDom(e)),b.push(d)}}return b}; +Blockly.Variables.VAR_LETTER_OPTIONS="ijkmnopqrstuvwxyzabcdefgh";Blockly.Variables.generateUniqueName=function(a){return Blockly.Variables.generateUniqueNameFromOptions(Blockly.Variables.VAR_LETTER_OPTIONS.charAt(0),a.getAllVariableNames())}; +Blockly.Variables.generateUniqueNameFromOptions=function(a,b){if(!b.length)return a;for(var c=Blockly.Variables.VAR_LETTER_OPTIONS,d="",e=c.indexOf(a);;){for(var f=!1,g=0;ge?Blockly.WidgetDiv.positionInternal_(a,0,c.height+e):Blockly.WidgetDiv.positionInternal_(a,e,c.height)};Blockly.WidgetDiv.calculateX_=function(a,b,c,d){if(d)return b=Math.max(b.right-c.width,a.left),Math.min(b,a.right-c.width);b=Math.min(b.left,a.right-c.width);return Math.max(b,a.left)}; +Blockly.WidgetDiv.calculateY_=function(a,b,c){return b.bottom+c.height>=a.bottom?b.top-c.height:b.bottom};Blockly.VERSION="3.20200625.2";Blockly.mainWorkspace=null;Blockly.selected=null;Blockly.draggingConnections=[];Blockly.clipboardXml_=null;Blockly.clipboardSource_=null;Blockly.clipboardTypeCounts_=null;Blockly.cache3dSupported_=null;Blockly.parentContainer=null;Blockly.svgSize=function(a){return new Blockly.utils.Size(a.cachedWidth_,a.cachedHeight_)};Blockly.resizeSvgContents=function(a){a.resizeContents()}; +Blockly.svgResize=function(a){for(;a.options.parentWorkspace;)a=a.options.parentWorkspace;var b=a.getParentSvg(),c=b.parentNode;if(c){var d=c.offsetWidth;c=c.offsetHeight;b.cachedWidth_!=d&&(b.setAttribute("width",d+"px"),b.cachedWidth_=d);b.cachedHeight_!=c&&(b.setAttribute("height",c+"px"),b.cachedHeight_=c);a.resize()}}; +Blockly.onKeyDown=function(a){var b=Blockly.mainWorkspace;if(b&&!(Blockly.utils.isTargetInput(a)||b.rendered&&!b.isVisible()))if(b.options.readOnly)Blockly.navigation.onKeyPress(a);else{var c=!1;if(a.keyCode==Blockly.utils.KeyCodes.ESC)Blockly.hideChaff(),Blockly.navigation.onBlocklyAction(Blockly.navigation.ACTION_EXIT);else{if(!Blockly.Gesture.inProgress()&&Blockly.navigation.onKeyPress(a))return;if(a.keyCode==Blockly.utils.KeyCodes.BACKSPACE||a.keyCode==Blockly.utils.KeyCodes.DELETE){a.preventDefault(); +if(Blockly.Gesture.inProgress())return;Blockly.selected&&Blockly.selected.isDeletable()&&(c=!0)}else if(a.altKey||a.ctrlKey||a.metaKey){if(Blockly.Gesture.inProgress())return;Blockly.selected&&Blockly.selected.isDeletable()&&Blockly.selected.isMovable()&&(a.keyCode==Blockly.utils.KeyCodes.C?(Blockly.hideChaff(),Blockly.copy_(Blockly.selected)):a.keyCode!=Blockly.utils.KeyCodes.X||Blockly.selected.workspace.isFlyout||(Blockly.copy_(Blockly.selected),c=!0));a.keyCode==Blockly.utils.KeyCodes.V?Blockly.clipboardXml_&& +(a=Blockly.clipboardSource_,a.isFlyout&&(a=a.targetWorkspace),Blockly.clipboardTypeCounts_&&a.isCapacityAvailable(Blockly.clipboardTypeCounts_)&&(Blockly.Events.setGroup(!0),a.paste(Blockly.clipboardXml_),Blockly.Events.setGroup(!1))):a.keyCode==Blockly.utils.KeyCodes.Z?(Blockly.hideChaff(),b.undo(a.shiftKey)):a.ctrlKey&&a.keyCode==Blockly.utils.KeyCodes.Y&&(Blockly.hideChaff(),b.undo(!0))}}c&&!Blockly.selected.workspace.isFlyout&&(Blockly.Events.setGroup(!0),Blockly.hideChaff(),Blockly.selected.dispose(!0, +!0),Blockly.Events.setGroup(!1))}};Blockly.copy_=function(a){a=a.toCopyData();Blockly.clipboardXml_=a.xml;Blockly.clipboardSource_=a.source;Blockly.clipboardTypeCounts_=a.typeCounts};Blockly.duplicate=function(a){var b=Blockly.clipboardXml_,c=Blockly.clipboardSource_;Blockly.copy_(a);a.workspace.paste(Blockly.clipboardXml_);Blockly.clipboardXml_=b;Blockly.clipboardSource_=c};Blockly.onContextMenu_=function(a){Blockly.utils.isTargetInput(a)||a.preventDefault()}; +Blockly.hideChaff=function(a){Blockly.Tooltip.hide();Blockly.WidgetDiv.hide();Blockly.DropDownDiv.hideWithoutAnimation();a||(a=Blockly.getMainWorkspace(),a.trashcan&&a.trashcan.flyout&&a.trashcan.flyout.hide(),(a=a.getToolbox())&&a.getFlyout()&&a.getFlyout().autoClose&&a.clearSelection())};Blockly.getMainWorkspace=function(){return Blockly.mainWorkspace};Blockly.alert=function(a,b){alert(a);b&&b()};Blockly.confirm=function(a,b){b(confirm(a))};Blockly.prompt=function(a,b,c){c(prompt(a,b))}; +Blockly.jsonInitFactory_=function(a){return function(){this.jsonInit(a)}}; +Blockly.defineBlocksWithJsonArray=function(a){for(var b=0;b90-b||a>-90-b&&a<-90+b?!0:!1}; +Blockly.HorizontalFlyout.prototype.getClientRect=function(){if(!this.svgGroup_)return null;var a=this.svgGroup_.getBoundingClientRect(),b=a.top;return this.toolboxPosition_==Blockly.TOOLBOX_AT_TOP?new Blockly.utils.Rect(-1E9,b+a.height,-1E9,1E9):new Blockly.utils.Rect(b,1E9,-1E9,1E9)}; +Blockly.HorizontalFlyout.prototype.reflowInternal_=function(){this.workspace_.scale=this.targetWorkspace.scale;for(var a=0,b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++)a=Math.max(a,d.getHeightWidth().height);a+=1.5*this.MARGIN;a*=this.workspace_.scale;a+=Blockly.Scrollbar.scrollbarThickness;if(this.height_!=a){for(c=0;d=b[c];c++)d.flyoutRect_&&this.moveRectToBlock_(d.flyoutRect_,d);this.height_=a;this.position()}};Blockly.VerticalFlyout=function(a){Blockly.VerticalFlyout.superClass_.constructor.call(this,a)};Blockly.utils.object.inherits(Blockly.VerticalFlyout,Blockly.Flyout); +Blockly.VerticalFlyout.prototype.getMetrics_=function(){if(!this.isVisible())return null;try{var a=this.workspace_.getCanvas().getBBox()}catch(e){a={height:0,y:0,width:0,x:0}}var b=this.SCROLLBAR_PADDING,c=this.height_-2*this.SCROLLBAR_PADDING,d=this.width_;this.RTL||(d-=this.SCROLLBAR_PADDING);return{contentHeight:a.height*this.workspace_.scale+2*this.MARGIN,contentWidth:a.width*this.workspace_.scale+2*this.MARGIN,contentTop:a.y,contentLeft:a.x,viewHeight:c,viewWidth:d,viewTop:-this.workspace_.scrollY+ +a.y,viewLeft:-this.workspace_.scrollX,absoluteTop:b,absoluteLeft:0}};Blockly.VerticalFlyout.prototype.setMetrics_=function(a){var b=this.getMetrics_();b&&("number"==typeof a.y&&(this.workspace_.scrollY=-b.contentHeight*a.y),this.workspace_.translate(this.workspace_.scrollX+b.absoluteLeft,this.workspace_.scrollY+b.absoluteTop))}; +Blockly.VerticalFlyout.prototype.position=function(){if(this.isVisible()){var a=this.targetWorkspace.getMetrics();a&&(this.height_=a.viewHeight,this.setBackgroundPath_(this.width_-this.CORNER_RADIUS,a.viewHeight-2*this.CORNER_RADIUS),this.positionAt_(this.width_,this.height_,this.targetWorkspace.toolboxPosition==this.toolboxPosition_?a.toolboxWidth?this.toolboxPosition_==Blockly.TOOLBOX_AT_LEFT?a.toolboxWidth:a.viewWidth-this.width_:this.toolboxPosition_==Blockly.TOOLBOX_AT_LEFT?0:a.viewWidth:this.toolboxPosition_== +Blockly.TOOLBOX_AT_LEFT?0:a.viewWidth+a.absoluteLeft-this.width_,0))}}; +Blockly.VerticalFlyout.prototype.setBackgroundPath_=function(a,b){var c=this.toolboxPosition_==Blockly.TOOLBOX_AT_RIGHT,d=a+this.CORNER_RADIUS;d=["M "+(c?d:0)+",0"];d.push("h",c?-a:a);d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,c?0:1,c?-this.CORNER_RADIUS:this.CORNER_RADIUS,this.CORNER_RADIUS);d.push("v",Math.max(0,b));d.push("a",this.CORNER_RADIUS,this.CORNER_RADIUS,0,0,c?0:1,c?this.CORNER_RADIUS:-this.CORNER_RADIUS,this.CORNER_RADIUS);d.push("h",c?a:-a);d.push("z");this.svgBackground_.setAttribute("d", +d.join(" "))};Blockly.VerticalFlyout.prototype.scrollToStart=function(){this.scrollbar.set(0)};Blockly.VerticalFlyout.prototype.wheel_=function(a){var b=Blockly.utils.getScrollDeltaPixels(a);if(b.y){var c=this.getMetrics_();b=c.viewTop-c.contentTop+b.y;b=Math.min(b,c.contentHeight-c.viewHeight);b=Math.max(b,0);this.scrollbar.set(b);Blockly.WidgetDiv.hide();Blockly.DropDownDiv.hideWithoutAnimation()}a.preventDefault();a.stopPropagation()}; +Blockly.VerticalFlyout.prototype.layout_=function(a,b){this.workspace_.scale=this.targetWorkspace.scale;for(var c=this.MARGIN,d=this.RTL?c:c+this.tabWidth_,e=0,f;f=a[e];e++)if("block"==f.type){f=f.block;for(var g=f.getDescendants(!1),h=0,k;k=g[h];h++)k.isInFlyout=!0;f.render();g=f.getSvgRoot();h=f.getHeightWidth();k=f.outputConnection?d-this.tabWidth_:d;f.moveBy(k,c);k=this.createRect_(f,this.RTL?k-h.width:k,c,h,e);this.addBlockListeners_(g,f,k);c+=h.height+b[e]}else"button"==f.type&&(this.initFlyoutButton_(f.button, +d,c),c+=f.button.height+b[e])};Blockly.VerticalFlyout.prototype.isDragTowardWorkspace=function(a){a=Math.atan2(a.y,a.x)/Math.PI*180;var b=this.dragAngleRange_;return a-b||a<-180+b||a>180-b?!0:!1};Blockly.VerticalFlyout.prototype.getClientRect=function(){if(!this.svgGroup_)return null;var a=this.svgGroup_.getBoundingClientRect(),b=a.left;return this.toolboxPosition_==Blockly.TOOLBOX_AT_LEFT?new Blockly.utils.Rect(-1E9,1E9,-1E9,b+a.width):new Blockly.utils.Rect(-1E9,1E9,b,1E9)}; +Blockly.VerticalFlyout.prototype.reflowInternal_=function(){this.workspace_.scale=this.targetWorkspace.scale;for(var a=0,b=this.workspace_.getTopBlocks(!1),c=0,d;d=b[c];c++){var e=d.getHeightWidth().width;d.outputConnection&&(e-=this.tabWidth_);a=Math.max(a,e)}for(c=0;d=this.buttons_[c];c++)a=Math.max(a,d.width);a+=1.5*this.MARGIN+this.tabWidth_;a*=this.workspace_.scale;a+=Blockly.Scrollbar.scrollbarThickness;if(this.width_!=a){for(c=0;d=b[c];c++){if(this.RTL){e=d.getRelativeToSurfaceXY().x;var f= +a/this.workspace_.scale-this.MARGIN;d.outputConnection||(f-=this.tabWidth_);d.moveBy(f-e,0)}d.flyoutRect_&&this.moveRectToBlock_(d.flyoutRect_,d)}if(this.RTL)for(c=0;d=this.buttons_[c];c++)b=d.getPosition().y,d.moveTo(a/this.workspace_.scale-d.width-this.MARGIN-this.tabWidth_,b);this.width_=a;this.position()}};Blockly.FlyoutButton=function(a,b,c,d){this.workspace_=a;this.targetWorkspace_=b;this.text_=c.text;this.position_=new Blockly.utils.Coordinate(0,0);this.isLabel_=d;this.callbackKey_=c.callbackKey||c.callbackkey;this.cssClass_=c["web-class"]||null;this.onMouseUpWrapper_=null};Blockly.FlyoutButton.MARGIN_X=5;Blockly.FlyoutButton.MARGIN_Y=2;Blockly.FlyoutButton.prototype.width=0;Blockly.FlyoutButton.prototype.height=0; +Blockly.FlyoutButton.prototype.createDom=function(){var a=this.isLabel_?"blocklyFlyoutLabel":"blocklyFlyoutButton";this.cssClass_&&(a+=" "+this.cssClass_);this.svgGroup_=Blockly.utils.dom.createSvgElement("g",{"class":a},this.workspace_.getCanvas());if(!this.isLabel_)var b=Blockly.utils.dom.createSvgElement("rect",{"class":"blocklyFlyoutButtonShadow",rx:4,ry:4,x:1,y:1},this.svgGroup_);a=Blockly.utils.dom.createSvgElement("rect",{"class":this.isLabel_?"blocklyFlyoutLabelBackground":"blocklyFlyoutButtonBackground", +rx:4,ry:4},this.svgGroup_);var c=Blockly.utils.dom.createSvgElement("text",{"class":this.isLabel_?"blocklyFlyoutLabelText":"blocklyText",x:0,y:0,"text-anchor":"middle"},this.svgGroup_),d=Blockly.utils.replaceMessageReferences(this.text_);this.workspace_.RTL&&(d+="\u200f");c.textContent=d;this.isLabel_&&(this.svgText_=c,this.workspace_.getThemeManager().subscribe(this.svgText_,"flyoutForegroundColour","fill"));var e=Blockly.utils.style.getComputedStyle(c,"fontSize"),f=Blockly.utils.style.getComputedStyle(c, +"fontWeight"),g=Blockly.utils.style.getComputedStyle(c,"fontFamily");this.width=Blockly.utils.dom.getFastTextWidthWithSizeString(c,e,f,g);d=Blockly.utils.dom.measureFontMetrics(d,e,f,g);this.height=d.height;this.isLabel_||(this.width+=2*Blockly.FlyoutButton.MARGIN_X,this.height+=2*Blockly.FlyoutButton.MARGIN_Y,b.setAttribute("width",this.width),b.setAttribute("height",this.height));a.setAttribute("width",this.width);a.setAttribute("height",this.height);c.setAttribute("x",this.width/2);c.setAttribute("y", +this.height/2-d.height/2+d.baseline);this.updateTransform_();this.onMouseUpWrapper_=Blockly.bindEventWithChecks_(this.svgGroup_,"mouseup",this,this.onMouseUp_);return this.svgGroup_};Blockly.FlyoutButton.prototype.show=function(){this.updateTransform_();this.svgGroup_.setAttribute("display","block")};Blockly.FlyoutButton.prototype.updateTransform_=function(){this.svgGroup_.setAttribute("transform","translate("+this.position_.x+","+this.position_.y+")")}; +Blockly.FlyoutButton.prototype.moveTo=function(a,b){this.position_.x=a;this.position_.y=b;this.updateTransform_()};Blockly.FlyoutButton.prototype.getPosition=function(){return this.position_};Blockly.FlyoutButton.prototype.getTargetWorkspace=function(){return this.targetWorkspace_};Blockly.FlyoutButton.prototype.dispose=function(){this.onMouseUpWrapper_&&Blockly.unbindEvent_(this.onMouseUpWrapper_);this.svgGroup_&&Blockly.utils.dom.removeNode(this.svgGroup_);this.svgText_&&this.workspace_.getThemeManager().unsubscribe(this.svgText_)}; +Blockly.FlyoutButton.prototype.onMouseUp_=function(a){(a=this.targetWorkspace_.getGesture(a))&&a.cancel();this.isLabel_&&this.callbackKey_?console.warn("Labels should not have callbacks. Label text: "+this.text_):this.isLabel_||this.callbackKey_&&this.targetWorkspace_.getButtonCallback(this.callbackKey_)?this.isLabel_||this.targetWorkspace_.getButtonCallback(this.callbackKey_)(this):console.warn("Buttons should have callbacks. Button text: "+this.text_)};Blockly.Css.register(".blocklyFlyoutButton {,fill: #888;,cursor: default;,},.blocklyFlyoutButtonShadow {,fill: #666;,},.blocklyFlyoutButton:hover {,fill: #aaa;,},.blocklyFlyoutLabel {,cursor: default;,},.blocklyFlyoutLabelBackground {,opacity: 0;,}".split(","));Blockly.Generator=function(a){this.name_=a;this.FUNCTION_NAME_PLACEHOLDER_REGEXP_=new RegExp(this.FUNCTION_NAME_PLACEHOLDER_,"g")};Blockly.Generator.NAME_TYPE="generated_function";Blockly.Generator.prototype.INFINITE_LOOP_TRAP=null;Blockly.Generator.prototype.STATEMENT_PREFIX=null;Blockly.Generator.prototype.STATEMENT_SUFFIX=null;Blockly.Generator.prototype.INDENT=" ";Blockly.Generator.prototype.COMMENT_WRAP=60;Blockly.Generator.prototype.ORDER_OVERRIDES=[]; +Blockly.Generator.prototype.workspaceToCode=function(a){a||(console.warn("No workspace specified in workspaceToCode call. Guessing."),a=Blockly.getMainWorkspace());var b=[];this.init(a);a=a.getTopBlocks(!0);for(var c=0,d;d=a[c];c++){var e=this.blockToCode(d);Array.isArray(e)&&(e=e[0]);e&&(d.outputConnection&&(e=this.scrubNakedValue(e),this.STATEMENT_PREFIX&&!d.suppressPrefixSuffix&&(e=this.injectId(this.STATEMENT_PREFIX,d)+e),this.STATEMENT_SUFFIX&&!d.suppressPrefixSuffix&&(e+=this.injectId(this.STATEMENT_SUFFIX, +d))),b.push(e))}b=b.join("\n");b=this.finish(b);b=b.replace(/^\s+\n/,"");b=b.replace(/\n\s+$/,"\n");return b=b.replace(/[ \t]+\n/g,"\n")};Blockly.Generator.prototype.prefixLines=function(a,b){return b+a.replace(/(?!\n$)\n/g,"\n"+b)};Blockly.Generator.prototype.allNestedComments=function(a){var b=[];a=a.getDescendants(!0);for(var c=0;ca&&(a=(a=this.getParent())?a.getDepth()+1:0,this.setDepth_(a));return a};Blockly.tree.BaseNode.prototype.setDepth_=function(a){if(a!=this.depth_){this.depth_=a;var b=this.getRowElement();if(b){var c=this.getPixelIndent_()+"px";this.rightToLeft_?b.style.paddingRight=c:b.style.paddingLeft=c}this.forEachChild(function(b){b.setDepth_(a+1)})}};Blockly.tree.BaseNode.prototype.contains=function(a){for(;a;){if(a==this)return!0;a=a.getParent()}return!1}; +Blockly.tree.BaseNode.prototype.getChildren=function(){var a=[];this.forEachChild(function(b){a.push(b)});return a};Blockly.tree.BaseNode.prototype.getParent=function(){return Blockly.tree.BaseNode.superClass_.getParent.call(this)};Blockly.tree.BaseNode.prototype.getPreviousSibling=function(){return this.previousSibling_};Blockly.tree.BaseNode.prototype.getNextSibling=function(){return this.nextSibling_};Blockly.tree.BaseNode.prototype.isLastSibling=function(){return!this.nextSibling_}; +Blockly.tree.BaseNode.prototype.isSelected=function(){return this.selected_};Blockly.tree.BaseNode.prototype.select=function(){var a=this.getTree();a&&a.setSelectedItem(this)};Blockly.tree.BaseNode.prototype.setSelected=function(a){if(this.selected_!=a){this.selected_=a;this.updateRow();var b=this.getElement();b&&(Blockly.utils.aria.setState(b,Blockly.utils.aria.State.SELECTED,a),a&&(a=this.getTree().getElement(),Blockly.utils.aria.setState(a,Blockly.utils.aria.State.ACTIVEDESCENDANT,this.getId())))}}; +Blockly.tree.BaseNode.prototype.setExpanded=function(a){var b=a!=this.expanded_,c;this.expanded_=a;var d=this.getTree(),e=this.getElement();if(this.hasChildren()){if(!a&&d&&this.contains(d.getSelectedItem())&&this.select(),e){if(c=this.getChildrenElement())Blockly.utils.style.setElementShown(c,a),Blockly.utils.aria.setState(e,Blockly.utils.aria.State.EXPANDED,a),a&&this.isInDocument()&&!c.hasChildNodes()&&(this.forEachChild(function(a){c.appendChild(a.toDom())}),this.forEachChild(function(a){a.enterDocument()})); +this.updateExpandIcon()}}else(c=this.getChildrenElement())&&Blockly.utils.style.setElementShown(c,!1);e&&this.updateIcon_();b&&(a?this.doNodeExpanded():this.doNodeCollapsed())};Blockly.tree.BaseNode.prototype.doNodeExpanded=function(){};Blockly.tree.BaseNode.prototype.doNodeCollapsed=function(){};Blockly.tree.BaseNode.prototype.toggle=function(){this.setExpanded(!this.expanded_)}; +Blockly.tree.BaseNode.prototype.toDom=function(){var a=this.expanded_&&this.hasChildren(),b=document.createElement("div");b.style.backgroundPosition=this.getBackgroundPosition();a||(b.style.display="none");a&&this.forEachChild(function(a){b.appendChild(a.toDom())});a=document.createElement("div");a.id=this.getId();a.appendChild(this.getRowDom());a.appendChild(b);return a};Blockly.tree.BaseNode.prototype.getPixelIndent_=function(){return Math.max(0,(this.getDepth()-1)*this.config_.indentWidth)}; +Blockly.tree.BaseNode.prototype.getRowDom=function(){var a=document.createElement("div");a.className=this.getRowClassName();a.style["padding-"+(this.rightToLeft_?"right":"left")]=this.getPixelIndent_()+"px";a.appendChild(this.getIconDom());a.appendChild(this.getLabelDom());return a};Blockly.tree.BaseNode.prototype.getRowClassName=function(){var a="";this.isSelected()&&(a=" "+(this.config_.cssSelectedRow||""));return this.config_.cssTreeRow+a}; +Blockly.tree.BaseNode.prototype.getLabelDom=function(){var a=document.createElement("span");a.className=this.config_.cssItemLabel||"";a.textContent=this.content;return a};Blockly.tree.BaseNode.prototype.getIconDom=function(){var a=document.createElement("span");a.style.display="inline-block";a.className=this.getCalculatedIconClass();return a};Blockly.tree.BaseNode.prototype.getCalculatedIconClass=function(){throw Error(Blockly.Component.Error.ABSTRACT_METHOD);}; +Blockly.tree.BaseNode.prototype.getBackgroundPosition=function(){return(this.isLastSibling()?"-100":(this.getDepth()-1)*this.config_.indentWidth)+"px 0"};Blockly.tree.BaseNode.prototype.getElement=function(){var a=Blockly.tree.BaseNode.superClass_.getElement.call(this);a||(a=document.getElementById(this.getId()),this.setElementInternal(a));return a};Blockly.tree.BaseNode.prototype.getRowElement=function(){var a=this.getElement();return a?a.firstChild:null}; +Blockly.tree.BaseNode.prototype.getIconElement=function(){var a=this.getRowElement();return a?a.firstChild:null};Blockly.tree.BaseNode.prototype.getLabelElement=function(){var a=this.getRowElement();return a&&a.lastChild?a.lastChild.previousSibling:null};Blockly.tree.BaseNode.prototype.getChildrenElement=function(){var a=this.getElement();return a?a.lastChild:null};Blockly.tree.BaseNode.prototype.updateRow=function(){var a=this.getRowElement();a&&(a.className=this.getRowClassName())}; +Blockly.tree.BaseNode.prototype.updateExpandIcon=function(){var a=this.getChildrenElement();a&&(a.style.backgroundPosition=this.getBackgroundPosition())};Blockly.tree.BaseNode.prototype.updateIcon_=function(){this.getIconElement().className=this.getCalculatedIconClass()};Blockly.tree.BaseNode.prototype.onClick_=function(a){a.preventDefault()}; +Blockly.tree.BaseNode.prototype.onKeyDown=function(a){switch(a.keyCode){case Blockly.utils.KeyCodes.RIGHT:var b=this.selectChild();break;case Blockly.utils.KeyCodes.LEFT:b=this.selectParent();break;case Blockly.utils.KeyCodes.DOWN:b=this.selectNext();break;case Blockly.utils.KeyCodes.UP:b=this.selectPrevious();break;case Blockly.utils.KeyCodes.ENTER:case Blockly.utils.KeyCodes.SPACE:this.toggle();b=!0;break;default:b=!1}b&&a.preventDefault();return b}; +Blockly.tree.BaseNode.prototype.selectNext=function(){var a=this.getNextShownNode();a&&a.select();return!0};Blockly.tree.BaseNode.prototype.selectPrevious=function(){var a=this.getPreviousShownNode();a&&a.select();return!0};Blockly.tree.BaseNode.prototype.selectParent=function(){if(this.hasChildren()&&this.expanded_)this.setExpanded(!1);else{var a=this.getParent(),b=this.getTree();a&&a!=b&&a.select()}return!0}; +Blockly.tree.BaseNode.prototype.selectChild=function(){return this.hasChildren()?(this.expanded_?this.getChildAt(0).select():this.setExpanded(!0),!0):!1};Blockly.tree.BaseNode.prototype.getLastShownDescendant=function(){return this.expanded_&&this.hasChildren()?this.getChildAt(this.getChildCount()-1).getLastShownDescendant():this}; +Blockly.tree.BaseNode.prototype.getNextShownNode=function(){if(this.hasChildren()&&this.expanded_)return this.getChildAt(0);for(var a=this,b;a!=this.getTree();){b=a.getNextSibling();if(null!=b)return b;a=a.getParent()}return null};Blockly.tree.BaseNode.prototype.getPreviousShownNode=function(){var a=this.getPreviousSibling();if(null!=a)return a.getLastShownDescendant();a=this.getParent();var b=this.getTree();return a==b||this==b?null:a}; +Blockly.tree.BaseNode.prototype.setTreeInternal=function(a){this.tree!=a&&(this.tree=a,this.forEachChild(function(b){b.setTreeInternal(a)}))};Blockly.tree.TreeNode=function(a,b,c){this.toolbox_=a;Blockly.tree.BaseNode.call(this,b,c);this.onSizeChanged_=null};Blockly.utils.object.inherits(Blockly.tree.TreeNode,Blockly.tree.BaseNode);Blockly.tree.TreeNode.prototype.getTree=function(){if(this.tree)return this.tree;var a=this.getParent();return a&&(a=a.getTree())?(this.setTreeInternal(a),a):null}; +Blockly.tree.TreeNode.prototype.getCalculatedIconClass=function(){var a=this.expanded_;if(a&&this.expandedIconClass)return this.expandedIconClass;var b=this.iconClass;if(!a&&b)return b;b=this.config_;if(this.hasChildren()){if(a&&b.cssExpandedFolderIcon)return b.cssTreeIcon+" "+b.cssExpandedFolderIcon;if(!a&&b.cssCollapsedFolderIcon)return b.cssTreeIcon+" "+b.cssCollapsedFolderIcon}else if(b.cssFileIcon)return b.cssTreeIcon+" "+b.cssFileIcon;return""}; +Blockly.tree.TreeNode.prototype.onClick_=function(a){this.hasChildren()?(this.toggle(),this.select()):this.isSelected()?this.getTree().setSelectedItem(null):this.select();this.updateRow()}; +Blockly.tree.TreeNode.prototype.onKeyDown=function(a){if(this.tree.toolbox_.horizontalLayout_){var b={},c=Blockly.utils.KeyCodes.DOWN,d=Blockly.utils.KeyCodes.UP;b[Blockly.utils.KeyCodes.RIGHT]=this.rightToLeft_?d:c;b[Blockly.utils.KeyCodes.LEFT]=this.rightToLeft_?c:d;b[Blockly.utils.KeyCodes.UP]=Blockly.utils.KeyCodes.LEFT;b[Blockly.utils.KeyCodes.DOWN]=Blockly.utils.KeyCodes.RIGHT;Object.defineProperties(a,{keyCode:{value:b[a.keyCode]||a.keyCode}})}return Blockly.tree.TreeNode.superClass_.onKeyDown.call(this, +a)};Blockly.tree.TreeNode.prototype.onSizeChanged=function(a){this.onSizeChanged_=a};Blockly.tree.TreeNode.prototype.resizeToolbox_=function(){this.onSizeChanged_&&this.onSizeChanged_.call(this.toolbox_)};Blockly.tree.TreeNode.prototype.doNodeExpanded=Blockly.tree.TreeNode.prototype.resizeToolbox_;Blockly.tree.TreeNode.prototype.doNodeCollapsed=Blockly.tree.TreeNode.prototype.resizeToolbox_;Blockly.tree.TreeControl=function(a,b){this.toolbox_=a;this.onKeydownWrapper_=this.onClickWrapper_=null;Blockly.tree.BaseNode.call(this,"",b);this.selected_=this.expanded_=!0;this.selectedItem_=this;this.onAfterSelected_=this.onBeforeSelected_=null};Blockly.utils.object.inherits(Blockly.tree.TreeControl,Blockly.tree.BaseNode);Blockly.tree.TreeControl.prototype.getTree=function(){return this};Blockly.tree.TreeControl.prototype.getToolbox=function(){return this.toolbox_}; +Blockly.tree.TreeControl.prototype.getDepth=function(){return 0};Blockly.tree.TreeControl.prototype.setExpanded=function(a){this.expanded_=a};Blockly.tree.TreeControl.prototype.getIconElement=function(){var a=this.getRowElement();return a?a.firstChild:null};Blockly.tree.TreeControl.prototype.updateExpandIcon=function(){};Blockly.tree.TreeControl.prototype.getRowClassName=function(){return Blockly.tree.TreeControl.superClass_.getRowClassName.call(this)+" "+this.config_.cssHideRoot}; +Blockly.tree.TreeControl.prototype.getCalculatedIconClass=function(){var a=this.expanded_;if(a&&this.expandedIconClass)return this.expandedIconClass;var b=this.iconClass;return!a&&b?b:""}; +Blockly.tree.TreeControl.prototype.setSelectedItem=function(a){if(a!=this.selectedItem_&&(!this.onBeforeSelected_||this.onBeforeSelected_.call(this.toolbox_,a))){var b=this.getSelectedItem();this.selectedItem_&&this.selectedItem_.setSelected(!1);(this.selectedItem_=a)&&a.setSelected(!0);this.onAfterSelected_&&this.onAfterSelected_.call(this.toolbox_,b,a)}};Blockly.tree.TreeControl.prototype.onBeforeSelected=function(a){this.onBeforeSelected_=a}; +Blockly.tree.TreeControl.prototype.onAfterSelected=function(a){this.onAfterSelected_=a};Blockly.tree.TreeControl.prototype.getSelectedItem=function(){return this.selectedItem_};Blockly.tree.TreeControl.prototype.initAccessibility=function(){Blockly.tree.TreeControl.superClass_.initAccessibility.call(this);var a=this.getElement();Blockly.utils.aria.setRole(a,Blockly.utils.aria.Role.TREE);Blockly.utils.aria.setState(a,Blockly.utils.aria.State.LABELLEDBY,this.getLabelElement().id)}; +Blockly.tree.TreeControl.prototype.enterDocument=function(){Blockly.tree.TreeControl.superClass_.enterDocument.call(this);var a=this.getElement();a.className=this.config_.cssRoot;a.setAttribute("hideFocus","true");this.attachEvents_();this.initAccessibility()};Blockly.tree.TreeControl.prototype.exitDocument=function(){Blockly.tree.TreeControl.superClass_.exitDocument.call(this);this.detachEvents_()}; +Blockly.tree.TreeControl.prototype.attachEvents_=function(){var a=this.getElement();a.tabIndex=0;this.onClickWrapper_=Blockly.bindEventWithChecks_(a,"click",this,this.handleMouseEvent_);this.onKeydownWrapper_=Blockly.bindEvent_(a,"keydown",this,this.handleKeyEvent_)}; +Blockly.tree.TreeControl.prototype.detachEvents_=function(){this.onClickWrapper_&&(Blockly.unbindEvent_(this.onClickWrapper_),this.onClickWrapper_=null);this.onKeydownWrapper_&&(Blockly.unbindEvent_(this.onKeydownWrapper_),this.onKeydownWrapper_=null)};Blockly.tree.TreeControl.prototype.handleMouseEvent_=function(a){var b=this.getNodeFromEvent_(a);if(b&&"click"==a.type)b.onClick_(a)}; +Blockly.tree.TreeControl.prototype.handleKeyEvent_=function(a){var b=!(!this.selectedItem_||!this.selectedItem_.onKeyDown(a));b&&(Blockly.utils.style.scrollIntoContainerView(this.selectedItem_.getElement(),this.getElement().parentNode),a.preventDefault());return b};Blockly.tree.TreeControl.prototype.getNodeFromEvent_=function(a){for(var b=a.target;b;){if(a=Blockly.tree.BaseNode.allNodes[b.id])return a;if(b==this.getElement())break;if(b.getAttribute("role")==Blockly.utils.aria.Role.GROUP)break;b=b.parentNode}return null}; +Blockly.tree.TreeControl.prototype.createNode=function(a){return new Blockly.tree.TreeNode(this.toolbox_,a||"",this.config_)};Blockly.Toolbox=function(a){this.workspace_=a;this.RTL=a.options.RTL;this.horizontalLayout_=a.options.horizontalLayout;this.toolboxPosition=a.options.toolboxPosition;this.config_={indentWidth:19,cssRoot:"blocklyTreeRoot",cssHideRoot:"blocklyHidden",cssTreeRow:"blocklyTreeRow",cssItemLabel:"blocklyTreeLabel",cssTreeIcon:"blocklyTreeIcon",cssExpandedFolderIcon:"blocklyTreeIconOpen",cssFileIcon:"blocklyTreeIconNone",cssSelectedRow:"blocklyTreeSelected"};this.treeSeparatorConfig_={cssTreeRow:"blocklyTreeSeparator"}; +this.horizontalLayout_&&(this.config_.cssTreeRow+=a.RTL?" blocklyHorizontalTreeRtl":" blocklyHorizontalTree",this.treeSeparatorConfig_.cssTreeRow="blocklyTreeSeparatorHorizontal "+(a.RTL?"blocklyHorizontalTreeRtl":"blocklyHorizontalTree"),this.config_.cssTreeIcon="");this.flyout_=null;this.height=this.width=0;this.lastCategory_=null}; +Blockly.Toolbox.prototype.init=function(){var a=this.workspace_,b=this.workspace_.getParentSvg();this.HtmlDiv=document.createElement("div");this.HtmlDiv.className="blocklyToolboxDiv blocklyNonSelectable";this.HtmlDiv.setAttribute("dir",a.RTL?"RTL":"LTR");b.parentNode.insertBefore(this.HtmlDiv,b);var c=a.getThemeManager();c.subscribe(this.HtmlDiv,"toolboxBackgroundColour","background-color");c.subscribe(this.HtmlDiv,"toolboxForegroundColour","color");Blockly.bindEventWithChecks_(this.HtmlDiv,"mousedown", +this,function(a){Blockly.utils.isRightButton(a)||a.target==this.HtmlDiv?Blockly.hideChaff(!1):Blockly.hideChaff(!0);Blockly.Touch.clearTouchIdentifier()},!1,!0);c=new Blockly.Options({parentWorkspace:a,rtl:a.RTL,oneBasedIndex:a.options.oneBasedIndex,horizontalLayout:a.horizontalLayout,renderer:a.options.renderer,rendererOverrides:a.options.rendererOverrides});c.toolboxPosition=a.options.toolboxPosition;if(a.horizontalLayout){if(!Blockly.HorizontalFlyout)throw Error("Missing require for Blockly.HorizontalFlyout"); +this.flyout_=new Blockly.HorizontalFlyout(c)}else{if(!Blockly.VerticalFlyout)throw Error("Missing require for Blockly.VerticalFlyout");this.flyout_=new Blockly.VerticalFlyout(c)}if(!this.flyout_)throw Error("One of Blockly.VerticalFlyout or Blockly.Horizontal must berequired.");Blockly.utils.dom.insertAfter(this.flyout_.createDom("svg"),b);this.flyout_.init(a);this.config_.cssCollapsedFolderIcon="blocklyTreeIconClosed"+(a.RTL?"Rtl":"Ltr");this.render(a.options.languageTree)}; +Blockly.Toolbox.prototype.render=function(a){this.tree_&&(this.tree_.dispose(),this.lastCategory_=null);var b=new Blockly.tree.TreeControl(this,this.config_);this.tree_=b;b.setSelectedItem(null);b.onBeforeSelected(this.handleBeforeTreeSelected_);b.onAfterSelected(this.handleAfterTreeSelected_);var c=null;if(a){this.tree_.contents=[];this.hasColours_=!1;c=this.createTree_(a,this.tree_);if(this.tree_.contents.length)throw Error("Toolbox cannot have both blocks and categories in the root level.");this.workspace_.resizeContents()}b.render(this.HtmlDiv); +c&&b.setSelectedItem(c);this.addColour_();this.position();this.horizontalLayout_&&Blockly.utils.aria.setState(this.tree_.getElement(),Blockly.utils.aria.State.ORIENTATION,"horizontal")}; +Blockly.Toolbox.prototype.createTree_=function(a,b){var c=null,d=null;if(!a)return null;for(var e=0,f;f=a[e];e++)switch(f.kind.toUpperCase()){case "CATEGORY":c=this.addCategory_(f,b)||c;d=f;break;case "SEP":d=this.addSeparator_(f,b,d)||d;break;case "BLOCK":case "SHADOW":case "LABEL":case "BUTTON":b.contents.push(f),d=f}return c}; +Blockly.Toolbox.prototype.addCategory_=function(a,b){var c=null,d=Blockly.utils.replaceMessageReferences(a.name),e=this.tree_.createNode(d);e.onSizeChanged(this.handleNodeSizeChanged_);e.contents=[];b.add(e);(b=a.custom)?e.contents=b:c=this.createTree_(a.contents,e)||c;this.setColourOrStyle_(a,e,d);return c=this.setExpanded_(a,e)||c}; +Blockly.Toolbox.prototype.setColourOrStyle_=function(a,b,c){var d=a.categorystyle;(a=a.colour)&&d?(b.hexColour="",console.warn('Toolbox category "'+c+'" must not have both a style and a colour')):d?this.setColourFromStyle_(d,b,c):this.setColour_(a,b,c)};Blockly.Toolbox.prototype.addSeparator_=function(a,b,c){if(c&&"CATEGORY"==c.kind.toUpperCase())b.add(new Blockly.Toolbox.TreeSeparator(this.treeSeparatorConfig_));else return b.contents.push(a),a;return null}; +Blockly.Toolbox.prototype.setExpanded_=function(a,b){var c=null;"true"==a.expanded?(b.contents.length&&(c=b),b.setExpanded(!0)):b.setExpanded(!1);return c};Blockly.Toolbox.prototype.handleBeforeTreeSelected_=function(a){if(a==this.tree_)return!1;this.lastCategory_&&(this.lastCategory_.getRowElement().style.backgroundColor="");if(a){var b=a.hexColour||"#57e";a.getRowElement().style.backgroundColor=b;this.addColour_(a)}return!0}; +Blockly.Toolbox.prototype.handleAfterTreeSelected_=function(a,b){b&&b.contents&&b.contents.length?(this.flyout_.show(b.contents),this.lastCategory_!=b&&this.flyout_.scrollToStart(),this.workspace_.keyboardAccessibilityMode&&Blockly.navigation.setState(Blockly.navigation.STATE_TOOLBOX)):(this.flyout_.hide(),!this.workspace_.keyboardAccessibilityMode||b instanceof Blockly.Toolbox.TreeSeparator||Blockly.navigation.setState(Blockly.navigation.STATE_WS));a!=b&&a!=this&&(a=new Blockly.Events.Ui(null,"category", +a&&a.content,b&&b.content),a.workspaceId=this.workspace_.id,Blockly.Events.fire(a));b&&(this.lastCategory_=b)};Blockly.Toolbox.prototype.handleNodeSizeChanged_=function(){Blockly.svgResize(this.workspace_)}; +Blockly.Toolbox.prototype.onBlocklyAction=function(a){var b=this.tree_.getSelectedItem();if(!b)return!1;switch(a.name){case Blockly.navigation.actionNames.PREVIOUS:return b.selectPrevious();case Blockly.navigation.actionNames.OUT:return b.selectParent();case Blockly.navigation.actionNames.NEXT:return b.selectNext();case Blockly.navigation.actionNames.IN:return b.selectChild();default:return!1}}; +Blockly.Toolbox.prototype.dispose=function(){this.flyout_.dispose();this.tree_.dispose();this.workspace_.getThemeManager().unsubscribe(this.HtmlDiv);Blockly.utils.dom.removeNode(this.HtmlDiv);this.lastCategory_=null};Blockly.Toolbox.prototype.setVisible=function(a){this.HtmlDiv.style.display=a?"block":"none"};Blockly.Toolbox.prototype.getWidth=function(){return this.width};Blockly.Toolbox.prototype.getHeight=function(){return this.height};Blockly.Toolbox.prototype.getFlyout=function(){return this.flyout_}; +Blockly.Toolbox.prototype.position=function(){var a=this.HtmlDiv;if(a){var b=Blockly.svgSize(this.workspace_.getParentSvg());this.horizontalLayout_?(a.style.left="0",a.style.height="auto",a.style.width=b.width+"px",this.height=a.offsetHeight,this.toolboxPosition==Blockly.TOOLBOX_AT_TOP?a.style.top="0":a.style.bottom="0"):(this.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT?a.style.right="0":a.style.left="0",a.style.height=b.height+"px",this.width=a.offsetWidth);this.flyout_.position()}}; +Blockly.Toolbox.prototype.setColour_=function(a,b,c){a=Blockly.utils.replaceMessageReferences(a);if(null===a||""===a)b.hexColour="";else{var d=Number(a);isNaN(d)?(d=Blockly.utils.colour.parse(a))?(b.hexColour=d,this.hasColours_=!0):(b.hexColour="",console.warn('Toolbox category "'+c+'" has unrecognized colour attribute: '+a)):(b.hexColour=Blockly.hueToHex(d),this.hasColours_=!0)}}; +Blockly.Toolbox.prototype.setColourFromStyle_=function(a,b,c){b.styleName=a;var d=this.workspace_.getTheme();a&&d&&((d=d.categoryStyles[a])&&d.colour?this.setColour_(d.colour,b,c):console.warn('Style "'+a+'" must exist and contain a colour value'))};Blockly.Toolbox.prototype.updateColourFromTheme_=function(a){if(a=a||this.tree_){a=a.getChildren(!1);for(var b=0,c;c=a[b];b++)c.styleName&&(this.setColourFromStyle_(c.styleName,c,""),this.addColour_()),this.updateColourFromTheme_(c)}}; +Blockly.Toolbox.prototype.refreshTheme=function(){var a=this.tree_;a&&(this.updateColourFromTheme_(a),this.updateSelectedItemColour_(a))};Blockly.Toolbox.prototype.updateSelectedItemColour_=function(a){if(a=a.getSelectedItem()){var b=a.hexColour||"#57e";a.getRowElement().style.backgroundColor=b;this.addColour_(a)}}; +Blockly.Toolbox.prototype.addColour_=function(a){a=(a||this.tree_).getChildren(!1);for(var b=0,c;c=a[b];b++){var d=c.getRowElement();if(d){var e=this.hasColours_?"8px solid "+(c.hexColour||"#ddd"):"none";this.workspace_.RTL?d.style.borderRight=e:d.style.borderLeft=e}this.addColour_(c)}};Blockly.Toolbox.prototype.clearSelection=function(){this.tree_.setSelectedItem(null)};Blockly.Toolbox.prototype.addStyle=function(a){Blockly.utils.dom.addClass(this.HtmlDiv,a)}; +Blockly.Toolbox.prototype.removeStyle=function(a){Blockly.utils.dom.removeClass(this.HtmlDiv,a)}; +Blockly.Toolbox.prototype.getClientRect=function(){if(!this.HtmlDiv)return null;var a=this.HtmlDiv.getBoundingClientRect(),b=a.top,c=b+a.height,d=a.left;a=d+a.width;return this.toolboxPosition==Blockly.TOOLBOX_AT_TOP?new Blockly.utils.Rect(-1E7,c,-1E7,1E7):this.toolboxPosition==Blockly.TOOLBOX_AT_BOTTOM?new Blockly.utils.Rect(b,1E7,-1E7,1E7):this.toolboxPosition==Blockly.TOOLBOX_AT_LEFT?new Blockly.utils.Rect(-1E7,1E7,-1E7,a):new Blockly.utils.Rect(-1E7,1E7,d,1E7)}; +Blockly.Toolbox.prototype.refreshSelection=function(){var a=this.tree_.getSelectedItem();a&&a.contents&&this.flyout_.show(a.contents)};Blockly.Toolbox.prototype.selectFirstCategory=function(){this.tree_.getSelectedItem()||this.tree_.selectChild()};Blockly.Toolbox.TreeSeparator=function(a){Blockly.tree.TreeNode.call(this,null,"",a)};Blockly.utils.object.inherits(Blockly.Toolbox.TreeSeparator,Blockly.tree.TreeNode); +Blockly.Css.register([".blocklyToolboxDelete {",'cursor: url("<<>>/handdelete.cur"), auto;',"}",".blocklyToolboxGrab {",'cursor: url("<<>>/handclosed.cur"), auto;',"cursor: grabbing;","cursor: -webkit-grabbing;","}",".blocklyToolboxDiv {","background-color: #ddd;","overflow-x: visible;","overflow-y: auto;","position: absolute;","z-index: 70;","-webkit-tap-highlight-color: transparent;","}",".blocklyTreeRoot {","padding: 4px 0;","}",".blocklyTreeRoot:focus {","outline: none;","}",".blocklyTreeRow {", +"height: 22px;","line-height: 22px;","margin-bottom: 3px;","padding-right: 8px;","white-space: nowrap;","}",".blocklyHorizontalTree {","float: left;","margin: 1px 5px 8px 0;","}",".blocklyHorizontalTreeRtl {","float: right;","margin: 1px 0 8px 5px;","}",'.blocklyToolboxDiv[dir="RTL"] .blocklyTreeRow {',"margin-left: 8px;","}",".blocklyTreeRow:not(.blocklyTreeSelected):hover {","background-color: rgba(255, 255, 255, 0.2);","}",".blocklyTreeSeparator {","border-bottom: solid #e5e5e5 1px;","height: 0;", +"margin: 5px 0;","}",".blocklyTreeSeparatorHorizontal {","border-right: solid #e5e5e5 1px;","width: 0;","padding: 5px 0;","margin: 0 5px;","}",".blocklyTreeIcon {","background-image: url(<<>>/sprites.png);","height: 16px;","vertical-align: middle;","width: 16px;","}",".blocklyTreeIconClosedLtr {","background-position: -32px -1px;","}",".blocklyTreeIconClosedRtl {","background-position: 0 -1px;","}",".blocklyTreeIconOpen {","background-position: -16px -1px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedLtr {", +"background-position: -32px -17px;","}",".blocklyTreeSelected>.blocklyTreeIconClosedRtl {","background-position: 0 -17px;","}",".blocklyTreeSelected>.blocklyTreeIconOpen {","background-position: -16px -17px;","}",".blocklyTreeIconNone,",".blocklyTreeSelected>.blocklyTreeIconNone {","background-position: -48px -1px;","}",".blocklyTreeLabel {","cursor: default;","font: 16px sans-serif;","padding: 0 3px;","vertical-align: middle;","}",".blocklyToolboxDelete .blocklyTreeLabel {",'cursor: url("<<>>/handdelete.cur"), auto;', +"}",".blocklyTreeSelected .blocklyTreeLabel {","color: #fff;","}"]);Blockly.registry.register(Blockly.registry.Type.TOOLBOX,Blockly.registry.DEFAULT,Blockly.Toolbox);Blockly.Trashcan=function(a){this.workspace_=a;this.contents_=[];this.flyout=null;if(!(0>=this.workspace_.options.maxTrashcanContents)){a=new Blockly.Options({scrollbars:!0,parentWorkspace:this.workspace_,rtl:this.workspace_.RTL,oneBasedIndex:this.workspace_.options.oneBasedIndex,renderer:this.workspace_.options.renderer,rendererOverrides:this.workspace_.options.rendererOverrides});if(this.workspace_.horizontalLayout){a.toolboxPosition=this.workspace_.toolboxPosition==Blockly.TOOLBOX_AT_TOP?Blockly.TOOLBOX_AT_BOTTOM: +Blockly.TOOLBOX_AT_TOP;if(!Blockly.HorizontalFlyout)throw Error("Missing require for Blockly.HorizontalFlyout");this.flyout=new Blockly.HorizontalFlyout(a)}else{a.toolboxPosition=this.workspace_.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT?Blockly.TOOLBOX_AT_LEFT:Blockly.TOOLBOX_AT_RIGHT;if(!Blockly.VerticalFlyout)throw Error("Missing require for Blockly.VerticalFlyout");this.flyout=new Blockly.VerticalFlyout(a)}this.workspace_.addChangeListener(this.onDelete_.bind(this))}}; +Blockly.Trashcan.prototype.WIDTH_=47;Blockly.Trashcan.prototype.BODY_HEIGHT_=44;Blockly.Trashcan.prototype.LID_HEIGHT_=16;Blockly.Trashcan.prototype.MARGIN_BOTTOM_=20;Blockly.Trashcan.prototype.MARGIN_SIDE_=20;Blockly.Trashcan.prototype.MARGIN_HOTSPOT_=10;Blockly.Trashcan.prototype.SPRITE_LEFT_=0;Blockly.Trashcan.prototype.SPRITE_TOP_=32;Blockly.Trashcan.prototype.HAS_BLOCKS_LID_ANGLE_=.1;Blockly.Trashcan.ANIMATION_LENGTH_=80;Blockly.Trashcan.ANIMATION_FRAMES_=4;Blockly.Trashcan.OPACITY_MIN_=.4; +Blockly.Trashcan.OPACITY_MAX_=.8;Blockly.Trashcan.MAX_LID_ANGLE_=45;Blockly.Trashcan.prototype.isOpen=!1;Blockly.Trashcan.prototype.minOpenness_=0;Blockly.Trashcan.prototype.svgGroup_=null;Blockly.Trashcan.prototype.svgLid_=null;Blockly.Trashcan.prototype.lidTask_=0;Blockly.Trashcan.prototype.lidOpen_=0;Blockly.Trashcan.prototype.left_=0;Blockly.Trashcan.prototype.top_=0; +Blockly.Trashcan.prototype.createDom=function(){this.svgGroup_=Blockly.utils.dom.createSvgElement("g",{"class":"blocklyTrash"},null);var a=String(Math.random()).substring(2);var b=Blockly.utils.dom.createSvgElement("clipPath",{id:"blocklyTrashBodyClipPath"+a},this.svgGroup_);Blockly.utils.dom.createSvgElement("rect",{width:this.WIDTH_,height:this.BODY_HEIGHT_,y:this.LID_HEIGHT_},b);var c=Blockly.utils.dom.createSvgElement("image",{width:Blockly.SPRITE.width,x:-this.SPRITE_LEFT_,height:Blockly.SPRITE.height, +y:-this.SPRITE_TOP_,"clip-path":"url(#blocklyTrashBodyClipPath"+a+")"},this.svgGroup_);c.setAttributeNS(Blockly.utils.dom.XLINK_NS,"xlink:href",this.workspace_.options.pathToMedia+Blockly.SPRITE.url);b=Blockly.utils.dom.createSvgElement("clipPath",{id:"blocklyTrashLidClipPath"+a},this.svgGroup_);Blockly.utils.dom.createSvgElement("rect",{width:this.WIDTH_,height:this.LID_HEIGHT_},b);this.svgLid_=Blockly.utils.dom.createSvgElement("image",{width:Blockly.SPRITE.width,x:-this.SPRITE_LEFT_,height:Blockly.SPRITE.height, +y:-this.SPRITE_TOP_,"clip-path":"url(#blocklyTrashLidClipPath"+a+")"},this.svgGroup_);this.svgLid_.setAttributeNS(Blockly.utils.dom.XLINK_NS,"xlink:href",this.workspace_.options.pathToMedia+Blockly.SPRITE.url);Blockly.bindEventWithChecks_(this.svgGroup_,"mouseup",this,this.click);Blockly.bindEvent_(c,"mouseover",this,this.mouseOver_);Blockly.bindEvent_(c,"mouseout",this,this.mouseOut_);this.animateLid_();return this.svgGroup_}; +Blockly.Trashcan.prototype.init=function(a){0this.minOpenness_&&1>this.lidOpen_&&(this.lidTask_=setTimeout(this.animateLid_.bind(this),Blockly.Trashcan.ANIMATION_LENGTH_/ +a))};Blockly.Trashcan.prototype.setLidAngle_=function(a){var b=this.workspace_.toolboxPosition==Blockly.TOOLBOX_AT_RIGHT||this.workspace_.horizontalLayout&&this.workspace_.RTL;this.svgLid_.setAttribute("transform","rotate("+(b?-a:a)+","+(b?4:this.WIDTH_-4)+","+(this.LID_HEIGHT_-2)+")")};Blockly.Trashcan.prototype.setMinOpenness_=function(a){this.minOpenness_=a;this.isOpen||this.setLidAngle_(a*Blockly.Trashcan.MAX_LID_ANGLE_)};Blockly.Trashcan.prototype.close=function(){this.setOpen(!1)}; +Blockly.Trashcan.prototype.click=function(){if(this.contents_.length){for(var a=[],b=0,c;c=this.contents_[b];b++)a[b]=Blockly.Xml.textToDom(c);this.flyout.show(a)}};Blockly.Trashcan.prototype.mouseOver_=function(){this.contents_.length&&this.setOpen(!0)};Blockly.Trashcan.prototype.mouseOut_=function(){this.setOpen(!1)}; +Blockly.Trashcan.prototype.onDelete_=function(a){if(!(0>=this.workspace_.options.maxTrashcanContents)&&a.type==Blockly.Events.BLOCK_DELETE&&"shadow"!=a.oldXml.tagName.toLowerCase()&&(a=this.cleanBlockXML_(a.oldXml),-1==this.contents_.indexOf(a))){for(this.contents_.unshift(a);this.contents_.length>this.workspace_.options.maxTrashcanContents;)this.contents_.pop();this.setMinOpenness_(this.HAS_BLOCKS_LID_ANGLE_)}}; +Blockly.Trashcan.prototype.cleanBlockXML_=function(a){for(var b=a=a.cloneNode(!0);b;){b.removeAttribute&&(b.removeAttribute("x"),b.removeAttribute("y"),b.removeAttribute("id"),b.removeAttribute("disabled"),"comment"==b.nodeName&&(b.removeAttribute("h"),b.removeAttribute("w"),b.removeAttribute("pinned")));var c=b.firstChild||b.nextSibling;if(!c)for(c=b.parentNode;c;){if(c.nextSibling){c=c.nextSibling;break}c=c.parentNode}b=c}return Blockly.Xml.domToText(a)};Blockly.VariablesDynamic={};Blockly.VariablesDynamic.onCreateVariableButtonClick_String=function(a){Blockly.Variables.createVariableButtonHandler(a.getTargetWorkspace(),void 0,"String")};Blockly.VariablesDynamic.onCreateVariableButtonClick_Number=function(a){Blockly.Variables.createVariableButtonHandler(a.getTargetWorkspace(),void 0,"Number")};Blockly.VariablesDynamic.onCreateVariableButtonClick_Colour=function(a){Blockly.Variables.createVariableButtonHandler(a.getTargetWorkspace(),void 0,"Colour")}; +Blockly.VariablesDynamic.flyoutCategory=function(a){var b=[],c=document.createElement("button");c.setAttribute("text",Blockly.Msg.NEW_STRING_VARIABLE);c.setAttribute("callbackKey","CREATE_VARIABLE_STRING");b.push(c);c=document.createElement("button");c.setAttribute("text",Blockly.Msg.NEW_NUMBER_VARIABLE);c.setAttribute("callbackKey","CREATE_VARIABLE_NUMBER");b.push(c);c=document.createElement("button");c.setAttribute("text",Blockly.Msg.NEW_COLOUR_VARIABLE);c.setAttribute("callbackKey","CREATE_VARIABLE_COLOUR"); +b.push(c);a.registerButtonCallback("CREATE_VARIABLE_STRING",Blockly.VariablesDynamic.onCreateVariableButtonClick_String);a.registerButtonCallback("CREATE_VARIABLE_NUMBER",Blockly.VariablesDynamic.onCreateVariableButtonClick_Number);a.registerButtonCallback("CREATE_VARIABLE_COLOUR",Blockly.VariablesDynamic.onCreateVariableButtonClick_Colour);a=Blockly.VariablesDynamic.flyoutCategoryBlocks(a);return b=b.concat(a)}; +Blockly.VariablesDynamic.flyoutCategoryBlocks=function(a){a=a.getAllVariables();var b=[];if(0image, .blocklyZoom>svg>image {","opacity: .4;","}",".blocklyZoom>image:hover, .blocklyZoom>svg>image:hover {","opacity: .6;","}",".blocklyZoom>image:active, .blocklyZoom>svg>image:active {","opacity: .8;","}"]);Blockly.Mutator=function(a){Blockly.Mutator.superClass_.constructor.call(this,null);this.quarkNames_=a};Blockly.utils.object.inherits(Blockly.Mutator,Blockly.Icon);Blockly.Mutator.prototype.workspaceWidth_=0;Blockly.Mutator.prototype.workspaceHeight_=0;Blockly.Mutator.prototype.setBlock=function(a){this.block_=a};Blockly.Mutator.prototype.getWorkspace=function(){return this.workspace_}; +Blockly.Mutator.prototype.drawIcon_=function(a){Blockly.utils.dom.createSvgElement("rect",{"class":"blocklyIconShape",rx:"4",ry:"4",height:"16",width:"16"},a);Blockly.utils.dom.createSvgElement("path",{"class":"blocklyIconSymbol",d:"m4.203,7.296 0,1.368 -0.92,0.677 -0.11,0.41 0.9,1.559 0.41,0.11 1.043,-0.457 1.187,0.683 0.127,1.134 0.3,0.3 1.8,0 0.3,-0.299 0.127,-1.138 1.185,-0.682 1.046,0.458 0.409,-0.11 0.9,-1.559 -0.11,-0.41 -0.92,-0.677 0,-1.366 0.92,-0.677 0.11,-0.41 -0.9,-1.559 -0.409,-0.109 -1.046,0.458 -1.185,-0.682 -0.127,-1.138 -0.3,-0.299 -1.8,0 -0.3,0.3 -0.126,1.135 -1.187,0.682 -1.043,-0.457 -0.41,0.11 -0.899,1.559 0.108,0.409z"}, +a);Blockly.utils.dom.createSvgElement("circle",{"class":"blocklyIconShape",r:"2.7",cx:"8",cy:"8"},a)};Blockly.Mutator.prototype.iconClick_=function(a){this.block_.isEditable()&&Blockly.Icon.prototype.iconClick_.call(this,a)}; +Blockly.Mutator.prototype.createEditor_=function(){this.svgDialog_=Blockly.utils.dom.createSvgElement("svg",{x:Blockly.Bubble.BORDER_WIDTH,y:Blockly.Bubble.BORDER_WIDTH},null);if(this.quarkNames_.length)for(var a=Blockly.utils.xml.createElement("xml"),b=0,c;c=this.quarkNames_[b];b++){var d=Blockly.utils.xml.createElement("block");d.setAttribute("type",c);a.appendChild(d)}else a=null;b=new Blockly.Options({disable:!1,parentWorkspace:this.block_.workspace,media:this.block_.workspace.options.pathToMedia, +rtl:this.block_.RTL,horizontalLayout:!1,renderer:this.block_.workspace.options.renderer,rendererOverrides:this.block_.workspace.options.rendererOverrides});b.toolboxPosition=this.block_.RTL?Blockly.TOOLBOX_AT_RIGHT:Blockly.TOOLBOX_AT_LEFT;if(c=!!a)b.languageTree=Blockly.utils.toolbox.convertToolboxToJSON(a),b.getMetrics=this.getFlyoutMetrics_.bind(this);this.workspace_=new Blockly.WorkspaceSvg(b);this.workspace_.isMutator=!0;this.workspace_.addChangeListener(Blockly.Events.disableOrphans);a=c?this.workspace_.addFlyout("g"): +null;b=this.workspace_.createDom("blocklyMutatorBackground");a&&b.insertBefore(a,this.workspace_.svgBlockCanvas_);this.svgDialog_.appendChild(b);return this.svgDialog_};Blockly.Mutator.prototype.updateEditable=function(){Blockly.Mutator.superClass_.updateEditable.call(this);this.block_.isInFlyout||(this.block_.isEditable()?this.iconGroup_&&Blockly.utils.dom.removeClass(this.iconGroup_,"blocklyIconGroupReadonly"):(this.setVisible(!1),this.iconGroup_&&Blockly.utils.dom.addClass(this.iconGroup_,"blocklyIconGroupReadonly")))}; +Blockly.Mutator.prototype.resizeBubble_=function(){var a=2*Blockly.Bubble.BORDER_WIDTH,b=this.workspace_.getCanvas().getBBox();var c=this.block_.RTL?-b.x:b.width+b.x;b=b.height+3*a;var d=this.workspace_.getFlyout();d&&(d=d.getMetrics_(),b=Math.max(b,d.contentHeight+20));c+=3*a;if(Math.abs(this.workspaceWidth_-c)>a||Math.abs(this.workspaceHeight_-b)>a)this.workspaceWidth_=c,this.workspaceHeight_=b,this.bubble_.setBubbleSize(c+a,b+a),this.svgDialog_.setAttribute("width",this.workspaceWidth_),this.svgDialog_.setAttribute("height", +this.workspaceHeight_);this.block_.RTL&&(a="translate("+this.workspaceWidth_+",0)",this.workspace_.getCanvas().setAttribute("transform",a));this.workspace_.resize()};Blockly.Mutator.prototype.onBubbleMove_=function(){this.workspace_&&this.workspace_.recordDeleteAreas()}; +Blockly.Mutator.prototype.setVisible=function(a){if(a!=this.isVisible())if(Blockly.Events.fire(new Blockly.Events.Ui(this.block_,"mutatorOpen",!a,a)),a){this.bubble_=new Blockly.Bubble(this.block_.workspace,this.createEditor_(),this.block_.pathObject.svgPath,this.iconXY_,null,null);this.bubble_.setSvgId(this.block_.id);this.bubble_.registerMoveEvent(this.onBubbleMove_.bind(this));var b=this.workspace_.options.languageTree;a=this.workspace_.getFlyout();b&&(a.init(this.workspace_),a.show(b));this.rootBlock_= +this.block_.decompose(this.workspace_);b=this.rootBlock_.getDescendants(!1);for(var c=0,d;d=b[c];c++)d.render();this.rootBlock_.setMovable(!1);this.rootBlock_.setDeletable(!1);a?(b=2*a.CORNER_RADIUS,a=a.getWidth()+b):a=b=16;this.block_.RTL&&(a=-a);this.rootBlock_.moveBy(a,b);if(this.block_.saveConnections){var e=this,f=this.block_;f.saveConnections(this.rootBlock_);this.sourceListener_=function(){f.saveConnections(e.rootBlock_)};this.block_.workspace.addChangeListener(this.sourceListener_)}this.resizeBubble_(); +this.workspace_.addChangeListener(this.workspaceChanged_.bind(this));this.applyColour()}else this.svgDialog_=null,this.workspace_.dispose(),this.rootBlock_=this.workspace_=null,this.bubble_.dispose(),this.bubble_=null,this.workspaceHeight_=this.workspaceWidth_=0,this.sourceListener_&&(this.block_.workspace.removeChangeListener(this.sourceListener_),this.sourceListener_=null)}; +Blockly.Mutator.prototype.workspaceChanged_=function(a){if(a.type!=Blockly.Events.UI&&(a.type!=Blockly.Events.CHANGE||"disabled"!=a.element)){if(!this.workspace_.isDragging())for(var b=this.workspace_.getTopBlocks(!1),c=0;a=b[c];c++){var d=a.getRelativeToSurfaceXY(),e=a.getHeightWidth();20>d.y+e.height&&a.moveBy(0,20-e.height-d.y)}this.rootBlock_.workspace==this.workspace_&&(Blockly.Events.setGroup(!0),a=this.block_,b=(b=a.mutationToDom())&&Blockly.Xml.domToText(b),a.compose(this.rootBlock_),a.initSvg(), +a.render(),Blockly.getMainWorkspace().keyboardAccessibilityMode&&Blockly.navigation.moveCursorOnBlockMutation(a),c=(c=a.mutationToDom())&&Blockly.Xml.domToText(c),b!=c&&Blockly.Events.fire(new Blockly.Events.BlockChange(a,"mutation",null,b,c)),this.workspace_.isDragging()||this.resizeBubble_(),Blockly.Events.setGroup(!1))}}; +Blockly.Mutator.prototype.getFlyoutMetrics_=function(){return{contentHeight:0,contentWidth:0,contentTop:0,contentLeft:0,viewHeight:this.workspaceHeight_,viewWidth:this.workspaceWidth_-this.workspace_.getFlyout().getWidth(),viewTop:0,viewLeft:0,absoluteTop:0,absoluteLeft:this.workspace_.RTL?0:this.workspace_.getFlyout().getWidth()}};Blockly.Mutator.prototype.dispose=function(){this.block_.mutator=null;Blockly.Icon.prototype.dispose.call(this)}; +Blockly.Mutator.prototype.updateBlockStyle=function(){var a=this.workspace_;if(a&&a.getAllBlocks(!1)){for(var b=a.getAllBlocks(!1),c=0;c=a&&this.sourceBlock_.outputConnection&&!b}else this.fullBlockClickTarget_=!1;this.fullBlockClickTarget_?this.clickTarget_=this.sourceBlock_.getSvgRoot():this.createBorderRect_();this.createTextElement_()}; +Blockly.FieldTextInput.prototype.doClassValidation_=function(a){return null===a||void 0===a?null:String(a)};Blockly.FieldTextInput.prototype.doValueInvalid_=function(a){this.isBeingEdited_&&(this.isTextValid_=!1,a=this.value_,this.value_=this.htmlInput_.untypedDefaultValue_,this.sourceBlock_&&Blockly.Events.isEnabled()&&Blockly.Events.fire(new Blockly.Events.BlockChange(this.sourceBlock_,"field",this.name||null,a,this.value_)))}; +Blockly.FieldTextInput.prototype.doValueUpdate_=function(a){this.isTextValid_=!0;this.value_=a;this.isBeingEdited_||(this.isDirty_=!0)};Blockly.FieldTextInput.prototype.applyColour=function(){this.sourceBlock_&&this.getConstants().FULL_BLOCK_FIELDS&&(this.borderRect_?this.borderRect_.setAttribute("stroke",this.sourceBlock_.style.colourTertiary):this.sourceBlock_.pathObject.svgPath.setAttribute("fill",this.getConstants().FIELD_BORDER_RECT_COLOUR))}; +Blockly.FieldTextInput.prototype.render_=function(){Blockly.FieldTextInput.superClass_.render_.call(this);if(this.isBeingEdited_){this.resizeEditor_();var a=this.htmlInput_;this.isTextValid_?(Blockly.utils.dom.removeClass(a,"blocklyInvalidInput"),Blockly.utils.aria.setState(a,Blockly.utils.aria.State.INVALID,!1)):(Blockly.utils.dom.addClass(a,"blocklyInvalidInput"),Blockly.utils.aria.setState(a,Blockly.utils.aria.State.INVALID,!0))}}; +Blockly.FieldTextInput.prototype.setSpellcheck=function(a){a!=this.spellcheck_&&(this.spellcheck_=a,this.htmlInput_&&this.htmlInput_.setAttribute("spellcheck",this.spellcheck_))};Blockly.FieldTextInput.prototype.showEditor_=function(a,b){this.workspace_=this.sourceBlock_.workspace;a=b||!1;!a&&(Blockly.utils.userAgent.MOBILE||Blockly.utils.userAgent.ANDROID||Blockly.utils.userAgent.IPAD)?this.showPromptEditor_():this.showInlineEditor_(a)}; +Blockly.FieldTextInput.prototype.showPromptEditor_=function(){var a=this;Blockly.prompt(Blockly.Msg.CHANGE_VALUE_TITLE,this.getText(),function(b){a.setValue(b)})};Blockly.FieldTextInput.prototype.showInlineEditor_=function(a){Blockly.WidgetDiv.show(this,this.sourceBlock_.RTL,this.widgetDispose_.bind(this));this.htmlInput_=this.widgetCreate_();this.isBeingEdited_=!0;a||(this.htmlInput_.focus({preventScroll:!0}),this.htmlInput_.select())}; +Blockly.FieldTextInput.prototype.widgetCreate_=function(){var a=Blockly.WidgetDiv.DIV;Blockly.utils.dom.addClass(this.getClickTarget_(),"editing");var b=document.createElement("input");b.className="blocklyHtmlInput";b.setAttribute("spellcheck",this.spellcheck_);var c=this.workspace_.getScale(),d=this.getConstants().FIELD_TEXT_FONTSIZE*c+"pt";a.style.fontSize=d;b.style.fontSize=d;d=Blockly.FieldTextInput.BORDERRADIUS*c+"px";if(this.fullBlockClickTarget_){d=this.getScaledBBox();d=(d.bottom-d.top)/2+ +"px";var e=this.sourceBlock_.getParent()?this.sourceBlock_.getParent().style.colourTertiary:this.sourceBlock_.style.colourTertiary;b.style.border=1*c+"px solid "+e;a.style.borderRadius=d;a.style.transition="box-shadow 0.25s ease 0s";this.getConstants().FIELD_TEXTINPUT_BOX_SHADOW&&(a.style.boxShadow="rgba(255, 255, 255, 0.3) 0px 0px 0px "+4*c+"px")}b.style.borderRadius=d;a.appendChild(b);b.value=b.defaultValue=this.getEditorText_(this.value_);b.untypedDefaultValue_=this.value_;b.oldValue_=null;this.resizeEditor_(); +this.bindInputEvents_(b);return b};Blockly.FieldTextInput.prototype.widgetDispose_=function(){this.isBeingEdited_=!1;this.isTextValid_=!0;this.forceRerender();if(this.onFinishEditing_)this.onFinishEditing_(this.value_);this.unbindInputEvents_();var a=Blockly.WidgetDiv.DIV.style;a.width="auto";a.height="auto";a.fontSize="";a.transition="";a.boxShadow="";this.htmlInput_=null;Blockly.utils.dom.removeClass(this.getClickTarget_(),"editing")}; +Blockly.FieldTextInput.prototype.bindInputEvents_=function(a){this.onKeyDownWrapper_=Blockly.bindEventWithChecks_(a,"keydown",this,this.onHtmlInputKeyDown_);this.onKeyInputWrapper_=Blockly.bindEventWithChecks_(a,"input",this,this.onHtmlInputChange_)}; +Blockly.FieldTextInput.prototype.unbindInputEvents_=function(){this.onKeyDownWrapper_&&(Blockly.unbindEvent_(this.onKeyDownWrapper_),this.onKeyDownWrapper_=null);this.onKeyInputWrapper_&&(Blockly.unbindEvent_(this.onKeyInputWrapper_),this.onKeyInputWrapper_=null)}; +Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_=function(a){a.keyCode==Blockly.utils.KeyCodes.ENTER?(Blockly.WidgetDiv.hide(),Blockly.DropDownDiv.hideWithoutAnimation()):a.keyCode==Blockly.utils.KeyCodes.ESC?(this.htmlInput_.value=this.htmlInput_.defaultValue,Blockly.WidgetDiv.hide(),Blockly.DropDownDiv.hideWithoutAnimation()):a.keyCode==Blockly.utils.KeyCodes.TAB&&(Blockly.WidgetDiv.hide(),Blockly.DropDownDiv.hideWithoutAnimation(),this.sourceBlock_.tab(this,!a.shiftKey),a.preventDefault())}; +Blockly.FieldTextInput.prototype.onHtmlInputChange_=function(a){a=this.htmlInput_.value;a!==this.htmlInput_.oldValue_&&(this.htmlInput_.oldValue_=a,Blockly.Events.setGroup(!0),a=this.getValueFromEditorText_(a),this.setValue(a),this.forceRerender(),this.resizeEditor_(),Blockly.Events.setGroup(!1))};Blockly.FieldTextInput.prototype.setEditorValue_=function(a){this.isDirty_=!0;this.isBeingEdited_&&(this.htmlInput_.value=this.getEditorText_(a));this.setValue(a)}; +Blockly.FieldTextInput.prototype.resizeEditor_=function(){var a=Blockly.WidgetDiv.DIV,b=this.getScaledBBox();a.style.width=b.right-b.left+"px";a.style.height=b.bottom-b.top+"px";b=new Blockly.utils.Coordinate(this.sourceBlock_.RTL?b.right-a.offsetWidth:b.left,b.top);a.style.left=b.x+"px";a.style.top=b.y+"px"}; +Blockly.FieldTextInput.numberValidator=function(a){console.warn("Blockly.FieldTextInput.numberValidator is deprecated. Use Blockly.FieldNumber instead.");if(null===a)return null;a=String(a);a=a.replace(/O/ig,"0");a=a.replace(/,/g,"");a=Number(a||0);return isNaN(a)?null:String(a)};Blockly.FieldTextInput.nonnegativeIntegerValidator=function(a){(a=Blockly.FieldTextInput.numberValidator(a))&&(a=String(Math.max(0,Math.floor(a))));return a};Blockly.FieldTextInput.prototype.isTabNavigable=function(){return!0}; +Blockly.FieldTextInput.prototype.getText_=function(){return this.isBeingEdited_&&this.htmlInput_?this.htmlInput_.value:null};Blockly.FieldTextInput.prototype.getEditorText_=function(a){return String(a)};Blockly.FieldTextInput.prototype.getValueFromEditorText_=function(a){return a};Blockly.fieldRegistry.register("field_input",Blockly.FieldTextInput);Blockly.FieldAngle=function(a,b,c){this.clockwise_=Blockly.FieldAngle.CLOCKWISE;this.offset_=Blockly.FieldAngle.OFFSET;this.wrap_=Blockly.FieldAngle.WRAP;this.round_=Blockly.FieldAngle.ROUND;Blockly.FieldAngle.superClass_.constructor.call(this,a,b,c);this.moveSurfaceWrapper_=this.clickSurfaceWrapper_=this.clickWrapper_=this.line_=this.gauge_=null};Blockly.utils.object.inherits(Blockly.FieldAngle,Blockly.FieldTextInput);Blockly.FieldAngle.prototype.DEFAULT_VALUE=0; +Blockly.FieldAngle.fromJson=function(a){return new Blockly.FieldAngle(a.angle,void 0,a)};Blockly.FieldAngle.prototype.SERIALIZABLE=!0;Blockly.FieldAngle.ROUND=15;Blockly.FieldAngle.HALF=50;Blockly.FieldAngle.CLOCKWISE=!1;Blockly.FieldAngle.OFFSET=0;Blockly.FieldAngle.WRAP=360;Blockly.FieldAngle.RADIUS=Blockly.FieldAngle.HALF-1; +Blockly.FieldAngle.prototype.configure_=function(a){Blockly.FieldAngle.superClass_.configure_.call(this,a);switch(a.mode){case "compass":this.clockwise_=!0;this.offset_=90;break;case "protractor":this.clockwise_=!1,this.offset_=0}var b=a.clockwise;"boolean"==typeof b&&(this.clockwise_=b);b=a.offset;null!=b&&(b=Number(b),isNaN(b)||(this.offset_=b));b=a.wrap;null!=b&&(b=Number(b),isNaN(b)||(this.wrap_=b));a=a.round;null!=a&&(a=Number(a),isNaN(a)||(this.round_=a))}; +Blockly.FieldAngle.prototype.initView=function(){Blockly.FieldAngle.superClass_.initView.call(this);this.symbol_=Blockly.utils.dom.createSvgElement("tspan",{},null);this.symbol_.appendChild(document.createTextNode("\u00b0"));this.textElement_.appendChild(this.symbol_)};Blockly.FieldAngle.prototype.render_=function(){Blockly.FieldAngle.superClass_.render_.call(this);this.updateGraph_()}; +Blockly.FieldAngle.prototype.showEditor_=function(a){Blockly.FieldAngle.superClass_.showEditor_.call(this,a,Blockly.utils.userAgent.MOBILE||Blockly.utils.userAgent.ANDROID||Blockly.utils.userAgent.IPAD);a=this.dropdownCreate_();Blockly.DropDownDiv.getContentDiv().appendChild(a);Blockly.DropDownDiv.setColour(this.sourceBlock_.style.colourPrimary,this.sourceBlock_.style.colourTertiary);Blockly.DropDownDiv.showPositionedByField(this,this.dropdownDispose_.bind(this));this.updateGraph_()}; +Blockly.FieldAngle.prototype.dropdownCreate_=function(){var a=Blockly.utils.dom.createSvgElement("svg",{xmlns:Blockly.utils.dom.SVG_NS,"xmlns:html":Blockly.utils.dom.HTML_NS,"xmlns:xlink":Blockly.utils.dom.XLINK_NS,version:"1.1",height:2*Blockly.FieldAngle.HALF+"px",width:2*Blockly.FieldAngle.HALF+"px",style:"touch-action: none"},null),b=Blockly.utils.dom.createSvgElement("circle",{cx:Blockly.FieldAngle.HALF,cy:Blockly.FieldAngle.HALF,r:Blockly.FieldAngle.RADIUS,"class":"blocklyAngleCircle"},a);this.gauge_= +Blockly.utils.dom.createSvgElement("path",{"class":"blocklyAngleGauge"},a);this.line_=Blockly.utils.dom.createSvgElement("line",{x1:Blockly.FieldAngle.HALF,y1:Blockly.FieldAngle.HALF,"class":"blocklyAngleLine"},a);for(var c=0;360>c;c+=15)Blockly.utils.dom.createSvgElement("line",{x1:Blockly.FieldAngle.HALF+Blockly.FieldAngle.RADIUS,y1:Blockly.FieldAngle.HALF,x2:Blockly.FieldAngle.HALF+Blockly.FieldAngle.RADIUS-(0==c%45?10:5),y2:Blockly.FieldAngle.HALF,"class":"blocklyAngleMarks",transform:"rotate("+ +c+","+Blockly.FieldAngle.HALF+","+Blockly.FieldAngle.HALF+")"},a);this.clickWrapper_=Blockly.bindEventWithChecks_(a,"click",this,this.hide_);this.clickSurfaceWrapper_=Blockly.bindEventWithChecks_(b,"click",this,this.onMouseMove_,!0,!0);this.moveSurfaceWrapper_=Blockly.bindEventWithChecks_(b,"mousemove",this,this.onMouseMove_,!0,!0);return a}; +Blockly.FieldAngle.prototype.dropdownDispose_=function(){this.clickWrapper_&&(Blockly.unbindEvent_(this.clickWrapper_),this.clickWrapper_=null);this.clickSurfaceWrapper_&&(Blockly.unbindEvent_(this.clickSurfaceWrapper_),this.clickSurfaceWrapper_=null);this.moveSurfaceWrapper_&&(Blockly.unbindEvent_(this.moveSurfaceWrapper_),this.moveSurfaceWrapper_=null);this.line_=this.gauge_=null};Blockly.FieldAngle.prototype.hide_=function(){Blockly.DropDownDiv.hideIfOwner(this);Blockly.WidgetDiv.hide()}; +Blockly.FieldAngle.prototype.onMouseMove_=function(a){var b=this.gauge_.ownerSVGElement.getBoundingClientRect(),c=a.clientX-b.left-Blockly.FieldAngle.HALF;a=a.clientY-b.top-Blockly.FieldAngle.HALF;b=Math.atan(-a/c);isNaN(b)||(b=Blockly.utils.math.toDegrees(b),0>c?b+=180:0a&&(a+=360);a>this.wrap_&&(a-=360);return a};Blockly.Css.register(".blocklyAngleCircle {,stroke: #444;,stroke-width: 1;,fill: #ddd;,fill-opacity: .8;,},.blocklyAngleMarks {,stroke: #444;,stroke-width: 1;,},.blocklyAngleGauge {,fill: #f88;,fill-opacity: .8;,pointer-events: none;,},.blocklyAngleLine {,stroke: #f00;,stroke-width: 2;,stroke-linecap: round;,pointer-events: none;,}".split(",")); +Blockly.fieldRegistry.register("field_angle",Blockly.FieldAngle);Blockly.FieldCheckbox=function(a,b,c){this.checkChar_=null;Blockly.FieldCheckbox.superClass_.constructor.call(this,a,b,c)};Blockly.utils.object.inherits(Blockly.FieldCheckbox,Blockly.Field);Blockly.FieldCheckbox.prototype.DEFAULT_VALUE=!1;Blockly.FieldCheckbox.fromJson=function(a){return new Blockly.FieldCheckbox(a.checked,void 0,a)};Blockly.FieldCheckbox.CHECK_CHAR="\u2713";Blockly.FieldCheckbox.prototype.SERIALIZABLE=!0;Blockly.FieldCheckbox.prototype.CURSOR="default"; +Blockly.FieldCheckbox.prototype.configure_=function(a){Blockly.FieldCheckbox.superClass_.configure_.call(this,a);a.checkCharacter&&(this.checkChar_=a.checkCharacter)};Blockly.FieldCheckbox.prototype.initView=function(){Blockly.FieldCheckbox.superClass_.initView.call(this);Blockly.utils.dom.addClass(this.textElement_,"blocklyCheckbox");this.textElement_.style.display=this.value_?"block":"none"}; +Blockly.FieldCheckbox.prototype.render_=function(){this.textContent_&&(this.textContent_.nodeValue=this.getDisplayText_());this.updateSize_(this.getConstants().FIELD_CHECKBOX_X_OFFSET)};Blockly.FieldCheckbox.prototype.getDisplayText_=function(){return this.checkChar_||Blockly.FieldCheckbox.CHECK_CHAR};Blockly.FieldCheckbox.prototype.setCheckCharacter=function(a){this.checkChar_=a;this.forceRerender()};Blockly.FieldCheckbox.prototype.showEditor_=function(){this.setValue(!this.value_)}; +Blockly.FieldCheckbox.prototype.doClassValidation_=function(a){return!0===a||"TRUE"===a?"TRUE":!1===a||"FALSE"===a?"FALSE":null};Blockly.FieldCheckbox.prototype.doValueUpdate_=function(a){this.value_=this.convertValueToBool_(a);this.textElement_&&(this.textElement_.style.display=this.value_?"block":"none")};Blockly.FieldCheckbox.prototype.getValue=function(){return this.value_?"TRUE":"FALSE"};Blockly.FieldCheckbox.prototype.getValueBoolean=function(){return this.value_}; +Blockly.FieldCheckbox.prototype.getText=function(){return String(this.convertValueToBool_(this.value_))};Blockly.FieldCheckbox.prototype.convertValueToBool_=function(a){return"string"==typeof a?"TRUE"==a:!!a};Blockly.fieldRegistry.register("field_checkbox",Blockly.FieldCheckbox);Blockly.FieldColour=function(a,b,c){Blockly.FieldColour.superClass_.constructor.call(this,a,b,c);this.onKeyDownWrapper_=this.onMouseLeaveWrapper_=this.onMouseEnterWrapper_=this.onMouseMoveWrapper_=this.onClickWrapper_=this.highlightedIndex_=this.picker_=null};Blockly.utils.object.inherits(Blockly.FieldColour,Blockly.Field);Blockly.FieldColour.fromJson=function(a){return new Blockly.FieldColour(a.colour,void 0,a)};Blockly.FieldColour.prototype.SERIALIZABLE=!0;Blockly.FieldColour.prototype.CURSOR="default"; +Blockly.FieldColour.prototype.isDirty_=!1;Blockly.FieldColour.prototype.colours_=null;Blockly.FieldColour.prototype.titles_=null;Blockly.FieldColour.prototype.columns_=0;Blockly.FieldColour.prototype.configure_=function(a){Blockly.FieldColour.superClass_.configure_.call(this,a);a.colourOptions&&(this.colours_=a.colourOptions,this.titles_=a.colourTitles);a.columns&&(this.columns_=a.columns)}; +Blockly.FieldColour.prototype.initView=function(){this.size_=new Blockly.utils.Size(this.getConstants().FIELD_COLOUR_DEFAULT_WIDTH,this.getConstants().FIELD_COLOUR_DEFAULT_HEIGHT);this.getConstants().FIELD_COLOUR_FULL_BLOCK?this.clickTarget_=this.sourceBlock_.getSvgRoot():(this.createBorderRect_(),this.borderRect_.style.fillOpacity="1")}; +Blockly.FieldColour.prototype.applyColour=function(){this.getConstants().FIELD_COLOUR_FULL_BLOCK?(this.sourceBlock_.pathObject.svgPath.setAttribute("fill",this.getValue()),this.sourceBlock_.pathObject.svgPath.setAttribute("stroke","#fff")):this.borderRect_&&(this.borderRect_.style.fill=this.getValue())};Blockly.FieldColour.prototype.doClassValidation_=function(a){return"string"!=typeof a?null:Blockly.utils.colour.parse(a)}; +Blockly.FieldColour.prototype.doValueUpdate_=function(a){this.value_=a;this.borderRect_?this.borderRect_.style.fill=a:this.sourceBlock_&&this.sourceBlock_.rendered&&(this.sourceBlock_.pathObject.svgPath.setAttribute("fill",a),this.sourceBlock_.pathObject.svgPath.setAttribute("stroke","#fff"))};Blockly.FieldColour.prototype.getText=function(){var a=this.value_;/^#(.)\1(.)\2(.)\3$/.test(a)&&(a="#"+a[1]+a[3]+a[5]);return a};Blockly.FieldColour.COLOURS="#ffffff #cccccc #c0c0c0 #999999 #666666 #333333 #000000 #ffcccc #ff6666 #ff0000 #cc0000 #990000 #660000 #330000 #ffcc99 #ff9966 #ff9900 #ff6600 #cc6600 #993300 #663300 #ffff99 #ffff66 #ffcc66 #ffcc33 #cc9933 #996633 #663333 #ffffcc #ffff33 #ffff00 #ffcc00 #999900 #666600 #333300 #99ff99 #66ff99 #33ff33 #33cc00 #009900 #006600 #003300 #99ffff #33ffff #66cccc #00cccc #339999 #336666 #003333 #ccffff #66ffff #33ccff #3366ff #3333ff #000099 #000066 #ccccff #9999ff #6666cc #6633ff #6600cc #333399 #330099 #ffccff #ff99ff #cc66cc #cc33cc #993399 #663366 #330033".split(" "); +Blockly.FieldColour.prototype.DEFAULT_VALUE=Blockly.FieldColour.COLOURS[0];Blockly.FieldColour.TITLES=[];Blockly.FieldColour.COLUMNS=7;Blockly.FieldColour.prototype.setColours=function(a,b){this.colours_=a;b&&(this.titles_=b);return this};Blockly.FieldColour.prototype.setColumns=function(a){this.columns_=a;return this}; +Blockly.FieldColour.prototype.showEditor_=function(){this.picker_=this.dropdownCreate_();Blockly.DropDownDiv.getContentDiv().appendChild(this.picker_);Blockly.DropDownDiv.showPositionedByField(this,this.dropdownDispose_.bind(this));this.picker_.focus({preventScroll:!0})};Blockly.FieldColour.prototype.onClick_=function(a){a=(a=a.target)&&a.label;null!==a&&(this.setValue(a),Blockly.DropDownDiv.hideIfOwner(this))}; +Blockly.FieldColour.prototype.onKeyDown_=function(a){var b=!1;if(a.keyCode===Blockly.utils.KeyCodes.UP)this.moveHighlightBy_(0,-1),b=!0;else if(a.keyCode===Blockly.utils.KeyCodes.DOWN)this.moveHighlightBy_(0,1),b=!0;else if(a.keyCode===Blockly.utils.KeyCodes.LEFT)this.moveHighlightBy_(-1,0),b=!0;else if(a.keyCode===Blockly.utils.KeyCodes.RIGHT)this.moveHighlightBy_(1,0),b=!0;else if(a.keyCode===Blockly.utils.KeyCodes.ENTER){if(b=this.getHighlighted_())b=b&&b.label,null!==b&&this.setValue(b);Blockly.DropDownDiv.hideWithoutAnimation(); +b=!0}b&&a.stopPropagation()};Blockly.FieldColour.prototype.onBlocklyAction=function(a){if(this.picker_){if(a===Blockly.navigation.ACTION_PREVIOUS)return this.moveHighlightBy_(0,-1),!0;if(a===Blockly.navigation.ACTION_NEXT)return this.moveHighlightBy_(0,1),!0;if(a===Blockly.navigation.ACTION_OUT)return this.moveHighlightBy_(-1,0),!0;if(a===Blockly.navigation.ACTION_IN)return this.moveHighlightBy_(1,0),!0}return Blockly.FieldColour.superClass_.onBlocklyAction.call(this,a)}; +Blockly.FieldColour.prototype.moveHighlightBy_=function(a,b){var c=this.colours_||Blockly.FieldColour.COLOURS,d=this.columns_||Blockly.FieldColour.COLUMNS,e=this.highlightedIndex_%d,f=Math.floor(this.highlightedIndex_/d);e+=a;f+=b;0>a?0>e&&0e&&(e=0):0d-1&&fd-1&&e--:0>b?0>f&&(f=0):0Math.floor(c.length/d)-1&&(f=Math.floor(c.length/d)-1);this.setHighlightedCell_(this.picker_.childNodes[f].childNodes[e],f*d+e)}; +Blockly.FieldColour.prototype.onMouseMove_=function(a){var b=(a=a.target)&&Number(a.getAttribute("data-index"));null!==b&&b!==this.highlightedIndex_&&this.setHighlightedCell_(a,b)};Blockly.FieldColour.prototype.onMouseEnter_=function(){this.picker_.focus({preventScroll:!0})};Blockly.FieldColour.prototype.onMouseLeave_=function(){this.picker_.blur();var a=this.getHighlighted_();a&&Blockly.utils.dom.removeClass(a,"blocklyColourHighlighted")}; +Blockly.FieldColour.prototype.getHighlighted_=function(){var a=this.columns_||Blockly.FieldColour.COLUMNS,b=this.picker_.childNodes[Math.floor(this.highlightedIndex_/a)];return b?b.childNodes[this.highlightedIndex_%a]:null}; +Blockly.FieldColour.prototype.setHighlightedCell_=function(a,b){var c=this.getHighlighted_();c&&Blockly.utils.dom.removeClass(c,"blocklyColourHighlighted");Blockly.utils.dom.addClass(a,"blocklyColourHighlighted");this.highlightedIndex_=b;Blockly.utils.aria.setState(this.picker_,Blockly.utils.aria.State.ACTIVEDESCENDANT,a.getAttribute("id"))}; +Blockly.FieldColour.prototype.dropdownCreate_=function(){var a=this.columns_||Blockly.FieldColour.COLUMNS,b=this.colours_||Blockly.FieldColour.COLOURS,c=this.titles_||Blockly.FieldColour.TITLES,d=this.getValue(),e=document.createElement("table");e.className="blocklyColourTable";e.tabIndex=0;e.dir="ltr";Blockly.utils.aria.setRole(e,Blockly.utils.aria.Role.GRID);Blockly.utils.aria.setState(e,Blockly.utils.aria.State.EXPANDED,!0);Blockly.utils.aria.setState(e,Blockly.utils.aria.State.ROWCOUNT,Math.floor(b.length/ +a));Blockly.utils.aria.setState(e,Blockly.utils.aria.State.COLCOUNT,a);for(var f,g=0;gtr>td {","border: .5px solid #888;","box-sizing: border-box;","cursor: pointer;","display: inline-block;","height: 20px;","padding: 0;","width: 20px;","}",".blocklyColourTable>tr>td.blocklyColourHighlighted {","border-color: #eee;","box-shadow: 2px 2px 7px 2px rgba(0,0,0,.3);","position: relative;","}",".blocklyColourSelected, .blocklyColourSelected:hover {", +"border-color: #eee !important;","outline: 1px solid #333;","position: relative;","}"]);Blockly.fieldRegistry.register("field_colour",Blockly.FieldColour);Blockly.FieldDropdown=function(a,b,c){"function"!=typeof a&&Blockly.FieldDropdown.validateOptions_(a);this.menuGenerator_=a;this.suffixField=this.prefixField=this.generatedOptions_=null;this.trimOptions_();this.selectedOption_=this.getOptions(!1)[0];Blockly.FieldDropdown.superClass_.constructor.call(this,this.selectedOption_[1],b,c);this.svgArrow_=this.arrow_=this.imageElement_=this.menu_=this.selectedMenuItem_=null};Blockly.utils.object.inherits(Blockly.FieldDropdown,Blockly.Field); +Blockly.FieldDropdown.fromJson=function(a){return new Blockly.FieldDropdown(a.options,void 0,a)};Blockly.FieldDropdown.prototype.SERIALIZABLE=!0;Blockly.FieldDropdown.CHECKMARK_OVERHANG=25;Blockly.FieldDropdown.MAX_MENU_HEIGHT_VH=.45;Blockly.FieldDropdown.IMAGE_Y_OFFSET=5;Blockly.FieldDropdown.IMAGE_Y_PADDING=2*Blockly.FieldDropdown.IMAGE_Y_OFFSET;Blockly.FieldDropdown.ARROW_CHAR=Blockly.utils.userAgent.ANDROID?"\u25bc":"\u25be";Blockly.FieldDropdown.prototype.CURSOR="default"; +Blockly.FieldDropdown.prototype.initView=function(){this.shouldAddBorderRect_()?this.createBorderRect_():this.clickTarget_=this.sourceBlock_.getSvgRoot();this.createTextElement_();this.imageElement_=Blockly.utils.dom.createSvgElement("image",{},this.fieldGroup_);this.getConstants().FIELD_DROPDOWN_SVG_ARROW?this.createSVGArrow_():this.createTextArrow_();this.borderRect_&&Blockly.utils.dom.addClass(this.borderRect_,"blocklyDropdownRect")}; +Blockly.FieldDropdown.prototype.shouldAddBorderRect_=function(){return!this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW||this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW&&!this.sourceBlock_.isShadow()}; +Blockly.FieldDropdown.prototype.createTextArrow_=function(){this.arrow_=Blockly.utils.dom.createSvgElement("tspan",{},this.textElement_);this.arrow_.appendChild(document.createTextNode(this.sourceBlock_.RTL?Blockly.FieldDropdown.ARROW_CHAR+" ":" "+Blockly.FieldDropdown.ARROW_CHAR));this.sourceBlock_.RTL?this.textElement_.insertBefore(this.arrow_,this.textContent_):this.textElement_.appendChild(this.arrow_)}; +Blockly.FieldDropdown.prototype.createSVGArrow_=function(){this.svgArrow_=Blockly.utils.dom.createSvgElement("image",{height:this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE+"px",width:this.getConstants().FIELD_DROPDOWN_SVG_ARROW_SIZE+"px"},this.fieldGroup_);this.svgArrow_.setAttributeNS(Blockly.utils.dom.XLINK_NS,"xlink:href",this.getConstants().FIELD_DROPDOWN_SVG_ARROW_DATAURI)}; +Blockly.FieldDropdown.prototype.showEditor_=function(a){this.menu_=this.dropdownCreate_();this.menu_.openingCoords=a&&"number"===typeof a.clientX?new Blockly.utils.Coordinate(a.clientX,a.clientY):null;this.menu_.render(Blockly.DropDownDiv.getContentDiv());a=this.menu_.getElement();Blockly.utils.dom.addClass(a,"blocklyDropdownMenu");if(this.getConstants().FIELD_DROPDOWN_COLOURED_DIV){a=this.sourceBlock_.isShadow()?this.sourceBlock_.getParent().getColour():this.sourceBlock_.getColour();var b=this.sourceBlock_.isShadow()? +this.sourceBlock_.getParent().style.colourTertiary:this.sourceBlock_.style.colourTertiary;Blockly.DropDownDiv.setColour(a,b)}Blockly.DropDownDiv.showPositionedByField(this,this.dropdownDispose_.bind(this));this.menu_.focus();this.selectedMenuItem_&&this.menu_.setHighlighted(this.selectedMenuItem_);this.applyColour()}; +Blockly.FieldDropdown.prototype.dropdownCreate_=function(){var a=new Blockly.Menu;a.setRole(Blockly.utils.aria.Role.LISTBOX);var b=this.getOptions(!1);this.selectedMenuItem_=null;for(var c=0;ca.length)){b=[];for(c=0;c=c||0>=b)throw Error("Height and width values of an image field must be greater than 0.");this.flipRtl_=!1;this.altText_="";Blockly.FieldImage.superClass_.constructor.call(this, +a,null,g);g||(this.flipRtl_=!!f,this.altText_=Blockly.utils.replaceMessageReferences(d)||"");this.size_=new Blockly.utils.Size(b,c+Blockly.FieldImage.Y_PADDING);this.imageHeight_=c;this.clickHandler_=null;"function"==typeof e&&(this.clickHandler_=e);this.imageElement_=null};Blockly.utils.object.inherits(Blockly.FieldImage,Blockly.Field);Blockly.FieldImage.prototype.DEFAULT_VALUE="";Blockly.FieldImage.fromJson=function(a){return new Blockly.FieldImage(a.src,a.width,a.height,void 0,void 0,void 0,a)}; +Blockly.FieldImage.Y_PADDING=1;Blockly.FieldImage.prototype.EDITABLE=!1;Blockly.FieldImage.prototype.isDirty_=!1;Blockly.FieldImage.prototype.configure_=function(a){Blockly.FieldImage.superClass_.configure_.call(this,a);this.flipRtl_=!!a.flipRtl;this.altText_=Blockly.utils.replaceMessageReferences(a.alt)||""}; +Blockly.FieldImage.prototype.initView=function(){this.imageElement_=Blockly.utils.dom.createSvgElement("image",{height:this.imageHeight_+"px",width:this.size_.width+"px",alt:this.altText_},this.fieldGroup_);this.imageElement_.setAttributeNS(Blockly.utils.dom.XLINK_NS,"xlink:href",this.value_);this.clickHandler_&&(this.imageElement_.style.cursor="pointer")};Blockly.FieldImage.prototype.updateSize_=function(){}; +Blockly.FieldImage.prototype.doClassValidation_=function(a){return"string"!=typeof a?null:a};Blockly.FieldImage.prototype.doValueUpdate_=function(a){this.value_=a;this.imageElement_&&this.imageElement_.setAttributeNS(Blockly.utils.dom.XLINK_NS,"xlink:href",String(this.value_))};Blockly.FieldImage.prototype.getFlipRtl=function(){return this.flipRtl_};Blockly.FieldImage.prototype.setAlt=function(a){a!=this.altText_&&(this.altText_=a||"",this.imageElement_&&this.imageElement_.setAttribute("alt",this.altText_))}; +Blockly.FieldImage.prototype.showEditor_=function(){this.clickHandler_&&this.clickHandler_(this)};Blockly.FieldImage.prototype.setOnClickHandler=function(a){this.clickHandler_=a};Blockly.FieldImage.prototype.getText_=function(){return this.altText_};Blockly.fieldRegistry.register("field_image",Blockly.FieldImage);Blockly.FieldMultilineInput=function(a,b,c){Blockly.FieldMultilineInput.superClass_.constructor.call(this,a,b,c);this.textGroup_=null};Blockly.utils.object.inherits(Blockly.FieldMultilineInput,Blockly.FieldTextInput);Blockly.FieldMultilineInput.fromJson=function(a){var b=Blockly.utils.replaceMessageReferences(a.text);return new Blockly.FieldMultilineInput(b,void 0,a)}; +Blockly.FieldMultilineInput.prototype.initView=function(){this.createBorderRect_();this.textGroup_=Blockly.utils.dom.createSvgElement("g",{"class":"blocklyEditableText"},this.fieldGroup_)}; +Blockly.FieldMultilineInput.prototype.getDisplayText_=function(){var a=this.value_;if(!a)return Blockly.Field.NBSP;var b=a.split("\n");a="";for(var c=0;cthis.maxDisplayLength&&(d=d.substring(0,this.maxDisplayLength-4)+"...");d=d.replace(/\s/g,Blockly.Field.NBSP);a+=d;c!==b.length-1&&(a+="\n")}this.sourceBlock_.RTL&&(a+="\u200f");return a}; +Blockly.FieldMultilineInput.prototype.render_=function(){for(var a;a=this.textGroup_.firstChild;)this.textGroup_.removeChild(a);a=this.getDisplayText_().split("\n");for(var b=0,c=0;cb&&(b=e);c+=this.getConstants().FIELD_TEXT_HEIGHT+(0this.max_&&Blockly.utils.aria.setState(a,Blockly.utils.aria.State.VALUEMAX,this.max_);return a};Blockly.fieldRegistry.register("field_number",Blockly.FieldNumber);Blockly.FieldVariable=function(a,b,c,d,e){this.menuGenerator_=Blockly.FieldVariable.dropdownCreate;this.defaultVariableName=a||"";this.size_=new Blockly.utils.Size(0,0);e&&this.configure_(e);b&&this.setValidator(b);e||this.setTypes_(c,d)};Blockly.utils.object.inherits(Blockly.FieldVariable,Blockly.FieldDropdown);Blockly.FieldVariable.fromJson=function(a){var b=Blockly.utils.replaceMessageReferences(a.variable);return new Blockly.FieldVariable(b,void 0,void 0,void 0,a)}; +Blockly.FieldVariable.prototype.SERIALIZABLE=!0;Blockly.FieldVariable.prototype.configure_=function(a){Blockly.FieldVariable.superClass_.configure_.call(this,a);this.setTypes_(a.variableTypes,a.defaultType)};Blockly.FieldVariable.prototype.initModel=function(){if(!this.variable_){var a=Blockly.Variables.getOrCreateVariablePackage(this.sourceBlock_.workspace,null,this.defaultVariableName,this.defaultType_);this.doValueUpdate_(a.getId())}}; +Blockly.FieldVariable.prototype.shouldAddBorderRect_=function(){return Blockly.FieldVariable.superClass_.shouldAddBorderRect_.call(this)&&(!this.getConstants().FIELD_DROPDOWN_NO_BORDER_RECT_SHADOW||"variables_get"!=this.sourceBlock_.type)}; +Blockly.FieldVariable.prototype.fromXml=function(a){var b=a.getAttribute("id"),c=a.textContent,d=a.getAttribute("variabletype")||a.getAttribute("variableType")||"";b=Blockly.Variables.getOrCreateVariablePackage(this.sourceBlock_.workspace,b,c,d);if(null!=d&&d!==b.type)throw Error("Serialized variable type with id '"+b.getId()+"' had type "+b.type+", and does not match variable field that references it: "+Blockly.Xml.domToText(a)+".");this.setValue(b.getId())}; +Blockly.FieldVariable.prototype.toXml=function(a){this.initModel();a.id=this.variable_.getId();a.textContent=this.variable_.name;this.variable_.type&&a.setAttribute("variabletype",this.variable_.type);return a};Blockly.FieldVariable.prototype.setSourceBlock=function(a){if(a.isShadow())throw Error("Variable fields are not allowed to exist on shadow blocks.");Blockly.FieldVariable.superClass_.setSourceBlock.call(this,a)}; +Blockly.FieldVariable.prototype.getValue=function(){return this.variable_?this.variable_.getId():null};Blockly.FieldVariable.prototype.getText=function(){return this.variable_?this.variable_.name:""};Blockly.FieldVariable.prototype.getVariable=function(){return this.variable_};Blockly.FieldVariable.prototype.getValidator=function(){return this.variable_?this.validator_:null}; +Blockly.FieldVariable.prototype.doClassValidation_=function(a){if(null===a)return null;var b=Blockly.Variables.getVariable(this.sourceBlock_.workspace,a);if(!b)return console.warn("Variable id doesn't point to a real variable! ID was "+a),null;b=b.type;return this.typeIsAllowed_(b)?a:(console.warn("Variable type doesn't match this field! Type was "+b),null)}; +Blockly.FieldVariable.prototype.doValueUpdate_=function(a){this.variable_=Blockly.Variables.getVariable(this.sourceBlock_.workspace,a);Blockly.FieldVariable.superClass_.doValueUpdate_.call(this,a)};Blockly.FieldVariable.prototype.typeIsAllowed_=function(a){var b=this.getVariableTypes_();if(!b)return!0;for(var c=0;crect,",a+" .blocklyEditableText>rect {","fill: "+this.FIELD_BORDER_RECT_COLOUR+";","fill-opacity: .6;","stroke: none;","}",a+" .blocklyNonEditableText>text,",a+" .blocklyEditableText>text {","fill: #000;", +"}",a+" .blocklyFlyoutLabelText {","fill: #000;","}",a+" .blocklyText.blocklyBubbleText {","fill: #000;","}",a+" .blocklyEditableText:not(.editing):hover>rect {","stroke: #fff;","stroke-width: 2;","}",a+" .blocklyHtmlInput {","font-family: "+this.FIELD_TEXT_FONTFAMILY+";","font-weight: "+this.FIELD_TEXT_FONTWEIGHT+";","}",a+" .blocklySelected>.blocklyPath {","stroke: #fc3;","stroke-width: 3px;","}",a+" .blocklyHighlightedConnectionPath {","stroke: #fc3;","}",a+" .blocklyReplaceable .blocklyPath {", +"fill-opacity: .5;","}",a+" .blocklyReplaceable .blocklyPathLight,",a+" .blocklyReplaceable .blocklyPathDark {","display: none;","}",a+" .blocklyInsertionMarker>.blocklyPath {","fill-opacity: "+this.INSERTION_MARKER_OPACITY+";","stroke: none;","}"]};Blockly.blockRendering.MarkerSvg=function(a,b,c){this.workspace_=a;this.marker_=c;this.parent_=null;this.constants_=b;this.currentMarkerSvg=null;a=this.isCursor()?this.constants_.CURSOR_COLOUR:this.constants_.MARKER_COLOUR;this.colour_=c.colour||a};Blockly.blockRendering.MarkerSvg.CURSOR_CLASS="blocklyCursor";Blockly.blockRendering.MarkerSvg.MARKER_CLASS="blocklyMarker";Blockly.blockRendering.MarkerSvg.HEIGHT_MULTIPLIER=.75;Blockly.blockRendering.MarkerSvg.prototype.getSvgRoot=function(){return this.svgGroup_}; +Blockly.blockRendering.MarkerSvg.prototype.getMarker=function(){return this.marker_};Blockly.blockRendering.MarkerSvg.prototype.isCursor=function(){return"cursor"==this.marker_.type};Blockly.blockRendering.MarkerSvg.prototype.createDom=function(){var a=this.isCursor()?Blockly.blockRendering.MarkerSvg.CURSOR_CLASS:Blockly.blockRendering.MarkerSvg.MARKER_CLASS;this.svgGroup_=Blockly.utils.dom.createSvgElement("g",{"class":a},null);this.createDomInternal_();return this.svgGroup_}; +Blockly.blockRendering.MarkerSvg.prototype.setParent_=function(a){this.isCursor()?(this.parent_&&this.parent_.setCursorSvg(null),a.setCursorSvg(this.getSvgRoot())):(this.parent_&&this.parent_.setMarkerSvg(null),a.setMarkerSvg(this.getSvgRoot()));this.parent_=a}; +Blockly.blockRendering.MarkerSvg.prototype.draw=function(a,b){if(b){this.constants_=this.workspace_.getRenderer().getConstants();var c=this.isCursor()?this.constants_.CURSOR_COLOUR:this.constants_.MARKER_COLOUR;this.colour_=this.marker_.colour||c;this.applyColour_(b);this.showAtLocation_(b);this.fireMarkerEvent_(a,b);a=this.currentMarkerSvg.childNodes[0];void 0!==a&&a.beginElement&&a.beginElement()}else this.hide()}; +Blockly.blockRendering.MarkerSvg.prototype.showAtLocation_=function(a){var b=a.getLocation();a.getType()==Blockly.ASTNode.types.BLOCK?this.showWithBlock_(a):a.getType()==Blockly.ASTNode.types.OUTPUT?this.showWithOutput_(a):b.type==Blockly.INPUT_VALUE?this.showWithInput_(a):b.type==Blockly.NEXT_STATEMENT?this.showWithNext_(a):a.getType()==Blockly.ASTNode.types.PREVIOUS?this.showWithPrevious_(a):a.getType()==Blockly.ASTNode.types.FIELD?this.showWithField_(a):a.getType()==Blockly.ASTNode.types.WORKSPACE? +this.showWithCoordinates_(a):a.getType()==Blockly.ASTNode.types.STACK&&this.showWithStack_(a)}; +Blockly.blockRendering.MarkerSvg.prototype.showWithBlockPrevOutput_=function(a){a=a.getSourceBlock();var b=a.width,c=a.height,d=c*Blockly.blockRendering.MarkerSvg.HEIGHT_MULTIPLIER,e=this.constants_.CURSOR_BLOCK_PADDING;if(a.previousConnection){var f=this.constants_.shapeFor(a.previousConnection);this.positionPrevious_(b,e,d,f)}else a.outputConnection?(f=this.constants_.shapeFor(a.outputConnection),this.positionOutput_(b,c,f)):this.positionBlock_(b,e,d);this.setParent_(a);this.showCurrent_()}; +Blockly.blockRendering.MarkerSvg.prototype.showWithBlock_=function(a){this.showWithBlockPrevOutput_(a)};Blockly.blockRendering.MarkerSvg.prototype.showWithPrevious_=function(a){this.showWithBlockPrevOutput_(a)};Blockly.blockRendering.MarkerSvg.prototype.showWithOutput_=function(a){this.showWithBlockPrevOutput_(a)}; +Blockly.blockRendering.MarkerSvg.prototype.showWithCoordinates_=function(a){var b=a.getWsCoordinate();a=b.x;b=b.y;this.workspace_.RTL&&(a-=this.constants_.CURSOR_WS_WIDTH);this.positionLine_(a,b,this.constants_.CURSOR_WS_WIDTH);this.setParent_(this.workspace_);this.showCurrent_()};Blockly.blockRendering.MarkerSvg.prototype.showWithField_=function(a){a=a.getLocation();var b=a.getSize().width,c=a.getSize().height;this.positionRect_(0,0,b,c);this.setParent_(a);this.showCurrent_()}; +Blockly.blockRendering.MarkerSvg.prototype.showWithInput_=function(a){a=a.getLocation();var b=a.getSourceBlock();this.positionInput_(a);this.setParent_(b);this.showCurrent_()};Blockly.blockRendering.MarkerSvg.prototype.showWithNext_=function(a){var b=a.getLocation();a=b.getSourceBlock();var c=0;b=b.getOffsetInBlock().y;var d=a.getHeightWidth().width;this.workspace_.RTL&&(c=-d);this.positionLine_(c,b,d);this.setParent_(a);this.showCurrent_()}; +Blockly.blockRendering.MarkerSvg.prototype.showWithStack_=function(a){a=a.getLocation();var b=a.getHeightWidth(),c=b.width+this.constants_.CURSOR_STACK_PADDING;b=b.height+this.constants_.CURSOR_STACK_PADDING;var d=-this.constants_.CURSOR_STACK_PADDING/2,e=-this.constants_.CURSOR_STACK_PADDING/2,f=d;this.workspace_.RTL&&(f=-(c+d));this.positionRect_(f,e,c,b);this.setParent_(a);this.showCurrent_()}; +Blockly.blockRendering.MarkerSvg.prototype.showCurrent_=function(){this.hide();this.currentMarkerSvg.style.display=""};Blockly.blockRendering.MarkerSvg.prototype.positionBlock_=function(a,b,c){a=Blockly.utils.svgPaths.moveBy(-b,c)+Blockly.utils.svgPaths.lineOnAxis("V",-b)+Blockly.utils.svgPaths.lineOnAxis("H",a+2*b)+Blockly.utils.svgPaths.lineOnAxis("V",c);this.markerBlock_.setAttribute("d",a);this.workspace_.RTL&&this.flipRtl_(this.markerBlock_);this.currentMarkerSvg=this.markerBlock_}; +Blockly.blockRendering.MarkerSvg.prototype.positionInput_=function(a){var b=a.getOffsetInBlock().x,c=a.getOffsetInBlock().y;a=Blockly.utils.svgPaths.moveTo(0,0)+this.constants_.shapeFor(a).pathDown;this.markerInput_.setAttribute("d",a);this.markerInput_.setAttribute("transform","translate("+b+","+c+")"+(this.workspace_.RTL?" scale(-1 1)":""));this.currentMarkerSvg=this.markerInput_}; +Blockly.blockRendering.MarkerSvg.prototype.positionLine_=function(a,b,c){this.markerSvgLine_.setAttribute("x",a);this.markerSvgLine_.setAttribute("y",b);this.markerSvgLine_.setAttribute("width",c);this.currentMarkerSvg=this.markerSvgLine_}; +Blockly.blockRendering.MarkerSvg.prototype.positionOutput_=function(a,b,c){a=Blockly.utils.svgPaths.moveBy(a,0)+Blockly.utils.svgPaths.lineOnAxis("h",-(a-c.width))+Blockly.utils.svgPaths.lineOnAxis("v",this.constants_.TAB_OFFSET_FROM_TOP)+c.pathDown+Blockly.utils.svgPaths.lineOnAxis("V",b)+Blockly.utils.svgPaths.lineOnAxis("H",a);this.markerBlock_.setAttribute("d",a);this.workspace_.RTL&&this.flipRtl_(this.markerBlock_);this.currentMarkerSvg=this.markerBlock_}; +Blockly.blockRendering.MarkerSvg.prototype.positionPrevious_=function(a,b,c,d){a=Blockly.utils.svgPaths.moveBy(-b,c)+Blockly.utils.svgPaths.lineOnAxis("V",-b)+Blockly.utils.svgPaths.lineOnAxis("H",this.constants_.NOTCH_OFFSET_LEFT)+d.pathLeft+Blockly.utils.svgPaths.lineOnAxis("H",a+2*b)+Blockly.utils.svgPaths.lineOnAxis("V",c);this.markerBlock_.setAttribute("d",a);this.workspace_.RTL&&this.flipRtl_(this.markerBlock_);this.currentMarkerSvg=this.markerBlock_}; +Blockly.blockRendering.MarkerSvg.prototype.positionRect_=function(a,b,c,d){this.markerSvgRect_.setAttribute("x",a);this.markerSvgRect_.setAttribute("y",b);this.markerSvgRect_.setAttribute("width",c);this.markerSvgRect_.setAttribute("height",d);this.currentMarkerSvg=this.markerSvgRect_};Blockly.blockRendering.MarkerSvg.prototype.flipRtl_=function(a){a.setAttribute("transform","scale(-1 1)")}; +Blockly.blockRendering.MarkerSvg.prototype.hide=function(){this.markerSvgLine_.style.display="none";this.markerSvgRect_.style.display="none";this.markerInput_.style.display="none";this.markerBlock_.style.display="none"};Blockly.blockRendering.MarkerSvg.prototype.fireMarkerEvent_=function(a,b){var c=b.getSourceBlock(),d=this.isCursor()?"cursorMove":"markerMove";a=new Blockly.Events.Ui(c,d,a,b);b.getType()==Blockly.ASTNode.types.WORKSPACE&&(a.workspaceId=b.getLocation().id);Blockly.Events.fire(a)}; +Blockly.blockRendering.MarkerSvg.prototype.getBlinkProperties_=function(){return{attributeType:"XML",attributeName:"fill",dur:"1s",values:this.colour_+";transparent;transparent;",repeatCount:"indefinite"}}; +Blockly.blockRendering.MarkerSvg.prototype.createDomInternal_=function(){this.markerSvg_=Blockly.utils.dom.createSvgElement("g",{width:this.constants_.CURSOR_WS_WIDTH,height:this.constants_.WS_CURSOR_HEIGHT},this.svgGroup_);this.markerSvgLine_=Blockly.utils.dom.createSvgElement("rect",{width:this.constants_.CURSOR_WS_WIDTH,height:this.constants_.WS_CURSOR_HEIGHT,style:"display: none"},this.markerSvg_);this.markerSvgRect_=Blockly.utils.dom.createSvgElement("rect",{"class":"blocklyVerticalMarker",rx:10, +ry:10,style:"display: none"},this.markerSvg_);this.markerInput_=Blockly.utils.dom.createSvgElement("path",{transform:"",style:"display: none"},this.markerSvg_);this.markerBlock_=Blockly.utils.dom.createSvgElement("path",{transform:"",style:"display: none",fill:"none","stroke-width":this.constants_.CURSOR_STROKE_WIDTH},this.markerSvg_);if(this.isCursor()){var a=this.getBlinkProperties_();Blockly.utils.dom.createSvgElement("animate",a,this.markerSvgLine_);Blockly.utils.dom.createSvgElement("animate", +a,this.markerInput_);a.attributeName="stroke";Blockly.utils.dom.createSvgElement("animate",a,this.markerBlock_)}return this.markerSvg_}; +Blockly.blockRendering.MarkerSvg.prototype.applyColour_=function(a){this.markerSvgLine_.setAttribute("fill",this.colour_);this.markerSvgRect_.setAttribute("stroke",this.colour_);this.markerInput_.setAttribute("fill",this.colour_);this.markerBlock_.setAttribute("stroke",this.colour_);this.isCursor()&&(a=this.colour_+";transparent;transparent;",this.markerSvgLine_.firstChild.setAttribute("values",a),this.markerInput_.firstChild.setAttribute("values",a),this.markerBlock_.firstChild.setAttribute("values", +a))};Blockly.blockRendering.MarkerSvg.prototype.dispose=function(){this.svgGroup_&&Blockly.utils.dom.removeNode(this.svgGroup_)};Blockly.blockRendering.Types={NONE:0,FIELD:1,HAT:2,ICON:4,SPACER:8,BETWEEN_ROW_SPACER:16,IN_ROW_SPACER:32,EXTERNAL_VALUE_INPUT:64,INPUT:128,INLINE_INPUT:256,STATEMENT_INPUT:512,CONNECTION:1024,PREVIOUS_CONNECTION:2048,NEXT_CONNECTION:4096,OUTPUT_CONNECTION:8192,CORNER:16384,LEFT_SQUARE_CORNER:32768,LEFT_ROUND_CORNER:65536,RIGHT_SQUARE_CORNER:131072,RIGHT_ROUND_CORNER:262144,JAGGED_EDGE:524288,ROW:1048576,TOP_ROW:2097152,BOTTOM_ROW:4194304,INPUT_ROW:8388608}; +Blockly.blockRendering.Types.LEFT_CORNER=Blockly.blockRendering.Types.LEFT_SQUARE_CORNER|Blockly.blockRendering.Types.LEFT_ROUND_CORNER;Blockly.blockRendering.Types.RIGHT_CORNER=Blockly.blockRendering.Types.RIGHT_SQUARE_CORNER|Blockly.blockRendering.Types.RIGHT_ROUND_CORNER;Blockly.blockRendering.Types.nextTypeValue_=16777216; +Blockly.blockRendering.Types.getType=function(a){Blockly.blockRendering.Types.hasOwnProperty(a)||(Blockly.blockRendering.Types[a]=Blockly.blockRendering.Types.nextTypeValue_,Blockly.blockRendering.Types.nextTypeValue_<<=1);return Blockly.blockRendering.Types[a]};Blockly.blockRendering.Types.isField=function(a){return a.type&Blockly.blockRendering.Types.FIELD};Blockly.blockRendering.Types.isHat=function(a){return a.type&Blockly.blockRendering.Types.HAT}; +Blockly.blockRendering.Types.isIcon=function(a){return a.type&Blockly.blockRendering.Types.ICON};Blockly.blockRendering.Types.isSpacer=function(a){return a.type&Blockly.blockRendering.Types.SPACER};Blockly.blockRendering.Types.isInRowSpacer=function(a){return a.type&Blockly.blockRendering.Types.IN_ROW_SPACER};Blockly.blockRendering.Types.isInput=function(a){return a.type&Blockly.blockRendering.Types.INPUT};Blockly.blockRendering.Types.isExternalInput=function(a){return a.type&Blockly.blockRendering.Types.EXTERNAL_VALUE_INPUT}; +Blockly.blockRendering.Types.isInlineInput=function(a){return a.type&Blockly.blockRendering.Types.INLINE_INPUT};Blockly.blockRendering.Types.isStatementInput=function(a){return a.type&Blockly.blockRendering.Types.STATEMENT_INPUT};Blockly.blockRendering.Types.isPreviousConnection=function(a){return a.type&Blockly.blockRendering.Types.PREVIOUS_CONNECTION};Blockly.blockRendering.Types.isNextConnection=function(a){return a.type&Blockly.blockRendering.Types.NEXT_CONNECTION}; +Blockly.blockRendering.Types.isPreviousOrNextConnection=function(a){return a.type&(Blockly.blockRendering.Types.PREVIOUS_CONNECTION|Blockly.blockRendering.Types.NEXT_CONNECTION)};Blockly.blockRendering.Types.isLeftRoundedCorner=function(a){return a.type&Blockly.blockRendering.Types.LEFT_ROUND_CORNER};Blockly.blockRendering.Types.isRightRoundedCorner=function(a){return a.type&Blockly.blockRendering.Types.RIGHT_ROUND_CORNER}; +Blockly.blockRendering.Types.isLeftSquareCorner=function(a){return a.type&Blockly.blockRendering.Types.LEFT_SQUARE_CORNER};Blockly.blockRendering.Types.isRightSquareCorner=function(a){return a.type&Blockly.blockRendering.Types.RIGHT_SQUARE_CORNER};Blockly.blockRendering.Types.isCorner=function(a){return a.type&Blockly.blockRendering.Types.CORNER};Blockly.blockRendering.Types.isJaggedEdge=function(a){return a.type&Blockly.blockRendering.Types.JAGGED_EDGE}; +Blockly.blockRendering.Types.isRow=function(a){return a.type&Blockly.blockRendering.Types.ROW};Blockly.blockRendering.Types.isBetweenRowSpacer=function(a){return a.type&Blockly.blockRendering.Types.BETWEEN_ROW_SPACER};Blockly.blockRendering.Types.isTopRow=function(a){return a.type&Blockly.blockRendering.Types.TOP_ROW};Blockly.blockRendering.Types.isBottomRow=function(a){return a.type&Blockly.blockRendering.Types.BOTTOM_ROW}; +Blockly.blockRendering.Types.isTopOrBottomRow=function(a){return a.type&(Blockly.blockRendering.Types.TOP_ROW|Blockly.blockRendering.Types.BOTTOM_ROW)};Blockly.blockRendering.Types.isInputRow=function(a){return a.type&Blockly.blockRendering.Types.INPUT_ROW};Blockly.blockRendering.Measurable=function(a){this.height=this.width=0;this.type=Blockly.blockRendering.Types.NONE;this.centerline=this.xPos=0;this.constants_=a;this.notchOffset=this.constants_.NOTCH_OFFSET_LEFT};Blockly.blockRendering.Connection=function(a,b){Blockly.blockRendering.Connection.superClass_.constructor.call(this,a);this.connectionModel=b;this.shape=this.constants_.shapeFor(b);this.isDynamicShape=!!this.shape.isDynamic;this.type|=Blockly.blockRendering.Types.CONNECTION};Blockly.utils.object.inherits(Blockly.blockRendering.Connection,Blockly.blockRendering.Measurable); +Blockly.blockRendering.OutputConnection=function(a,b){Blockly.blockRendering.OutputConnection.superClass_.constructor.call(this,a,b);this.type|=Blockly.blockRendering.Types.OUTPUT_CONNECTION;this.height=this.isDynamicShape?0:this.shape.height;this.startX=this.width=this.isDynamicShape?0:this.shape.width;this.connectionOffsetY=this.constants_.TAB_OFFSET_FROM_TOP;this.connectionOffsetX=0};Blockly.utils.object.inherits(Blockly.blockRendering.OutputConnection,Blockly.blockRendering.Connection); +Blockly.blockRendering.PreviousConnection=function(a,b){Blockly.blockRendering.PreviousConnection.superClass_.constructor.call(this,a,b);this.type|=Blockly.blockRendering.Types.PREVIOUS_CONNECTION;this.height=this.shape.height;this.width=this.shape.width};Blockly.utils.object.inherits(Blockly.blockRendering.PreviousConnection,Blockly.blockRendering.Connection); +Blockly.blockRendering.NextConnection=function(a,b){Blockly.blockRendering.NextConnection.superClass_.constructor.call(this,a,b);this.type|=Blockly.blockRendering.Types.NEXT_CONNECTION;this.height=this.shape.height;this.width=this.shape.width};Blockly.utils.object.inherits(Blockly.blockRendering.NextConnection,Blockly.blockRendering.Connection);Blockly.blockRendering.InputConnection=function(a,b){Blockly.blockRendering.InputConnection.superClass_.constructor.call(this,a,b.connection);this.type|=Blockly.blockRendering.Types.INPUT;this.input=b;this.align=b.align;(this.connectedBlock=b.connection&&b.connection.targetBlock()?b.connection.targetBlock():null)?(a=this.connectedBlock.getHeightWidth(),this.connectedBlockWidth=a.width,this.connectedBlockHeight=a.height):this.connectedBlockHeight=this.connectedBlockWidth=0;this.connectionOffsetY=this.connectionOffsetX= +0};Blockly.utils.object.inherits(Blockly.blockRendering.InputConnection,Blockly.blockRendering.Connection); +Blockly.blockRendering.InlineInput=function(a,b){Blockly.blockRendering.InlineInput.superClass_.constructor.call(this,a,b);this.type|=Blockly.blockRendering.Types.INLINE_INPUT;this.connectedBlock?(this.width=this.connectedBlockWidth,this.height=this.connectedBlockHeight):(this.height=this.constants_.EMPTY_INLINE_INPUT_HEIGHT,this.width=this.constants_.EMPTY_INLINE_INPUT_PADDING);this.connectionHeight=this.isDynamicShape?this.shape.height(this.height):this.shape.height;this.connectionWidth=this.isDynamicShape? +this.shape.width(this.height):this.shape.width;this.connectedBlock||(this.width+=this.connectionWidth*(this.isDynamicShape?2:1));this.connectionOffsetY=this.isDynamicShape?this.shape.connectionOffsetY(this.connectionHeight):this.constants_.TAB_OFFSET_FROM_TOP;this.connectionOffsetX=this.isDynamicShape?this.shape.connectionOffsetX(this.connectionWidth):0};Blockly.utils.object.inherits(Blockly.blockRendering.InlineInput,Blockly.blockRendering.InputConnection); +Blockly.blockRendering.StatementInput=function(a,b){Blockly.blockRendering.StatementInput.superClass_.constructor.call(this,a,b);this.type|=Blockly.blockRendering.Types.STATEMENT_INPUT;this.height=this.connectedBlock?this.connectedBlockHeight+this.constants_.STATEMENT_BOTTOM_SPACER:this.constants_.EMPTY_STATEMENT_INPUT_HEIGHT;this.width=this.constants_.STATEMENT_INPUT_NOTCH_OFFSET+this.shape.width};Blockly.utils.object.inherits(Blockly.blockRendering.StatementInput,Blockly.blockRendering.InputConnection); +Blockly.blockRendering.ExternalValueInput=function(a,b){Blockly.blockRendering.ExternalValueInput.superClass_.constructor.call(this,a,b);this.type|=Blockly.blockRendering.Types.EXTERNAL_VALUE_INPUT;this.height=this.connectedBlock?this.connectedBlockHeight-this.constants_.TAB_OFFSET_FROM_TOP-this.constants_.MEDIUM_PADDING:this.shape.height;this.width=this.shape.width+this.constants_.EXTERNAL_VALUE_INPUT_PADDING;this.connectionOffsetY=this.constants_.TAB_OFFSET_FROM_TOP;this.connectionHeight=this.shape.height; +this.connectionWidth=this.shape.width};Blockly.utils.object.inherits(Blockly.blockRendering.ExternalValueInput,Blockly.blockRendering.InputConnection);Blockly.blockRendering.Icon=function(a,b){Blockly.blockRendering.Icon.superClass_.constructor.call(this,a);this.icon=b;this.isVisible=b.isVisible();this.type|=Blockly.blockRendering.Types.ICON;a=b.getCorrectedSize();this.height=a.height;this.width=a.width};Blockly.utils.object.inherits(Blockly.blockRendering.Icon,Blockly.blockRendering.Measurable); +Blockly.blockRendering.JaggedEdge=function(a){Blockly.blockRendering.JaggedEdge.superClass_.constructor.call(this,a);this.type|=Blockly.blockRendering.Types.JAGGED_EDGE;this.height=this.constants_.JAGGED_TEETH.height;this.width=this.constants_.JAGGED_TEETH.width};Blockly.utils.object.inherits(Blockly.blockRendering.JaggedEdge,Blockly.blockRendering.Measurable); +Blockly.blockRendering.Field=function(a,b,c){Blockly.blockRendering.Field.superClass_.constructor.call(this,a);this.field=b;this.isEditable=b.EDITABLE;this.flipRtl=b.getFlipRtl();this.type|=Blockly.blockRendering.Types.FIELD;a=this.field.getSize();this.height=a.height;this.width=a.width;this.parentInput=c};Blockly.utils.object.inherits(Blockly.blockRendering.Field,Blockly.blockRendering.Measurable); +Blockly.blockRendering.Hat=function(a){Blockly.blockRendering.Hat.superClass_.constructor.call(this,a);this.type|=Blockly.blockRendering.Types.HAT;this.height=this.constants_.START_HAT.height;this.width=this.constants_.START_HAT.width;this.ascenderHeight=this.height};Blockly.utils.object.inherits(Blockly.blockRendering.Hat,Blockly.blockRendering.Measurable); +Blockly.blockRendering.SquareCorner=function(a,b){Blockly.blockRendering.SquareCorner.superClass_.constructor.call(this,a);this.type=(b&&"left"!=b?Blockly.blockRendering.Types.RIGHT_SQUARE_CORNER:Blockly.blockRendering.Types.LEFT_SQUARE_CORNER)|Blockly.blockRendering.Types.CORNER;this.width=this.height=this.constants_.NO_PADDING};Blockly.utils.object.inherits(Blockly.blockRendering.SquareCorner,Blockly.blockRendering.Measurable); +Blockly.blockRendering.RoundCorner=function(a,b){Blockly.blockRendering.RoundCorner.superClass_.constructor.call(this,a);this.type=(b&&"left"!=b?Blockly.blockRendering.Types.RIGHT_ROUND_CORNER:Blockly.blockRendering.Types.LEFT_ROUND_CORNER)|Blockly.blockRendering.Types.CORNER;this.width=this.constants_.CORNER_RADIUS;this.height=this.constants_.CORNER_RADIUS/2};Blockly.utils.object.inherits(Blockly.blockRendering.RoundCorner,Blockly.blockRendering.Measurable); +Blockly.blockRendering.InRowSpacer=function(a,b){Blockly.blockRendering.InRowSpacer.superClass_.constructor.call(this,a);this.type=this.type|Blockly.blockRendering.Types.SPACER|Blockly.blockRendering.Types.IN_ROW_SPACER;this.width=b;this.height=this.constants_.SPACER_DEFAULT_HEIGHT};Blockly.utils.object.inherits(Blockly.blockRendering.InRowSpacer,Blockly.blockRendering.Measurable);Blockly.blockRendering.Row=function(a){this.type=Blockly.blockRendering.Types.ROW;this.elements=[];this.xPos=this.yPos=this.widthWithConnectedBlocks=this.minWidth=this.minHeight=this.width=this.height=0;this.hasJaggedEdge=this.hasDummyInput=this.hasInlineInput=this.hasStatement=this.hasExternalInput=!1;this.constants_=a;this.notchOffset=this.constants_.NOTCH_OFFSET_LEFT;this.align=null}; +Blockly.blockRendering.Row.prototype.measure=function(){throw Error("Unexpected attempt to measure a base Row.");};Blockly.blockRendering.Row.prototype.getLastInput=function(){for(var a=this.elements.length-1,b;b=this.elements[a];a--)if(Blockly.blockRendering.Types.isInput(b))return b;return null};Blockly.blockRendering.Row.prototype.startsWithElemSpacer=function(){return!0};Blockly.blockRendering.Row.prototype.endsWithElemSpacer=function(){return!0}; +Blockly.blockRendering.Row.prototype.getFirstSpacer=function(){for(var a=0,b;b=this.elements[a];a++)if(Blockly.blockRendering.Types.isSpacer(b))return b;return null};Blockly.blockRendering.Row.prototype.getLastSpacer=function(){for(var a=this.elements.length-1,b;b=this.elements[a];a--)if(Blockly.blockRendering.Types.isSpacer(b))return b;return null}; +Blockly.blockRendering.TopRow=function(a){Blockly.blockRendering.TopRow.superClass_.constructor.call(this,a);this.type|=Blockly.blockRendering.Types.TOP_ROW;this.ascenderHeight=this.capline=0;this.hasPreviousConnection=!1;this.connection=null};Blockly.utils.object.inherits(Blockly.blockRendering.TopRow,Blockly.blockRendering.Row); +Blockly.blockRendering.TopRow.prototype.hasLeftSquareCorner=function(a){var b=(a.hat?"cap"===a.hat:this.constants_.ADD_START_HATS)&&!a.outputConnection&&!a.previousConnection,c=a.getPreviousBlock();return!!a.outputConnection||b||(c?c.getNextBlock()==a:!1)};Blockly.blockRendering.TopRow.prototype.hasRightSquareCorner=function(a){return!0}; +Blockly.blockRendering.TopRow.prototype.measure=function(){for(var a=0,b=0,c=0,d=0,e;e=this.elements[d];d++)b+=e.width,Blockly.blockRendering.Types.isSpacer(e)||(Blockly.blockRendering.Types.isHat(e)?c=Math.max(c,e.ascenderHeight):a=Math.max(a,e.height));this.width=Math.max(this.minWidth,b);this.height=Math.max(this.minHeight,a)+c;this.capline=this.ascenderHeight=c;this.widthWithConnectedBlocks=this.width};Blockly.blockRendering.TopRow.prototype.startsWithElemSpacer=function(){return!1}; +Blockly.blockRendering.TopRow.prototype.endsWithElemSpacer=function(){return!1};Blockly.blockRendering.BottomRow=function(a){Blockly.blockRendering.BottomRow.superClass_.constructor.call(this,a);this.type|=Blockly.blockRendering.Types.BOTTOM_ROW;this.hasNextConnection=!1;this.connection=null;this.baseline=this.descenderHeight=0};Blockly.utils.object.inherits(Blockly.blockRendering.BottomRow,Blockly.blockRendering.Row); +Blockly.blockRendering.BottomRow.prototype.hasLeftSquareCorner=function(a){return!!a.outputConnection||!!a.getNextBlock()};Blockly.blockRendering.BottomRow.prototype.hasRightSquareCorner=function(a){return!0}; +Blockly.blockRendering.BottomRow.prototype.measure=function(){for(var a=0,b=0,c=0,d=0,e;e=this.elements[d];d++)b+=e.width,Blockly.blockRendering.Types.isSpacer(e)||(Blockly.blockRendering.Types.isNextConnection(e)?c=Math.max(c,e.height):a=Math.max(a,e.height));this.width=Math.max(this.minWidth,b);this.height=Math.max(this.minHeight,a)+c;this.descenderHeight=c;this.widthWithConnectedBlocks=this.width};Blockly.blockRendering.BottomRow.prototype.startsWithElemSpacer=function(){return!1}; +Blockly.blockRendering.BottomRow.prototype.endsWithElemSpacer=function(){return!1};Blockly.blockRendering.SpacerRow=function(a,b,c){Blockly.blockRendering.SpacerRow.superClass_.constructor.call(this,a);this.type=this.type|Blockly.blockRendering.Types.SPACER|Blockly.blockRendering.Types.BETWEEN_ROW_SPACER;this.width=c;this.height=b;this.followsStatement=!1;this.widthWithConnectedBlocks=0;this.elements=[new Blockly.blockRendering.InRowSpacer(this.constants_,c)]}; +Blockly.utils.object.inherits(Blockly.blockRendering.SpacerRow,Blockly.blockRendering.Row);Blockly.blockRendering.SpacerRow.prototype.measure=function(){};Blockly.blockRendering.InputRow=function(a){Blockly.blockRendering.InputRow.superClass_.constructor.call(this,a);this.type|=Blockly.blockRendering.Types.INPUT_ROW;this.connectedBlockWidths=0};Blockly.utils.object.inherits(Blockly.blockRendering.InputRow,Blockly.blockRendering.Row); +Blockly.blockRendering.InputRow.prototype.measure=function(){this.width=this.minWidth;this.height=this.minHeight;for(var a=0,b=0,c;c=this.elements[b];b++)this.width+=c.width,Blockly.blockRendering.Types.isInput(c)&&(Blockly.blockRendering.Types.isStatementInput(c)?a+=c.connectedBlockWidth:Blockly.blockRendering.Types.isExternalInput(c)&&0!=c.connectedBlockWidth&&(a+=c.connectedBlockWidth-c.connectionWidth)),Blockly.blockRendering.Types.isSpacer(c)||(this.height=Math.max(this.height,c.height));this.connectedBlockWidths= +a;this.widthWithConnectedBlocks=this.width+a};Blockly.blockRendering.InputRow.prototype.endsWithElemSpacer=function(){return!this.hasExternalInput&&!this.hasStatement};Blockly.blockRendering.RenderInfo=function(a,b){this.block_=b;this.renderer_=a;this.constants_=this.renderer_.getConstants();this.outputConnection=b.outputConnection?new Blockly.blockRendering.OutputConnection(this.constants_,b.outputConnection):null;this.isInline=b.getInputsInline()&&!b.isCollapsed();this.isCollapsed=b.isCollapsed();this.isInsertionMarker=b.isInsertionMarker();this.RTL=b.RTL;this.statementEdge=this.width=this.widthWithChildren=this.height=0;this.rows=[];this.inputRows=[];this.hiddenIcons= +[];this.topRow=new Blockly.blockRendering.TopRow(this.constants_);this.bottomRow=new Blockly.blockRendering.BottomRow(this.constants_);this.startY=this.startX=0};Blockly.blockRendering.RenderInfo.prototype.getRenderer=function(){return this.renderer_};Blockly.blockRendering.RenderInfo.prototype.measure=function(){this.createRows_();this.addElemSpacing_();this.addRowSpacing_();this.computeBounds_();this.alignRowElements_();this.finalize_()}; +Blockly.blockRendering.RenderInfo.prototype.createRows_=function(){this.populateTopRow_();this.rows.push(this.topRow);var a=new Blockly.blockRendering.InputRow(this.constants_);this.inputRows.push(a);var b=this.block_.getIcons();if(b.length)for(var c=0,d;d=b[c];c++){var e=new Blockly.blockRendering.Icon(this.constants_,d);this.isCollapsed&&d.collapseHidden?this.hiddenIcons.push(e):a.elements.push(e)}d=null;for(c=0;b=this.block_.inputList[c];c++)if(b.isVisible()){this.shouldStartNewRow_(b,d)&&(this.rows.push(a), +a=new Blockly.blockRendering.InputRow(this.constants_),this.inputRows.push(a));for(d=0;e=b.fieldRow[d];d++)a.elements.push(new Blockly.blockRendering.Field(this.constants_,e,b));this.addInput_(b,a);d=b}this.isCollapsed&&(a.hasJaggedEdge=!0,a.elements.push(new Blockly.blockRendering.JaggedEdge(this.constants_)));(a.elements.length||a.hasDummyInput)&&this.rows.push(a);this.populateBottomRow_();this.rows.push(this.bottomRow)}; +Blockly.blockRendering.RenderInfo.prototype.populateTopRow_=function(){var a=!!this.block_.previousConnection,b=(this.block_.hat?"cap"===this.block_.hat:this.constants_.ADD_START_HATS)&&!this.outputConnection&&!a;this.topRow.hasLeftSquareCorner(this.block_)?this.topRow.elements.push(new Blockly.blockRendering.SquareCorner(this.constants_)):this.topRow.elements.push(new Blockly.blockRendering.RoundCorner(this.constants_));b?(a=new Blockly.blockRendering.Hat(this.constants_),this.topRow.elements.push(a), +this.topRow.capline=a.ascenderHeight):a&&(this.topRow.hasPreviousConnection=!0,this.topRow.connection=new Blockly.blockRendering.PreviousConnection(this.constants_,this.block_.previousConnection),this.topRow.elements.push(this.topRow.connection));this.block_.inputList.length&&this.block_.inputList[0].type==Blockly.NEXT_STATEMENT&&!this.block_.isCollapsed()?this.topRow.minHeight=this.constants_.TOP_ROW_PRECEDES_STATEMENT_MIN_HEIGHT:this.topRow.minHeight=this.constants_.TOP_ROW_MIN_HEIGHT;this.topRow.hasRightSquareCorner(this.block_)? +this.topRow.elements.push(new Blockly.blockRendering.SquareCorner(this.constants_,"right")):this.topRow.elements.push(new Blockly.blockRendering.RoundCorner(this.constants_,"right"))}; +Blockly.blockRendering.RenderInfo.prototype.populateBottomRow_=function(){this.bottomRow.hasNextConnection=!!this.block_.nextConnection;this.bottomRow.minHeight=this.block_.inputList.length&&this.block_.inputList[this.block_.inputList.length-1].type==Blockly.NEXT_STATEMENT?this.constants_.BOTTOM_ROW_AFTER_STATEMENT_MIN_HEIGHT:this.constants_.BOTTOM_ROW_MIN_HEIGHT;this.bottomRow.hasLeftSquareCorner(this.block_)?this.bottomRow.elements.push(new Blockly.blockRendering.SquareCorner(this.constants_)): +this.bottomRow.elements.push(new Blockly.blockRendering.RoundCorner(this.constants_));this.bottomRow.hasNextConnection&&(this.bottomRow.connection=new Blockly.blockRendering.NextConnection(this.constants_,this.block_.nextConnection),this.bottomRow.elements.push(this.bottomRow.connection));this.bottomRow.hasRightSquareCorner(this.block_)?this.bottomRow.elements.push(new Blockly.blockRendering.SquareCorner(this.constants_,"right")):this.bottomRow.elements.push(new Blockly.blockRendering.RoundCorner(this.constants_, +"right"))}; +Blockly.blockRendering.RenderInfo.prototype.addInput_=function(a,b){this.isInline&&a.type==Blockly.INPUT_VALUE?(b.elements.push(new Blockly.blockRendering.InlineInput(this.constants_,a)),b.hasInlineInput=!0):a.type==Blockly.NEXT_STATEMENT?(b.elements.push(new Blockly.blockRendering.StatementInput(this.constants_,a)),b.hasStatement=!0):a.type==Blockly.INPUT_VALUE?(b.elements.push(new Blockly.blockRendering.ExternalValueInput(this.constants_,a)),b.hasExternalInput=!0):a.type==Blockly.DUMMY_INPUT&&(b.minHeight= +Math.max(b.minHeight,a.getSourceBlock()&&a.getSourceBlock().isShadow()?this.constants_.DUMMY_INPUT_SHADOW_MIN_HEIGHT:this.constants_.DUMMY_INPUT_MIN_HEIGHT),b.hasDummyInput=!0);null==b.align&&(b.align=a.align)};Blockly.blockRendering.RenderInfo.prototype.shouldStartNewRow_=function(a,b){return b?a.type==Blockly.NEXT_STATEMENT||b.type==Blockly.NEXT_STATEMENT?!0:a.type==Blockly.INPUT_VALUE||a.type==Blockly.DUMMY_INPUT?!this.isInline:!1:!1}; +Blockly.blockRendering.RenderInfo.prototype.addElemSpacing_=function(){for(var a=0,b;b=this.rows[a];a++){var c=b.elements;b.elements=[];b.startsWithElemSpacer()&&b.elements.push(new Blockly.blockRendering.InRowSpacer(this.constants_,this.getInRowSpacing_(null,c[0])));if(c.length){for(var d=0;d.blocklyPathLight,",a+" .blocklyInsertionMarker>.blocklyPathDark {","fill-opacity: "+this.INSERTION_MARKER_OPACITY+";","stroke: none;","}"])};Blockly.geras.Highlighter=function(a){this.info_=a;this.inlineSteps_=this.steps_="";this.RTL_=this.info_.RTL;a=a.getRenderer();this.constants_=a.getConstants();this.highlightConstants_=a.getHighlightConstants();this.highlightOffset_=this.highlightConstants_.OFFSET;this.outsideCornerPaths_=this.highlightConstants_.OUTSIDE_CORNER;this.insideCornerPaths_=this.highlightConstants_.INSIDE_CORNER;this.puzzleTabPaths_=this.highlightConstants_.PUZZLE_TAB;this.notchPaths_=this.highlightConstants_.NOTCH;this.startPaths_= +this.highlightConstants_.START_HAT;this.jaggedTeethPaths_=this.highlightConstants_.JAGGED_TEETH};Blockly.geras.Highlighter.prototype.getPath=function(){return this.steps_+"\n"+this.inlineSteps_}; +Blockly.geras.Highlighter.prototype.drawTopCorner=function(a){this.steps_+=Blockly.utils.svgPaths.moveBy(a.xPos,this.info_.startY);for(var b=0,c;c=a.elements[b];b++)Blockly.blockRendering.Types.isLeftSquareCorner(c)?this.steps_+=this.highlightConstants_.START_POINT:Blockly.blockRendering.Types.isLeftRoundedCorner(c)?this.steps_+=this.outsideCornerPaths_.topLeft(this.RTL_):Blockly.blockRendering.Types.isPreviousConnection(c)?this.steps_+=this.notchPaths_.pathLeft:Blockly.blockRendering.Types.isHat(c)? +this.steps_+=this.startPaths_.path(this.RTL_):Blockly.blockRendering.Types.isSpacer(c)&&0!=c.width&&(this.steps_+=Blockly.utils.svgPaths.lineOnAxis("H",c.xPos+c.width-this.highlightOffset_));this.steps_+=Blockly.utils.svgPaths.lineOnAxis("H",a.xPos+a.width-this.highlightOffset_)};Blockly.geras.Highlighter.prototype.drawJaggedEdge_=function(a){this.info_.RTL&&(this.steps_+=this.jaggedTeethPaths_.pathLeft+Blockly.utils.svgPaths.lineOnAxis("v",a.height-this.jaggedTeethPaths_.height-this.highlightOffset_))}; +Blockly.geras.Highlighter.prototype.drawValueInput=function(a){var b=a.getLastInput();if(this.RTL_){var c=a.height-b.connectionHeight;this.steps_+=Blockly.utils.svgPaths.moveTo(b.xPos+b.width-this.highlightOffset_,a.yPos)+this.puzzleTabPaths_.pathDown(this.RTL_)+Blockly.utils.svgPaths.lineOnAxis("v",c)}else this.steps_+=Blockly.utils.svgPaths.moveTo(b.xPos+b.width,a.yPos)+this.puzzleTabPaths_.pathDown(this.RTL_)}; +Blockly.geras.Highlighter.prototype.drawStatementInput=function(a){var b=a.getLastInput();if(this.RTL_){var c=a.height-2*this.insideCornerPaths_.height;this.steps_+=Blockly.utils.svgPaths.moveTo(b.xPos,a.yPos)+this.insideCornerPaths_.pathTop(this.RTL_)+Blockly.utils.svgPaths.lineOnAxis("v",c)+this.insideCornerPaths_.pathBottom(this.RTL_)+Blockly.utils.svgPaths.lineTo(a.width-b.xPos-this.insideCornerPaths_.width,0)}else this.steps_+=Blockly.utils.svgPaths.moveTo(b.xPos,a.yPos+a.height)+this.insideCornerPaths_.pathBottom(this.RTL_)+ +Blockly.utils.svgPaths.lineTo(a.width-b.xPos-this.insideCornerPaths_.width,0)};Blockly.geras.Highlighter.prototype.drawRightSideRow=function(a){var b=a.xPos+a.width-this.highlightOffset_;a.followsStatement&&(this.steps_+=Blockly.utils.svgPaths.lineOnAxis("H",b));this.RTL_&&(this.steps_+=Blockly.utils.svgPaths.lineOnAxis("H",b),a.height>this.highlightOffset_&&(this.steps_+=Blockly.utils.svgPaths.lineOnAxis("V",a.yPos+a.height-this.highlightOffset_)))}; +Blockly.geras.Highlighter.prototype.drawBottomRow=function(a){if(this.RTL_)this.steps_+=Blockly.utils.svgPaths.lineOnAxis("V",a.baseline-this.highlightOffset_);else{var b=this.info_.bottomRow.elements[0];Blockly.blockRendering.Types.isLeftSquareCorner(b)?this.steps_+=Blockly.utils.svgPaths.moveTo(a.xPos+this.highlightOffset_,a.baseline-this.highlightOffset_):Blockly.blockRendering.Types.isLeftRoundedCorner(b)&&(this.steps_+=Blockly.utils.svgPaths.moveTo(a.xPos,a.baseline),this.steps_+=this.outsideCornerPaths_.bottomLeft())}}; +Blockly.geras.Highlighter.prototype.drawLeft=function(){var a=this.info_.outputConnection;a&&(a=a.connectionOffsetY+a.height,this.RTL_?this.steps_+=Blockly.utils.svgPaths.moveTo(this.info_.startX,a):(this.steps_+=Blockly.utils.svgPaths.moveTo(this.info_.startX+this.highlightOffset_,this.info_.bottomRow.baseline-this.highlightOffset_),this.steps_+=Blockly.utils.svgPaths.lineOnAxis("V",a)),this.steps_+=this.puzzleTabPaths_.pathUp(this.RTL_));this.RTL_||(a=this.info_.topRow,Blockly.blockRendering.Types.isLeftRoundedCorner(a.elements[0])? +this.steps_+=Blockly.utils.svgPaths.lineOnAxis("V",this.outsideCornerPaths_.height):this.steps_+=Blockly.utils.svgPaths.lineOnAxis("V",a.capline+this.highlightOffset_))}; +Blockly.geras.Highlighter.prototype.drawInlineInput=function(a){var b=this.highlightOffset_,c=a.xPos+a.connectionWidth,d=a.centerline-a.height/2,e=a.width-a.connectionWidth,f=d+b;this.RTL_?(d=a.connectionOffsetY-b,a=a.height-(a.connectionOffsetY+a.connectionHeight)+b,this.inlineSteps_+=Blockly.utils.svgPaths.moveTo(c-b,f)+Blockly.utils.svgPaths.lineOnAxis("v",d)+this.puzzleTabPaths_.pathDown(this.RTL_)+Blockly.utils.svgPaths.lineOnAxis("v",a)+Blockly.utils.svgPaths.lineOnAxis("h",e)):this.inlineSteps_+= +Blockly.utils.svgPaths.moveTo(a.xPos+a.width+b,f)+Blockly.utils.svgPaths.lineOnAxis("v",a.height)+Blockly.utils.svgPaths.lineOnAxis("h",-e)+Blockly.utils.svgPaths.moveTo(c,d+a.connectionOffsetY)+this.puzzleTabPaths_.pathDown(this.RTL_)};Blockly.geras.InlineInput=function(a,b){Blockly.geras.InlineInput.superClass_.constructor.call(this,a,b);this.connectedBlock&&(this.width+=this.constants_.DARK_PATH_OFFSET,this.height+=this.constants_.DARK_PATH_OFFSET)};Blockly.utils.object.inherits(Blockly.geras.InlineInput,Blockly.blockRendering.InlineInput);Blockly.geras.StatementInput=function(a,b){Blockly.geras.StatementInput.superClass_.constructor.call(this,a,b);this.connectedBlock&&(this.height+=this.constants_.DARK_PATH_OFFSET)}; +Blockly.utils.object.inherits(Blockly.geras.StatementInput,Blockly.blockRendering.StatementInput);Blockly.geras.RenderInfo=function(a,b){Blockly.geras.RenderInfo.superClass_.constructor.call(this,a,b)};Blockly.utils.object.inherits(Blockly.geras.RenderInfo,Blockly.blockRendering.RenderInfo);Blockly.geras.RenderInfo.prototype.getRenderer=function(){return this.renderer_}; +Blockly.geras.RenderInfo.prototype.populateBottomRow_=function(){Blockly.geras.RenderInfo.superClass_.populateBottomRow_.call(this);this.block_.inputList.length&&this.block_.inputList[this.block_.inputList.length-1].type==Blockly.NEXT_STATEMENT||(this.bottomRow.minHeight=this.constants_.MEDIUM_PADDING-this.constants_.DARK_PATH_OFFSET)}; +Blockly.geras.RenderInfo.prototype.addInput_=function(a,b){this.isInline&&a.type==Blockly.INPUT_VALUE?(b.elements.push(new Blockly.geras.InlineInput(this.constants_,a)),b.hasInlineInput=!0):a.type==Blockly.NEXT_STATEMENT?(b.elements.push(new Blockly.geras.StatementInput(this.constants_,a)),b.hasStatement=!0):a.type==Blockly.INPUT_VALUE?(b.elements.push(new Blockly.blockRendering.ExternalValueInput(this.constants_,a)),b.hasExternalInput=!0):a.type==Blockly.DUMMY_INPUT&&(b.minHeight=Math.max(b.minHeight, +this.constants_.DUMMY_INPUT_MIN_HEIGHT),b.hasDummyInput=!0);this.isInline||null!=b.align||(b.align=a.align)}; +Blockly.geras.RenderInfo.prototype.addElemSpacing_=function(){for(var a=!1,b=0,c;c=this.rows[b];b++)c.hasExternalInput&&(a=!0);for(b=0;c=this.rows[b];b++){var d=c.elements;c.elements=[];c.startsWithElemSpacer()&&c.elements.push(new Blockly.blockRendering.InRowSpacer(this.constants_,this.getInRowSpacing_(null,d[0])));if(d.length){for(var e=0;eb?b:c;e=e?-1:1;a=(d?-1:1)*a/2;return Blockly.utils.svgPaths.lineTo(-e*c,a)+Blockly.utils.svgPaths.lineTo(e*c,a)}var b=this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH;return{type:this.SHAPES.HEXAGONAL,isDynamic:!0,width:function(a){a/=2;return a>b?b:a},height:function(a){return a},connectionOffsetY:function(a){return a/2},connectionOffsetX:function(a){return-a},pathDown:function(b){return a(b,!1,!1)},pathUp:function(b){return a(b, +!0,!1)},pathRightDown:function(b){return a(b,!1,!0)},pathRightUp:function(b){return a(b,!1,!0)}}}; +Blockly.zelos.ConstantProvider.prototype.makeRounded=function(){function a(a,b,f){var d=a>c?a-c:0;a=(a>c?c:a)/2;return Blockly.utils.svgPaths.arc("a","0 0,1",a,Blockly.utils.svgPaths.point((b?-1:1)*a,(b?-1:1)*a))+Blockly.utils.svgPaths.lineOnAxis("v",(f?1:-1)*d)+Blockly.utils.svgPaths.arc("a","0 0,1",a,Blockly.utils.svgPaths.point((b?1:-1)*a,(b?-1:1)*a))}var b=this.MAX_DYNAMIC_CONNECTION_SHAPE_WIDTH,c=2*b;return{type:this.SHAPES.ROUND,isDynamic:!0,width:function(a){a/=2;return a>b?b:a},height:function(a){return a}, +connectionOffsetY:function(a){return a/2},connectionOffsetX:function(a){return-a},pathDown:function(b){return a(b,!1,!1)},pathUp:function(b){return a(b,!0,!1)},pathRightDown:function(b){return a(b,!1,!0)},pathRightUp:function(b){return a(b,!1,!0)}}}; +Blockly.zelos.ConstantProvider.prototype.makeSquared=function(){function a(a,d,e){a-=2*b;return Blockly.utils.svgPaths.arc("a","0 0,1",b,Blockly.utils.svgPaths.point((d?-1:1)*b,(d?-1:1)*b))+Blockly.utils.svgPaths.lineOnAxis("v",(e?1:-1)*a)+Blockly.utils.svgPaths.arc("a","0 0,1",b,Blockly.utils.svgPaths.point((d?1:-1)*b,(d?-1:1)*b))}var b=this.CORNER_RADIUS;return{type:this.SHAPES.SQUARE,isDynamic:!0,width:function(a){return b},height:function(a){return a},connectionOffsetY:function(a){return a/2}, +connectionOffsetX:function(a){return-a},pathDown:function(b){return a(b,!1,!1)},pathUp:function(b){return a(b,!0,!1)},pathRightDown:function(b){return a(b,!1,!0)},pathRightUp:function(b){return a(b,!1,!0)}}}; +Blockly.zelos.ConstantProvider.prototype.shapeFor=function(a){var b=a.getCheck();!b&&a.targetConnection&&(b=a.targetConnection.getCheck());switch(a.type){case Blockly.INPUT_VALUE:case Blockly.OUTPUT_VALUE:a=a.getSourceBlock().getOutputShape();if(null!=a)switch(a){case this.SHAPES.HEXAGONAL:return this.HEXAGONAL;case this.SHAPES.ROUND:return this.ROUNDED;case this.SHAPES.SQUARE:return this.SQUARED}if(b&&-1!=b.indexOf("Boolean"))return this.HEXAGONAL;if(b&&-1!=b.indexOf("Number"))return this.ROUNDED; +b&&b.indexOf("String");return this.ROUNDED;case Blockly.PREVIOUS_STATEMENT:case Blockly.NEXT_STATEMENT:return this.NOTCH;default:throw Error("Unknown type");}}; +Blockly.zelos.ConstantProvider.prototype.makeNotch=function(){function a(a){return Blockly.utils.svgPaths.curve("c",[Blockly.utils.svgPaths.point(a*e/2,0),Blockly.utils.svgPaths.point(a*e*3/4,g/2),Blockly.utils.svgPaths.point(a*e,g)])+Blockly.utils.svgPaths.line([Blockly.utils.svgPaths.point(a*e,f)])+Blockly.utils.svgPaths.curve("c",[Blockly.utils.svgPaths.point(a*e/4,g/2),Blockly.utils.svgPaths.point(a*e/2,g),Blockly.utils.svgPaths.point(a*e,g)])+Blockly.utils.svgPaths.lineOnAxis("h",a*d)+Blockly.utils.svgPaths.curve("c", +[Blockly.utils.svgPaths.point(a*e/2,0),Blockly.utils.svgPaths.point(a*e*3/4,-(g/2)),Blockly.utils.svgPaths.point(a*e,-g)])+Blockly.utils.svgPaths.line([Blockly.utils.svgPaths.point(a*e,-f)])+Blockly.utils.svgPaths.curve("c",[Blockly.utils.svgPaths.point(a*e/4,-(g/2)),Blockly.utils.svgPaths.point(a*e/2,-g),Blockly.utils.svgPaths.point(a*e,-g)])}var b=this.NOTCH_WIDTH,c=this.NOTCH_HEIGHT,d=b/3,e=d/3,f=c/2,g=f/2,h=a(1),k=a(-1);return{type:this.SHAPES.NOTCH,width:b,height:c,pathLeft:h,pathRight:k}}; +Blockly.zelos.ConstantProvider.prototype.makeInsideCorners=function(){var a=this.CORNER_RADIUS,b=Blockly.utils.svgPaths.arc("a","0 0,0",a,Blockly.utils.svgPaths.point(-a,a)),c=Blockly.utils.svgPaths.arc("a","0 0,1",a,Blockly.utils.svgPaths.point(-a,a)),d=Blockly.utils.svgPaths.arc("a","0 0,0",a,Blockly.utils.svgPaths.point(a,a)),e=Blockly.utils.svgPaths.arc("a","0 0,1",a,Blockly.utils.svgPaths.point(a,a));return{width:a,height:a,pathTop:b,pathBottom:d,rightWidth:a,rightHeight:a,pathTopRight:c,pathBottomRight:e}}; +Blockly.zelos.ConstantProvider.prototype.generateSecondaryColour_=function(a){return Blockly.utils.colour.blend("#000",a,.15)||a};Blockly.zelos.ConstantProvider.prototype.generateTertiaryColour_=function(a){return Blockly.utils.colour.blend("#000",a,.25)||a}; +Blockly.zelos.ConstantProvider.prototype.createDom=function(a,b,c){Blockly.zelos.ConstantProvider.superClass_.createDom.call(this,a,b,c);a=Blockly.utils.dom.createSvgElement("defs",{},a);b=Blockly.utils.dom.createSvgElement("filter",{id:"blocklySelectedGlowFilter"+this.randomIdentifier,height:"160%",width:"180%",y:"-30%",x:"-40%"},a);Blockly.utils.dom.createSvgElement("feGaussianBlur",{"in":"SourceGraphic",stdDeviation:this.SELECTED_GLOW_SIZE},b);c=Blockly.utils.dom.createSvgElement("feComponentTransfer", +{result:"outBlur"},b);Blockly.utils.dom.createSvgElement("feFuncA",{type:"table",tableValues:"0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1"},c);Blockly.utils.dom.createSvgElement("feFlood",{"flood-color":this.SELECTED_GLOW_COLOUR,"flood-opacity":1,result:"outColor"},b);Blockly.utils.dom.createSvgElement("feComposite",{"in":"outColor",in2:"outBlur",operator:"in",result:"outGlow"},b);this.selectedGlowFilterId=b.id;this.selectedGlowFilter_=b;a=Blockly.utils.dom.createSvgElement("filter",{id:"blocklyReplacementGlowFilter"+ +this.randomIdentifier,height:"160%",width:"180%",y:"-30%",x:"-40%"},a);Blockly.utils.dom.createSvgElement("feGaussianBlur",{"in":"SourceGraphic",stdDeviation:this.REPLACEMENT_GLOW_SIZE},a);b=Blockly.utils.dom.createSvgElement("feComponentTransfer",{result:"outBlur"},a);Blockly.utils.dom.createSvgElement("feFuncA",{type:"table",tableValues:"0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1"},b);Blockly.utils.dom.createSvgElement("feFlood",{"flood-color":this.REPLACEMENT_GLOW_COLOUR,"flood-opacity":1,result:"outColor"}, +a);Blockly.utils.dom.createSvgElement("feComposite",{"in":"outColor",in2:"outBlur",operator:"in",result:"outGlow"},a);Blockly.utils.dom.createSvgElement("feComposite",{"in":"SourceGraphic",in2:"outGlow",operator:"over"},a);this.replacementGlowFilterId=a.id;this.replacementGlowFilter_=a}; +Blockly.zelos.ConstantProvider.prototype.getCSS_=function(a){return[a+" .blocklyText,",a+" .blocklyFlyoutLabelText {","font: "+this.FIELD_TEXT_FONTWEIGHT+" "+this.FIELD_TEXT_FONTSIZE+"pt "+this.FIELD_TEXT_FONTFAMILY+";","}",a+" .blocklyText {","fill: #fff;","}",a+" .blocklyNonEditableText>rect:not(.blocklyDropdownRect),",a+" .blocklyEditableText>rect:not(.blocklyDropdownRect) {","fill: "+this.FIELD_BORDER_RECT_COLOUR+";","}",a+" .blocklyNonEditableText>text,",a+" .blocklyEditableText>text,",a+" .blocklyNonEditableText>g>text,", +a+" .blocklyEditableText>g>text {","fill: #575E75;","}",a+" .blocklyFlyoutLabelText {","fill: #575E75;","}",a+" .blocklyText.blocklyBubbleText {","fill: #575E75;","}",a+" .blocklyDraggable:not(.blocklyDisabled)"," .blocklyEditableText:not(.editing):hover>rect,",a+" .blocklyDraggable:not(.blocklyDisabled)"," .blocklyEditableText:not(.editing):hover>.blocklyPath {","stroke: #fff;","stroke-width: 2;","}",a+" .blocklyHtmlInput {","font-family: "+this.FIELD_TEXT_FONTFAMILY+";","font-weight: "+this.FIELD_TEXT_FONTWEIGHT+ +";","color: #575E75;","}",a+" .blocklyDropdownText {","fill: #fff !important;","}",a+".blocklyWidgetDiv .goog-menuitem,",a+".blocklyDropDownDiv .goog-menuitem {","font-family: "+this.FIELD_TEXT_FONTFAMILY+";","}",a+".blocklyDropDownDiv .goog-menuitem-content {","color: #fff;","}",a+" .blocklyHighlightedConnectionPath {","stroke: "+this.SELECTED_GLOW_COLOUR+";","}",a+" .blocklyDisabled > .blocklyOutlinePath {","fill: url(#blocklyDisabledPattern"+this.randomIdentifier+")","}",a+" .blocklyInsertionMarker>.blocklyPath {", +"fill-opacity: "+this.INSERTION_MARKER_OPACITY+";","stroke: none;","}"]};Blockly.zelos.TopRow=function(a){Blockly.zelos.TopRow.superClass_.constructor.call(this,a)};Blockly.utils.object.inherits(Blockly.zelos.TopRow,Blockly.blockRendering.TopRow);Blockly.zelos.TopRow.prototype.endsWithElemSpacer=function(){return!1};Blockly.zelos.TopRow.prototype.hasLeftSquareCorner=function(a){var b=(a.hat?"cap"===a.hat:this.constants_.ADD_START_HATS)&&!a.outputConnection&&!a.previousConnection;return!!a.outputConnection||b}; +Blockly.zelos.TopRow.prototype.hasRightSquareCorner=function(a){return!!a.outputConnection&&!a.statementInputCount&&!a.nextConnection};Blockly.zelos.BottomRow=function(a){Blockly.zelos.BottomRow.superClass_.constructor.call(this,a)};Blockly.utils.object.inherits(Blockly.zelos.BottomRow,Blockly.blockRendering.BottomRow);Blockly.zelos.BottomRow.prototype.endsWithElemSpacer=function(){return!1};Blockly.zelos.BottomRow.prototype.hasLeftSquareCorner=function(a){return!!a.outputConnection}; +Blockly.zelos.BottomRow.prototype.hasRightSquareCorner=function(a){return!!a.outputConnection&&!a.statementInputCount&&!a.nextConnection};Blockly.zelos.RightConnectionShape=function(a){Blockly.zelos.RightConnectionShape.superClass_.constructor.call(this,a);this.type|=Blockly.blockRendering.Types.getType("RIGHT_CONNECTION");this.width=this.height=0};Blockly.utils.object.inherits(Blockly.zelos.RightConnectionShape,Blockly.blockRendering.Measurable);Blockly.zelos.StatementInput=function(a,b){Blockly.zelos.StatementInput.superClass_.constructor.call(this,a,b);if(this.connectedBlock){for(a=this.connectedBlock;a.getNextBlock();)a=a.getNextBlock();a.nextConnection||(this.height=this.connectedBlockHeight,this.connectedBottomNextConnection=!0)}};Blockly.utils.object.inherits(Blockly.zelos.StatementInput,Blockly.blockRendering.StatementInput);Blockly.zelos.RenderInfo=function(a,b){Blockly.zelos.RenderInfo.superClass_.constructor.call(this,a,b);this.topRow=new Blockly.zelos.TopRow(this.constants_);this.bottomRow=new Blockly.zelos.BottomRow(this.constants_);this.isInline=!0;this.isMultiRow=!b.getInputsInline()||b.isCollapsed();this.hasStatementInput=0=this.rows.length-1?!!this.bottomRow.hasNextConnection:!!f.precedesStatement;if(Blockly.blockRendering.Types.isInputRow(e)&&e.hasStatement)e.measure(),b=e.width-e.getLastInput().width+a;else if(d&&(2==c||f)&& +Blockly.blockRendering.Types.isInputRow(e)&&!e.hasStatement){f=e.xPos;d=null;for(var g=0,h;h=e.elements[g];g++)Blockly.blockRendering.Types.isSpacer(h)&&(d=h),!(d&&(Blockly.blockRendering.Types.isField(h)||Blockly.blockRendering.Types.isInput(h))&&fc?c:this.height/2,b-c*(1-Math.sin(Math.acos((c-this.constants_.SMALL_PADDING)/c)));default:return 0}if(Blockly.blockRendering.Types.isInlineInput(a)){var e=a.connectedBlock;a=e?e.pathObject.outputShapeType: +a.shape.type;return e&&e.outputConnection&&(e.statementInputCount||e.nextConnection)||c==d.SHAPES.HEXAGONAL&&c!=a?0:b-this.constants_.SHAPE_IN_SHAPE_PADDING[c][a]}return Blockly.blockRendering.Types.isField(a)?c==d.SHAPES.ROUND&&a.field instanceof Blockly.FieldTextInput?b-2.75*d.GRID_UNIT:b-this.constants_.SHAPE_IN_SHAPE_PADDING[c][0]:Blockly.blockRendering.Types.isIcon(a)?this.constants_.SMALL_PADDING:0}; +Blockly.zelos.RenderInfo.prototype.finalizeVerticalAlignment_=function(){if(!this.outputConnection)for(var a=2;a=this.rows.length-1?!!this.bottomRow.hasNextConnection:!!d.precedesStatement;if(e?this.topRow.hasPreviousConnection:b.followsStatement){var g=3==c.elements.length&&(c.elements[1].field instanceof Blockly.FieldLabel||c.elements[1].field instanceof Blockly.FieldImage);if(!e&&g)b.height-=this.constants_.SMALL_PADDING, +d.height-=this.constants_.SMALL_PADDING,c.height-=this.constants_.MEDIUM_PADDING;else if(!e&&!f)b.height+=this.constants_.SMALL_PADDING;else if(f){e=!1;for(f=0;g=c.elements[f];f++)if(Blockly.blockRendering.Types.isInlineInput(g)&&g.connectedBlock&&!g.connectedBlock.isShadow()&&40<=g.connectedBlock.getHeightWidth().height){e=!0;break}e&&(b.height-=this.constants_.SMALL_PADDING,d.height-=this.constants_.SMALL_PADDING)}}}}; +Blockly.zelos.RenderInfo.prototype.finalize_=function(){this.finalizeOutputConnection_();this.finalizeHorizontalAlignment_();this.finalizeVerticalAlignment_();Blockly.zelos.RenderInfo.superClass_.finalize_.call(this);this.rightSide&&(this.widthWithChildren+=this.rightSide.width)};Blockly.zelos.Drawer=function(a,b){Blockly.zelos.Drawer.superClass_.constructor.call(this,a,b)};Blockly.utils.object.inherits(Blockly.zelos.Drawer,Blockly.blockRendering.Drawer); +Blockly.zelos.Drawer.prototype.draw=function(){var a=this.block_.pathObject;a.beginDrawing();this.hideHiddenIcons_();this.drawOutline_();this.drawInternals_();a.setPath(this.outlinePath_+"\n"+this.inlinePath_);this.info_.RTL&&a.flipRTL();Blockly.blockRendering.useDebugger&&this.block_.renderingDebugger.drawDebug(this.block_,this.info_);this.recordSizeOnBlock_();this.info_.outputConnection&&(a.outputShapeType=this.info_.outputConnection.shape.type);a.endDrawing()}; +Blockly.zelos.Drawer.prototype.drawOutline_=function(){this.info_.outputConnection&&this.info_.outputConnection.isDynamicShape&&!this.info_.hasStatementInput&&!this.info_.bottomRow.hasNextConnection?(this.drawFlatTop_(),this.drawRightDynamicConnection_(),this.drawFlatBottom_(),this.drawLeftDynamicConnection_()):Blockly.zelos.Drawer.superClass_.drawOutline_.call(this)}; +Blockly.zelos.Drawer.prototype.drawLeft_=function(){this.info_.outputConnection&&this.info_.outputConnection.isDynamicShape?this.drawLeftDynamicConnection_():Blockly.zelos.Drawer.superClass_.drawLeft_.call(this)}; +Blockly.zelos.Drawer.prototype.drawRightSideRow_=function(a){if(!(0>=a.height))if(a.precedesStatement||a.followsStatement){var b=this.constants_.INSIDE_CORNERS.rightHeight;b=a.height-(a.precedesStatement?b:0);this.outlinePath_+=(a.followsStatement?this.constants_.INSIDE_CORNERS.pathBottomRight:"")+(0","GT"],["\u200f\u2265","GTE"]]},{type:"input_value",name:"B"}],inputsInline:!0,output:"Boolean",style:"logic_blocks",helpUrl:"%{BKY_LOGIC_COMPARE_HELPURL}",extensions:["logic_compare", +"logic_op_tooltip"]},{type:"logic_operation",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Boolean"},{type:"field_dropdown",name:"OP",options:[["%{BKY_LOGIC_OPERATION_AND}","AND"],["%{BKY_LOGIC_OPERATION_OR}","OR"]]},{type:"input_value",name:"B",check:"Boolean"}],inputsInline:!0,output:"Boolean",style:"logic_blocks",helpUrl:"%{BKY_LOGIC_OPERATION_HELPURL}",extensions:["logic_op_tooltip"]},{type:"logic_negate",message0:"%{BKY_LOGIC_NEGATE_TITLE}",args0:[{type:"input_value",name:"BOOL", +check:"Boolean"}],output:"Boolean",style:"logic_blocks",tooltip:"%{BKY_LOGIC_NEGATE_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NEGATE_HELPURL}"},{type:"logic_null",message0:"%{BKY_LOGIC_NULL}",output:null,style:"logic_blocks",tooltip:"%{BKY_LOGIC_NULL_TOOLTIP}",helpUrl:"%{BKY_LOGIC_NULL_HELPURL}"},{type:"logic_ternary",message0:"%{BKY_LOGIC_TERNARY_CONDITION} %1",args0:[{type:"input_value",name:"IF",check:"Boolean"}],message1:"%{BKY_LOGIC_TERNARY_IF_TRUE} %1",args1:[{type:"input_value",name:"THEN"}],message2:"%{BKY_LOGIC_TERNARY_IF_FALSE} %1", +args2:[{type:"input_value",name:"ELSE"}],output:null,style:"logic_blocks",tooltip:"%{BKY_LOGIC_TERNARY_TOOLTIP}",helpUrl:"%{BKY_LOGIC_TERNARY_HELPURL}",extensions:["logic_ternary"]}]); +Blockly.defineBlocksWithJsonArray([{type:"controls_if_if",message0:"%{BKY_CONTROLS_IF_IF_TITLE_IF}",nextStatement:null,enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_IF_TOOLTIP}"},{type:"controls_if_elseif",message0:"%{BKY_CONTROLS_IF_ELSEIF_TITLE_ELSEIF}",previousStatement:null,nextStatement:null,enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_ELSEIF_TOOLTIP}"},{type:"controls_if_else",message0:"%{BKY_CONTROLS_IF_ELSE_TITLE_ELSE}",previousStatement:null, +enableContextMenu:!1,style:"logic_blocks",tooltip:"%{BKY_CONTROLS_IF_ELSE_TOOLTIP}"}]);Blockly.Constants.Logic.TOOLTIPS_BY_OP={EQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_EQ}",NEQ:"%{BKY_LOGIC_COMPARE_TOOLTIP_NEQ}",LT:"%{BKY_LOGIC_COMPARE_TOOLTIP_LT}",LTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_LTE}",GT:"%{BKY_LOGIC_COMPARE_TOOLTIP_GT}",GTE:"%{BKY_LOGIC_COMPARE_TOOLTIP_GTE}",AND:"%{BKY_LOGIC_OPERATION_TOOLTIP_AND}",OR:"%{BKY_LOGIC_OPERATION_TOOLTIP_OR}"}; +Blockly.Extensions.register("logic_op_tooltip",Blockly.Extensions.buildTooltipForDropdown("OP",Blockly.Constants.Logic.TOOLTIPS_BY_OP)); +Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN={elseifCount_:0,elseCount_:0,suppressPrefixSuffix:!0,mutationToDom:function(){if(!this.elseifCount_&&!this.elseCount_)return null;var a=Blockly.utils.xml.createElement("mutation");this.elseifCount_&&a.setAttribute("elseif",this.elseifCount_);this.elseCount_&&a.setAttribute("else",1);return a},domToMutation:function(a){this.elseifCount_=parseInt(a.getAttribute("elseif"),10)||0;this.elseCount_=parseInt(a.getAttribute("else"),10)||0;this.rebuildShape_()}, +decompose:function(a){var b=a.newBlock("controls_if_if");b.initSvg();for(var c=b.nextConnection,d=1;d<=this.elseifCount_;d++){var e=a.newBlock("controls_if_elseif");e.initSvg();c.connect(e.previousConnection);c=e.nextConnection}this.elseCount_&&(a=a.newBlock("controls_if_else"),a.initSvg(),c.connect(a.previousConnection));return b},compose:function(a){a=a.nextConnection.targetBlock();this.elseCount_=this.elseifCount_=0;for(var b=[null],c=[null],d=null;a;){switch(a.type){case "controls_if_elseif":this.elseifCount_++; +b.push(a.valueConnection_);c.push(a.statementConnection_);break;case "controls_if_else":this.elseCount_++;d=a.statementConnection_;break;default:throw TypeError("Unknown block type: "+a.type);}a=a.nextConnection&&a.nextConnection.targetBlock()}this.updateShape_();this.reconnectChildBlocks_(b,c,d)},saveConnections:function(a){a=a.nextConnection.targetBlock();for(var b=1;a;){switch(a.type){case "controls_if_elseif":var c=this.getInput("IF"+b),d=this.getInput("DO"+b);a.valueConnection_=c&&c.connection.targetConnection; +a.statementConnection_=d&&d.connection.targetConnection;b++;break;case "controls_if_else":d=this.getInput("ELSE");a.statementConnection_=d&&d.connection.targetConnection;break;default:throw TypeError("Unknown block type: "+a.type);}a=a.nextConnection&&a.nextConnection.targetBlock()}},rebuildShape_:function(){var a=[null],b=[null],c=null;this.getInput("ELSE")&&(c=this.getInput("ELSE").connection.targetConnection);for(var d=1;this.getInput("IF"+d);){var e=this.getInput("IF"+d),f=this.getInput("DO"+ +d);a.push(e.connection.targetConnection);b.push(f.connection.targetConnection);d++}this.updateShape_();this.reconnectChildBlocks_(a,b,c)},updateShape_:function(){this.getInput("ELSE")&&this.removeInput("ELSE");for(var a=1;this.getInput("IF"+a);)this.removeInput("IF"+a),this.removeInput("DO"+a),a++;for(a=1;a<=this.elseifCount_;a++)this.appendValueInput("IF"+a).setCheck("Boolean").appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSEIF),this.appendStatementInput("DO"+a).appendField(Blockly.Msg.CONTROLS_IF_MSG_THEN); +this.elseCount_&&this.appendStatementInput("ELSE").appendField(Blockly.Msg.CONTROLS_IF_MSG_ELSE)},reconnectChildBlocks_:function(a,b,c){for(var d=1;d<=this.elseifCount_;d++)Blockly.Mutator.reconnect(a[d],this,"IF"+d),Blockly.Mutator.reconnect(b[d],this,"DO"+d);Blockly.Mutator.reconnect(c,this,"ELSE")}};Blockly.Extensions.registerMutator("controls_if_mutator",Blockly.Constants.Logic.CONTROLS_IF_MUTATOR_MIXIN,null,["controls_if_elseif","controls_if_else"]); +Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION=function(){this.setTooltip(function(){if(this.elseifCount_||this.elseCount_){if(!this.elseifCount_&&this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_2;if(this.elseifCount_&&!this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_3;if(this.elseifCount_&&this.elseCount_)return Blockly.Msg.CONTROLS_IF_TOOLTIP_4}else return Blockly.Msg.CONTROLS_IF_TOOLTIP_1;return""}.bind(this))};Blockly.Extensions.register("controls_if_tooltip",Blockly.Constants.Logic.CONTROLS_IF_TOOLTIP_EXTENSION); +Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN={onchange:function(a){this.prevBlocks_||(this.prevBlocks_=[null,null]);var b=this.getInputTargetBlock("A"),c=this.getInputTargetBlock("B");b&&c&&!b.outputConnection.checkType(c.outputConnection)&&(Blockly.Events.setGroup(a.group),a=this.prevBlocks_[0],a!==b&&(b.unplug(),!a||a.isDisposed()||a.isShadow()||this.getInput("A").connection.connect(a.outputConnection)),b=this.prevBlocks_[1],b!==c&&(c.unplug(),!b||b.isDisposed()||b.isShadow()||this.getInput("B").connection.connect(b.outputConnection)), +this.bumpNeighbours(),Blockly.Events.setGroup(!1));this.prevBlocks_[0]=this.getInputTargetBlock("A");this.prevBlocks_[1]=this.getInputTargetBlock("B")}};Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION=function(){this.mixin(Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN)};Blockly.Extensions.register("logic_compare",Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION); +Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN={prevParentConnection_:null,onchange:function(a){var b=this.getInputTargetBlock("THEN"),c=this.getInputTargetBlock("ELSE"),d=this.outputConnection.targetConnection;if((b||c)&&d)for(var e=0;2>e;e++){var f=1==e?b:c;f&&!f.outputConnection.checkType(d)&&(Blockly.Events.setGroup(a.group),d===this.prevParentConnection_?(this.unplug(),d.getSourceBlock().bumpNeighbours()):(f.unplug(),f.bumpNeighbours()),Blockly.Events.setGroup(!1))}this.prevParentConnection_= +d}};Blockly.Extensions.registerMixin("logic_ternary",Blockly.Constants.Logic.LOGIC_TERNARY_ONCHANGE_MIXIN);Blockly.Blocks.loops={};Blockly.Constants.Loops={};Blockly.Constants.Loops.HUE=120; +Blockly.defineBlocksWithJsonArray([{type:"controls_repeat_ext",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"input_value",name:"TIMES",check:"Number"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_repeat",message0:"%{BKY_CONTROLS_REPEAT_TITLE}",args0:[{type:"field_number",name:"TIMES",value:10, +min:0,precision:1}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",tooltip:"%{BKY_CONTROLS_REPEAT_TOOLTIP}",helpUrl:"%{BKY_CONTROLS_REPEAT_HELPURL}"},{type:"controls_whileUntil",message0:"%1 %2",args0:[{type:"field_dropdown",name:"MODE",options:[["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_WHILE}","WHILE"],["%{BKY_CONTROLS_WHILEUNTIL_OPERATOR_UNTIL}","UNTIL"]]},{type:"input_value",name:"BOOL",check:"Boolean"}], +message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_WHILEUNTIL_HELPURL}",extensions:["controls_whileUntil_tooltip"]},{type:"controls_for",message0:"%{BKY_CONTROLS_FOR_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"FROM",check:"Number",align:"RIGHT"},{type:"input_value",name:"TO",check:"Number",align:"RIGHT"},{type:"input_value",name:"BY", +check:"Number",align:"RIGHT"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1",args1:[{type:"input_statement",name:"DO"}],inputsInline:!0,previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FOR_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_for_tooltip"]},{type:"controls_forEach",message0:"%{BKY_CONTROLS_FOREACH_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:null},{type:"input_value",name:"LIST",check:"Array"}],message1:"%{BKY_CONTROLS_REPEAT_INPUT_DO} %1", +args1:[{type:"input_statement",name:"DO"}],previousStatement:null,nextStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FOREACH_HELPURL}",extensions:["contextMenu_newGetVariableBlock","controls_forEach_tooltip"]},{type:"controls_flow_statements",message0:"%1",args0:[{type:"field_dropdown",name:"FLOW",options:[["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK}","BREAK"],["%{BKY_CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE}","CONTINUE"]]}],previousStatement:null,style:"loop_blocks",helpUrl:"%{BKY_CONTROLS_FLOW_STATEMENTS_HELPURL}", +extensions:["controls_flow_tooltip","controls_flow_in_loop_check"]}]);Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS={WHILE:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_WHILE}",UNTIL:"%{BKY_CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL}"};Blockly.Extensions.register("controls_whileUntil_tooltip",Blockly.Extensions.buildTooltipForDropdown("MODE",Blockly.Constants.Loops.WHILE_UNTIL_TOOLTIPS));Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS={BREAK:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK}",CONTINUE:"%{BKY_CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE}"}; +Blockly.Extensions.register("controls_flow_tooltip",Blockly.Extensions.buildTooltipForDropdown("FLOW",Blockly.Constants.Loops.BREAK_CONTINUE_TOOLTIPS)); +Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN={customContextMenu:function(a){if(!this.isInFlyout){var b=this.getField("VAR").getVariable(),c=b.name;if(!this.isCollapsed()&&null!=c){var d={enabled:!0};d.text=Blockly.Msg.VARIABLES_SET_CREATE_GET.replace("%1",c);b=Blockly.Variables.generateVariableFieldDom(b);c=Blockly.utils.xml.createElement("block");c.setAttribute("type","variables_get");c.appendChild(b);d.callback=Blockly.ContextMenu.callbackFactory(this,c);a.push(d)}}}}; +Blockly.Extensions.registerMixin("contextMenu_newGetVariableBlock",Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN);Blockly.Extensions.register("controls_for_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_CONTROLS_FOR_TOOLTIP}","VAR"));Blockly.Extensions.register("controls_forEach_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_CONTROLS_FOREACH_TOOLTIP}","VAR")); +Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN={LOOP_TYPES:["controls_repeat","controls_repeat_ext","controls_forEach","controls_for","controls_whileUntil"],suppressPrefixSuffix:!0,getSurroundLoop:function(a){do{if(-1!=Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.LOOP_TYPES.indexOf(a.type))return a;a=a.getSurroundParent()}while(a);return null},onchange:function(a){if(this.workspace.isDragging&&!this.workspace.isDragging()&&a.type==Blockly.Events.BLOCK_MOVE&&a.blockId==this.id){var b= +Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN.getSurroundLoop(this);this.setWarningText(b?null:Blockly.Msg.CONTROLS_FLOW_STATEMENTS_WARNING);if(!this.isInFlyout){var c=Blockly.Events.getGroup();Blockly.Events.setGroup(a.group);this.setEnabled(b);Blockly.Events.setGroup(c)}}}};Blockly.Extensions.registerMixin("controls_flow_in_loop_check",Blockly.Constants.Loops.CONTROL_FLOW_IN_LOOP_CHECK_MIXIN);Blockly.Blocks.math={};Blockly.Constants.Math={};Blockly.Constants.Math.HUE=230; +Blockly.defineBlocksWithJsonArray([{type:"math_number",message0:"%1",args0:[{type:"field_number",name:"NUM",value:0}],output:"Number",helpUrl:"%{BKY_MATH_NUMBER_HELPURL}",style:"math_blocks",tooltip:"%{BKY_MATH_NUMBER_TOOLTIP}",extensions:["parent_tooltip_when_inline"]},{type:"math_arithmetic",message0:"%1 %2 %3",args0:[{type:"input_value",name:"A",check:"Number"},{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ADDITION_SYMBOL}","ADD"],["%{BKY_MATH_SUBTRACTION_SYMBOL}","MINUS"],["%{BKY_MATH_MULTIPLICATION_SYMBOL}", +"MULTIPLY"],["%{BKY_MATH_DIVISION_SYMBOL}","DIVIDE"],["%{BKY_MATH_POWER_SYMBOL}","POWER"]]},{type:"input_value",name:"B",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ARITHMETIC_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_single",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_SINGLE_OP_ROOT}","ROOT"],["%{BKY_MATH_SINGLE_OP_ABSOLUTE}","ABS"],["-","NEG"],["ln","LN"],["log10","LOG10"],["e^","EXP"],["10^","POW10"]]}, +{type:"input_value",name:"NUM",check:"Number"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_SINGLE_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_trig",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_TRIG_SIN}","SIN"],["%{BKY_MATH_TRIG_COS}","COS"],["%{BKY_MATH_TRIG_TAN}","TAN"],["%{BKY_MATH_TRIG_ASIN}","ASIN"],["%{BKY_MATH_TRIG_ACOS}","ACOS"],["%{BKY_MATH_TRIG_ATAN}","ATAN"]]},{type:"input_value",name:"NUM",check:"Number"}],output:"Number",style:"math_blocks", +helpUrl:"%{BKY_MATH_TRIG_HELPURL}",extensions:["math_op_tooltip"]},{type:"math_constant",message0:"%1",args0:[{type:"field_dropdown",name:"CONSTANT",options:[["\u03c0","PI"],["e","E"],["\u03c6","GOLDEN_RATIO"],["sqrt(2)","SQRT2"],["sqrt(\u00bd)","SQRT1_2"],["\u221e","INFINITY"]]}],output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_CONSTANT_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTANT_HELPURL}"},{type:"math_number_property",message0:"%1 %2",args0:[{type:"input_value",name:"NUMBER_TO_CHECK",check:"Number"}, +{type:"field_dropdown",name:"PROPERTY",options:[["%{BKY_MATH_IS_EVEN}","EVEN"],["%{BKY_MATH_IS_ODD}","ODD"],["%{BKY_MATH_IS_PRIME}","PRIME"],["%{BKY_MATH_IS_WHOLE}","WHOLE"],["%{BKY_MATH_IS_POSITIVE}","POSITIVE"],["%{BKY_MATH_IS_NEGATIVE}","NEGATIVE"],["%{BKY_MATH_IS_DIVISIBLE_BY}","DIVISIBLE_BY"]]}],inputsInline:!0,output:"Boolean",style:"math_blocks",tooltip:"%{BKY_MATH_IS_TOOLTIP}",mutator:"math_is_divisibleby_mutator"},{type:"math_change",message0:"%{BKY_MATH_CHANGE_TITLE}",args0:[{type:"field_variable", +name:"VAR",variable:"%{BKY_MATH_CHANGE_TITLE_ITEM}"},{type:"input_value",name:"DELTA",check:"Number"}],previousStatement:null,nextStatement:null,style:"variable_blocks",helpUrl:"%{BKY_MATH_CHANGE_HELPURL}",extensions:["math_change_tooltip"]},{type:"math_round",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ROUND_OPERATOR_ROUND}","ROUND"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDUP}","ROUNDUP"],["%{BKY_MATH_ROUND_OPERATOR_ROUNDDOWN}","ROUNDDOWN"]]},{type:"input_value",name:"NUM", +check:"Number"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ROUND_HELPURL}",tooltip:"%{BKY_MATH_ROUND_TOOLTIP}"},{type:"math_on_list",message0:"%1 %2",args0:[{type:"field_dropdown",name:"OP",options:[["%{BKY_MATH_ONLIST_OPERATOR_SUM}","SUM"],["%{BKY_MATH_ONLIST_OPERATOR_MIN}","MIN"],["%{BKY_MATH_ONLIST_OPERATOR_MAX}","MAX"],["%{BKY_MATH_ONLIST_OPERATOR_AVERAGE}","AVERAGE"],["%{BKY_MATH_ONLIST_OPERATOR_MEDIAN}","MEDIAN"],["%{BKY_MATH_ONLIST_OPERATOR_MODE}","MODE"],["%{BKY_MATH_ONLIST_OPERATOR_STD_DEV}", +"STD_DEV"],["%{BKY_MATH_ONLIST_OPERATOR_RANDOM}","RANDOM"]]},{type:"input_value",name:"LIST",check:"Array"}],output:"Number",style:"math_blocks",helpUrl:"%{BKY_MATH_ONLIST_HELPURL}",mutator:"math_modes_of_list_mutator",extensions:["math_op_tooltip"]},{type:"math_modulo",message0:"%{BKY_MATH_MODULO_TITLE}",args0:[{type:"input_value",name:"DIVIDEND",check:"Number"},{type:"input_value",name:"DIVISOR",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_MODULO_TOOLTIP}", +helpUrl:"%{BKY_MATH_MODULO_HELPURL}"},{type:"math_constrain",message0:"%{BKY_MATH_CONSTRAIN_TITLE}",args0:[{type:"input_value",name:"VALUE",check:"Number"},{type:"input_value",name:"LOW",check:"Number"},{type:"input_value",name:"HIGH",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_CONSTRAIN_TOOLTIP}",helpUrl:"%{BKY_MATH_CONSTRAIN_HELPURL}"},{type:"math_random_int",message0:"%{BKY_MATH_RANDOM_INT_TITLE}",args0:[{type:"input_value",name:"FROM",check:"Number"}, +{type:"input_value",name:"TO",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_RANDOM_INT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_INT_HELPURL}"},{type:"math_random_float",message0:"%{BKY_MATH_RANDOM_FLOAT_TITLE_RANDOM}",output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_RANDOM_FLOAT_TOOLTIP}",helpUrl:"%{BKY_MATH_RANDOM_FLOAT_HELPURL}"},{type:"math_atan2",message0:"%{BKY_MATH_ATAN2_TITLE}",args0:[{type:"input_value",name:"X",check:"Number"},{type:"input_value", +name:"Y",check:"Number"}],inputsInline:!0,output:"Number",style:"math_blocks",tooltip:"%{BKY_MATH_ATAN2_TOOLTIP}",helpUrl:"%{BKY_MATH_ATAN2_HELPURL}"}]); +Blockly.Constants.Math.TOOLTIPS_BY_OP={ADD:"%{BKY_MATH_ARITHMETIC_TOOLTIP_ADD}",MINUS:"%{BKY_MATH_ARITHMETIC_TOOLTIP_MINUS}",MULTIPLY:"%{BKY_MATH_ARITHMETIC_TOOLTIP_MULTIPLY}",DIVIDE:"%{BKY_MATH_ARITHMETIC_TOOLTIP_DIVIDE}",POWER:"%{BKY_MATH_ARITHMETIC_TOOLTIP_POWER}",ROOT:"%{BKY_MATH_SINGLE_TOOLTIP_ROOT}",ABS:"%{BKY_MATH_SINGLE_TOOLTIP_ABS}",NEG:"%{BKY_MATH_SINGLE_TOOLTIP_NEG}",LN:"%{BKY_MATH_SINGLE_TOOLTIP_LN}",LOG10:"%{BKY_MATH_SINGLE_TOOLTIP_LOG10}",EXP:"%{BKY_MATH_SINGLE_TOOLTIP_EXP}",POW10:"%{BKY_MATH_SINGLE_TOOLTIP_POW10}", +SIN:"%{BKY_MATH_TRIG_TOOLTIP_SIN}",COS:"%{BKY_MATH_TRIG_TOOLTIP_COS}",TAN:"%{BKY_MATH_TRIG_TOOLTIP_TAN}",ASIN:"%{BKY_MATH_TRIG_TOOLTIP_ASIN}",ACOS:"%{BKY_MATH_TRIG_TOOLTIP_ACOS}",ATAN:"%{BKY_MATH_TRIG_TOOLTIP_ATAN}",SUM:"%{BKY_MATH_ONLIST_TOOLTIP_SUM}",MIN:"%{BKY_MATH_ONLIST_TOOLTIP_MIN}",MAX:"%{BKY_MATH_ONLIST_TOOLTIP_MAX}",AVERAGE:"%{BKY_MATH_ONLIST_TOOLTIP_AVERAGE}",MEDIAN:"%{BKY_MATH_ONLIST_TOOLTIP_MEDIAN}",MODE:"%{BKY_MATH_ONLIST_TOOLTIP_MODE}",STD_DEV:"%{BKY_MATH_ONLIST_TOOLTIP_STD_DEV}",RANDOM:"%{BKY_MATH_ONLIST_TOOLTIP_RANDOM}"}; +Blockly.Extensions.register("math_op_tooltip",Blockly.Extensions.buildTooltipForDropdown("OP",Blockly.Constants.Math.TOOLTIPS_BY_OP)); +Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN={mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation"),b="DIVISIBLE_BY"==this.getFieldValue("PROPERTY");a.setAttribute("divisor_input",b);return a},domToMutation:function(a){a="true"==a.getAttribute("divisor_input");this.updateShape_(a)},updateShape_:function(a){var b=this.getInput("DIVISOR");a?b||this.appendValueInput("DIVISOR").setCheck("Number"):b&&this.removeInput("DIVISOR")}}; +Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION=function(){this.getField("PROPERTY").setValidator(function(a){a="DIVISIBLE_BY"==a;this.getSourceBlock().updateShape_(a)})};Blockly.Extensions.registerMutator("math_is_divisibleby_mutator",Blockly.Constants.Math.IS_DIVISIBLEBY_MUTATOR_MIXIN,Blockly.Constants.Math.IS_DIVISIBLE_MUTATOR_EXTENSION);Blockly.Extensions.register("math_change_tooltip",Blockly.Extensions.buildTooltipWithFieldText("%{BKY_MATH_CHANGE_TOOLTIP}","VAR")); +Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN={updateType_:function(a){"MODE"==a?this.outputConnection.setCheck("Array"):this.outputConnection.setCheck("Number")},mutationToDom:function(){var a=Blockly.utils.xml.createElement("mutation");a.setAttribute("op",this.getFieldValue("OP"));return a},domToMutation:function(a){this.updateType_(a.getAttribute("op"))}};Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION=function(){this.getField("OP").setValidator(function(a){this.updateType_(a)}.bind(this))}; +Blockly.Extensions.registerMutator("math_modes_of_list_mutator",Blockly.Constants.Math.LIST_MODES_MUTATOR_MIXIN,Blockly.Constants.Math.LIST_MODES_MUTATOR_EXTENSION);Blockly.Blocks.procedures={}; +Blockly.Blocks.procedures_defnoreturn={init:function(){var a=new Blockly.FieldTextInput("",Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));(this.workspace.options.comments||this.workspace.options.parentWorkspace&&this.workspace.options.parentWorkspace.options.comments)&&Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT&&this.setCommentText(Blockly.Msg.PROCEDURES_DEFNORETURN_COMMENT); +this.setStyle("procedure_blocks");this.setTooltip(Blockly.Msg.PROCEDURES_DEFNORETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_DEFNORETURN_HELPURL);this.arguments_=[];this.argumentVarModels_=[];this.setStatements_(!0);this.statementConnection_=null},setStatements_:function(a){this.hasStatements_!==a&&(a?(this.appendStatementInput("STACK").appendField(Blockly.Msg.PROCEDURES_DEFNORETURN_DO),this.getInput("RETURN")&&this.moveInputBefore("STACK","RETURN")):this.removeInput("STACK",!0),this.hasStatements_= +a)},updateParams_:function(){var a="";this.arguments_.length&&(a=Blockly.Msg.PROCEDURES_BEFORE_PARAMS+" "+this.arguments_.join(", "));Blockly.Events.disable();try{this.setFieldValue(a,"PARAMS")}finally{Blockly.Events.enable()}},mutationToDom:function(a){var b=Blockly.utils.xml.createElement("mutation");a&&b.setAttribute("name",this.getFieldValue("NAME"));for(var c=0;cdropdown-arrow \ No newline at end of file diff --git a/clover_blocks/www/blockly/media/handclosed.cur b/clover_blocks/www/blockly/media/handclosed.cur new file mode 100755 index 000000000..4851755a1 Binary files /dev/null and b/clover_blocks/www/blockly/media/handclosed.cur differ diff --git a/clover_blocks/www/blockly/media/handdelete.cur b/clover_blocks/www/blockly/media/handdelete.cur new file mode 100755 index 000000000..170320fc2 Binary files /dev/null and b/clover_blocks/www/blockly/media/handdelete.cur differ diff --git a/clover_blocks/www/blockly/media/handopen.cur b/clover_blocks/www/blockly/media/handopen.cur new file mode 100755 index 000000000..da44588b2 Binary files /dev/null and b/clover_blocks/www/blockly/media/handopen.cur differ diff --git a/clover_blocks/www/blockly/media/pilcrow.png b/clover_blocks/www/blockly/media/pilcrow.png new file mode 100755 index 000000000..f224c3586 Binary files /dev/null and b/clover_blocks/www/blockly/media/pilcrow.png differ diff --git a/clover_blocks/www/blockly/media/quote0.png b/clover_blocks/www/blockly/media/quote0.png new file mode 100755 index 000000000..c7820d607 Binary files /dev/null and b/clover_blocks/www/blockly/media/quote0.png differ diff --git a/clover_blocks/www/blockly/media/quote1.png b/clover_blocks/www/blockly/media/quote1.png new file mode 100755 index 000000000..826583e0a Binary files /dev/null and b/clover_blocks/www/blockly/media/quote1.png differ diff --git a/clover_blocks/www/blockly/media/sprites.png b/clover_blocks/www/blockly/media/sprites.png new file mode 100755 index 000000000..20aadb6c4 Binary files /dev/null and b/clover_blocks/www/blockly/media/sprites.png differ diff --git a/clover_blocks/www/blockly/media/sprites.svg b/clover_blocks/www/blockly/media/sprites.svg new file mode 100755 index 000000000..3f09ef3a4 --- /dev/null +++ b/clover_blocks/www/blockly/media/sprites.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/clover_blocks/www/blockly/msg/js/en.js b/clover_blocks/www/blockly/msg/js/en.js new file mode 100755 index 000000000..307dc19e7 --- /dev/null +++ b/clover_blocks/www/blockly/msg/js/en.js @@ -0,0 +1,434 @@ +// This file was automatically generated. Do not modify. + +'use strict'; + +Blockly.Msg["ADD_COMMENT"] = "Add Comment"; +Blockly.Msg["CANNOT_DELETE_VARIABLE_PROCEDURE"] = "Can't delete the variable '%1' because it's part of the definition of the function '%2'"; +Blockly.Msg["CHANGE_VALUE_TITLE"] = "Change value:"; +Blockly.Msg["CLEAN_UP"] = "Clean up Blocks"; +Blockly.Msg["COLLAPSED_WARNINGS_WARNING"] = "Collapsed blocks contain warnings."; +Blockly.Msg["COLLAPSE_ALL"] = "Collapse Blocks"; +Blockly.Msg["COLLAPSE_BLOCK"] = "Collapse Block"; +Blockly.Msg["COLOUR_BLEND_COLOUR1"] = "colour 1"; +Blockly.Msg["COLOUR_BLEND_COLOUR2"] = "colour 2"; +Blockly.Msg["COLOUR_BLEND_HELPURL"] = "https://meyerweb.com/eric/tools/color-blend/#:::rgbp"; +Blockly.Msg["COLOUR_BLEND_RATIO"] = "ratio"; +Blockly.Msg["COLOUR_BLEND_TITLE"] = "blend"; +Blockly.Msg["COLOUR_BLEND_TOOLTIP"] = "Blends two colours together with a given ratio (0.0 - 1.0)."; +Blockly.Msg["COLOUR_PICKER_HELPURL"] = "https://en.wikipedia.org/wiki/Color"; +Blockly.Msg["COLOUR_PICKER_TOOLTIP"] = "Choose a colour from the palette."; +Blockly.Msg["COLOUR_RANDOM_HELPURL"] = "http://randomcolour.com"; +Blockly.Msg["COLOUR_RANDOM_TITLE"] = "random colour"; +Blockly.Msg["COLOUR_RANDOM_TOOLTIP"] = "Choose a colour at random."; +Blockly.Msg["COLOUR_RGB_BLUE"] = "blue"; +Blockly.Msg["COLOUR_RGB_GREEN"] = "green"; +Blockly.Msg["COLOUR_RGB_HELPURL"] = "https://www.december.com/html/spec/colorpercompact.html"; +Blockly.Msg["COLOUR_RGB_RED"] = "red"; +Blockly.Msg["COLOUR_RGB_TITLE"] = "colour with"; +Blockly.Msg["COLOUR_RGB_TOOLTIP"] = "Create a colour with the specified amount of red, green, and blue. All values must be between 0 and 100."; +Blockly.Msg["CONTROLS_FLOW_STATEMENTS_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#loop-termination-blocks"; +Blockly.Msg["CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK"] = "break out of loop"; +Blockly.Msg["CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE"] = "continue with next iteration of loop"; +Blockly.Msg["CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK"] = "Break out of the containing loop."; +Blockly.Msg["CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE"] = "Skip the rest of this loop, and continue with the next iteration."; +Blockly.Msg["CONTROLS_FLOW_STATEMENTS_WARNING"] = "Warning: This block may only be used within a loop."; +Blockly.Msg["CONTROLS_FOREACH_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#for-each"; +Blockly.Msg["CONTROLS_FOREACH_TITLE"] = "for each item %1 in list %2"; +Blockly.Msg["CONTROLS_FOREACH_TOOLTIP"] = "For each item in a list, set the variable '%1' to the item, and then do some statements."; +Blockly.Msg["CONTROLS_FOR_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#count-with"; +Blockly.Msg["CONTROLS_FOR_TITLE"] = "count with %1 from %2 to %3 by %4"; +Blockly.Msg["CONTROLS_FOR_TOOLTIP"] = "Have the variable '%1' take on the values from the start number to the end number, counting by the specified interval, and do the specified blocks."; +Blockly.Msg["CONTROLS_IF_ELSEIF_TOOLTIP"] = "Add a condition to the if block."; +Blockly.Msg["CONTROLS_IF_ELSE_TOOLTIP"] = "Add a final, catch-all condition to the if block."; +Blockly.Msg["CONTROLS_IF_HELPURL"] = "https://github.com/google/blockly/wiki/IfElse"; +Blockly.Msg["CONTROLS_IF_IF_TOOLTIP"] = "Add, remove, or reorder sections to reconfigure this if block."; +Blockly.Msg["CONTROLS_IF_MSG_ELSE"] = "else"; +Blockly.Msg["CONTROLS_IF_MSG_ELSEIF"] = "else if"; +Blockly.Msg["CONTROLS_IF_MSG_IF"] = "if"; +Blockly.Msg["CONTROLS_IF_TOOLTIP_1"] = "If a value is true, then do some statements."; +Blockly.Msg["CONTROLS_IF_TOOLTIP_2"] = "If a value is true, then do the first block of statements. Otherwise, do the second block of statements."; +Blockly.Msg["CONTROLS_IF_TOOLTIP_3"] = "If the first value is true, then do the first block of statements. Otherwise, if the second value is true, do the second block of statements."; +Blockly.Msg["CONTROLS_IF_TOOLTIP_4"] = "If the first value is true, then do the first block of statements. Otherwise, if the second value is true, do the second block of statements. If none of the values are true, do the last block of statements."; +Blockly.Msg["CONTROLS_REPEAT_HELPURL"] = "https://en.wikipedia.org/wiki/For_loop"; +Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"] = "do"; +Blockly.Msg["CONTROLS_REPEAT_TITLE"] = "repeat %1 times"; +Blockly.Msg["CONTROLS_REPEAT_TOOLTIP"] = "Do some statements several times."; +Blockly.Msg["CONTROLS_WHILEUNTIL_HELPURL"] = "https://github.com/google/blockly/wiki/Loops#repeat"; +Blockly.Msg["CONTROLS_WHILEUNTIL_OPERATOR_UNTIL"] = "repeat until"; +Blockly.Msg["CONTROLS_WHILEUNTIL_OPERATOR_WHILE"] = "repeat while"; +Blockly.Msg["CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL"] = "While a value is false, then do some statements."; +Blockly.Msg["CONTROLS_WHILEUNTIL_TOOLTIP_WHILE"] = "While a value is true, then do some statements."; +Blockly.Msg["DELETE_ALL_BLOCKS"] = "Delete all %1 blocks?"; +Blockly.Msg["DELETE_BLOCK"] = "Delete Block"; +Blockly.Msg["DELETE_VARIABLE"] = "Delete the '%1' variable"; +Blockly.Msg["DELETE_VARIABLE_CONFIRMATION"] = "Delete %1 uses of the '%2' variable?"; +Blockly.Msg["DELETE_X_BLOCKS"] = "Delete %1 Blocks"; +Blockly.Msg["DISABLE_BLOCK"] = "Disable Block"; +Blockly.Msg["DUPLICATE_BLOCK"] = "Duplicate"; +Blockly.Msg["DUPLICATE_COMMENT"] = "Duplicate Comment"; +Blockly.Msg["ENABLE_BLOCK"] = "Enable Block"; +Blockly.Msg["EXPAND_ALL"] = "Expand Blocks"; +Blockly.Msg["EXPAND_BLOCK"] = "Expand Block"; +Blockly.Msg["EXTERNAL_INPUTS"] = "External Inputs"; +Blockly.Msg["HELP"] = "Help"; +Blockly.Msg["INLINE_INPUTS"] = "Inline Inputs"; +Blockly.Msg["IOS_CANCEL"] = "Cancel"; +Blockly.Msg["IOS_ERROR"] = "Error"; +Blockly.Msg["IOS_OK"] = "OK"; +Blockly.Msg["IOS_PROCEDURES_ADD_INPUT"] = "+ Add Input"; +Blockly.Msg["IOS_PROCEDURES_ALLOW_STATEMENTS"] = "Allow statements"; +Blockly.Msg["IOS_PROCEDURES_DUPLICATE_INPUTS_ERROR"] = "This function has duplicate inputs."; +Blockly.Msg["IOS_PROCEDURES_INPUTS"] = "INPUTS"; +Blockly.Msg["IOS_VARIABLES_ADD_BUTTON"] = "Add"; +Blockly.Msg["IOS_VARIABLES_ADD_VARIABLE"] = "+ Add Variable"; +Blockly.Msg["IOS_VARIABLES_DELETE_BUTTON"] = "Delete"; +Blockly.Msg["IOS_VARIABLES_EMPTY_NAME_ERROR"] = "You can't use an empty variable name."; +Blockly.Msg["IOS_VARIABLES_RENAME_BUTTON"] = "Rename"; +Blockly.Msg["IOS_VARIABLES_VARIABLE_NAME"] = "Variable name"; +Blockly.Msg["LISTS_CREATE_EMPTY_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#create-empty-list"; +Blockly.Msg["LISTS_CREATE_EMPTY_TITLE"] = "create empty list"; +Blockly.Msg["LISTS_CREATE_EMPTY_TOOLTIP"] = "Returns a list, of length 0, containing no data records"; +Blockly.Msg["LISTS_CREATE_WITH_CONTAINER_TITLE_ADD"] = "list"; +Blockly.Msg["LISTS_CREATE_WITH_CONTAINER_TOOLTIP"] = "Add, remove, or reorder sections to reconfigure this list block."; +Blockly.Msg["LISTS_CREATE_WITH_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#create-list-with"; +Blockly.Msg["LISTS_CREATE_WITH_INPUT_WITH"] = "create list with"; +Blockly.Msg["LISTS_CREATE_WITH_ITEM_TOOLTIP"] = "Add an item to the list."; +Blockly.Msg["LISTS_CREATE_WITH_TOOLTIP"] = "Create a list with any number of items."; +Blockly.Msg["LISTS_GET_INDEX_FIRST"] = "first"; +Blockly.Msg["LISTS_GET_INDEX_FROM_END"] = "# from end"; +Blockly.Msg["LISTS_GET_INDEX_FROM_START"] = "#"; +Blockly.Msg["LISTS_GET_INDEX_GET"] = "get"; +Blockly.Msg["LISTS_GET_INDEX_GET_REMOVE"] = "get and remove"; +Blockly.Msg["LISTS_GET_INDEX_LAST"] = "last"; +Blockly.Msg["LISTS_GET_INDEX_RANDOM"] = "random"; +Blockly.Msg["LISTS_GET_INDEX_REMOVE"] = "remove"; +Blockly.Msg["LISTS_GET_INDEX_TAIL"] = ""; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_FIRST"] = "Returns the first item in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_FROM"] = "Returns the item at the specified position in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_LAST"] = "Returns the last item in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_RANDOM"] = "Returns a random item in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST"] = "Removes and returns the first item in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM"] = "Removes and returns the item at the specified position in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST"] = "Removes and returns the last item in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM"] = "Removes and returns a random item in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST"] = "Removes the first item in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM"] = "Removes the item at the specified position in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST"] = "Removes the last item in a list."; +Blockly.Msg["LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM"] = "Removes a random item in a list."; +Blockly.Msg["LISTS_GET_SUBLIST_END_FROM_END"] = "to # from end"; +Blockly.Msg["LISTS_GET_SUBLIST_END_FROM_START"] = "to #"; +Blockly.Msg["LISTS_GET_SUBLIST_END_LAST"] = "to last"; +Blockly.Msg["LISTS_GET_SUBLIST_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#getting-a-sublist"; +Blockly.Msg["LISTS_GET_SUBLIST_START_FIRST"] = "get sub-list from first"; +Blockly.Msg["LISTS_GET_SUBLIST_START_FROM_END"] = "get sub-list from # from end"; +Blockly.Msg["LISTS_GET_SUBLIST_START_FROM_START"] = "get sub-list from #"; +Blockly.Msg["LISTS_GET_SUBLIST_TAIL"] = ""; +Blockly.Msg["LISTS_GET_SUBLIST_TOOLTIP"] = "Creates a copy of the specified portion of a list."; +Blockly.Msg["LISTS_INDEX_FROM_END_TOOLTIP"] = "%1 is the last item."; +Blockly.Msg["LISTS_INDEX_FROM_START_TOOLTIP"] = "%1 is the first item."; +Blockly.Msg["LISTS_INDEX_OF_FIRST"] = "find first occurrence of item"; +Blockly.Msg["LISTS_INDEX_OF_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#getting-items-from-a-list"; +Blockly.Msg["LISTS_INDEX_OF_LAST"] = "find last occurrence of item"; +Blockly.Msg["LISTS_INDEX_OF_TOOLTIP"] = "Returns the index of the first/last occurrence of the item in the list. Returns %1 if item is not found."; +Blockly.Msg["LISTS_INLIST"] = "in list"; +Blockly.Msg["LISTS_ISEMPTY_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#is-empty"; +Blockly.Msg["LISTS_ISEMPTY_TITLE"] = "%1 is empty"; +Blockly.Msg["LISTS_ISEMPTY_TOOLTIP"] = "Returns true if the list is empty."; +Blockly.Msg["LISTS_LENGTH_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#length-of"; +Blockly.Msg["LISTS_LENGTH_TITLE"] = "length of %1"; +Blockly.Msg["LISTS_LENGTH_TOOLTIP"] = "Returns the length of a list."; +Blockly.Msg["LISTS_REPEAT_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#create-list-with"; +Blockly.Msg["LISTS_REPEAT_TITLE"] = "create list with item %1 repeated %2 times"; +Blockly.Msg["LISTS_REPEAT_TOOLTIP"] = "Creates a list consisting of the given value repeated the specified number of times."; +Blockly.Msg["LISTS_REVERSE_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#reversing-a-list"; +Blockly.Msg["LISTS_REVERSE_MESSAGE0"] = "reverse %1"; +Blockly.Msg["LISTS_REVERSE_TOOLTIP"] = "Reverse a copy of a list."; +Blockly.Msg["LISTS_SET_INDEX_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#in-list--set"; +Blockly.Msg["LISTS_SET_INDEX_INPUT_TO"] = "as"; +Blockly.Msg["LISTS_SET_INDEX_INSERT"] = "insert at"; +Blockly.Msg["LISTS_SET_INDEX_SET"] = "set"; +Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST"] = "Inserts the item at the start of a list."; +Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_FROM"] = "Inserts the item at the specified position in a list."; +Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_LAST"] = "Append the item to the end of a list."; +Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM"] = "Inserts the item randomly in a list."; +Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_FIRST"] = "Sets the first item in a list."; +Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_FROM"] = "Sets the item at the specified position in a list."; +Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_LAST"] = "Sets the last item in a list."; +Blockly.Msg["LISTS_SET_INDEX_TOOLTIP_SET_RANDOM"] = "Sets a random item in a list."; +Blockly.Msg["LISTS_SORT_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#sorting-a-list"; +Blockly.Msg["LISTS_SORT_ORDER_ASCENDING"] = "ascending"; +Blockly.Msg["LISTS_SORT_ORDER_DESCENDING"] = "descending"; +Blockly.Msg["LISTS_SORT_TITLE"] = "sort %1 %2 %3"; +Blockly.Msg["LISTS_SORT_TOOLTIP"] = "Sort a copy of a list."; +Blockly.Msg["LISTS_SORT_TYPE_IGNORECASE"] = "alphabetic, ignore case"; +Blockly.Msg["LISTS_SORT_TYPE_NUMERIC"] = "numeric"; +Blockly.Msg["LISTS_SORT_TYPE_TEXT"] = "alphabetic"; +Blockly.Msg["LISTS_SPLIT_HELPURL"] = "https://github.com/google/blockly/wiki/Lists#splitting-strings-and-joining-lists"; +Blockly.Msg["LISTS_SPLIT_LIST_FROM_TEXT"] = "make list from text"; +Blockly.Msg["LISTS_SPLIT_TEXT_FROM_LIST"] = "make text from list"; +Blockly.Msg["LISTS_SPLIT_TOOLTIP_JOIN"] = "Join a list of texts into one text, separated by a delimiter."; +Blockly.Msg["LISTS_SPLIT_TOOLTIP_SPLIT"] = "Split text into a list of texts, breaking at each delimiter."; +Blockly.Msg["LISTS_SPLIT_WITH_DELIMITER"] = "with delimiter"; +Blockly.Msg["LOGIC_BOOLEAN_FALSE"] = "false"; +Blockly.Msg["LOGIC_BOOLEAN_HELPURL"] = "https://github.com/google/blockly/wiki/Logic#values"; +Blockly.Msg["LOGIC_BOOLEAN_TOOLTIP"] = "Returns either true or false."; +Blockly.Msg["LOGIC_BOOLEAN_TRUE"] = "true"; +Blockly.Msg["LOGIC_COMPARE_HELPURL"] = "https://en.wikipedia.org/wiki/Inequality_(mathematics)"; +Blockly.Msg["LOGIC_COMPARE_TOOLTIP_EQ"] = "Return true if both inputs equal each other."; +Blockly.Msg["LOGIC_COMPARE_TOOLTIP_GT"] = "Return true if the first input is greater than the second input."; +Blockly.Msg["LOGIC_COMPARE_TOOLTIP_GTE"] = "Return true if the first input is greater than or equal to the second input."; +Blockly.Msg["LOGIC_COMPARE_TOOLTIP_LT"] = "Return true if the first input is smaller than the second input."; +Blockly.Msg["LOGIC_COMPARE_TOOLTIP_LTE"] = "Return true if the first input is smaller than or equal to the second input."; +Blockly.Msg["LOGIC_COMPARE_TOOLTIP_NEQ"] = "Return true if both inputs are not equal to each other."; +Blockly.Msg["LOGIC_NEGATE_HELPURL"] = "https://github.com/google/blockly/wiki/Logic#not"; +Blockly.Msg["LOGIC_NEGATE_TITLE"] = "not %1"; +Blockly.Msg["LOGIC_NEGATE_TOOLTIP"] = "Returns true if the input is false. Returns false if the input is true."; +Blockly.Msg["LOGIC_NULL"] = "null"; +Blockly.Msg["LOGIC_NULL_HELPURL"] = "https://en.wikipedia.org/wiki/Nullable_type"; +Blockly.Msg["LOGIC_NULL_TOOLTIP"] = "Returns null."; +Blockly.Msg["LOGIC_OPERATION_AND"] = "and"; +Blockly.Msg["LOGIC_OPERATION_HELPURL"] = "https://github.com/google/blockly/wiki/Logic#logical-operations"; +Blockly.Msg["LOGIC_OPERATION_OR"] = "or"; +Blockly.Msg["LOGIC_OPERATION_TOOLTIP_AND"] = "Return true if both inputs are true."; +Blockly.Msg["LOGIC_OPERATION_TOOLTIP_OR"] = "Return true if at least one of the inputs is true."; +Blockly.Msg["LOGIC_TERNARY_CONDITION"] = "test"; +Blockly.Msg["LOGIC_TERNARY_HELPURL"] = "https://en.wikipedia.org/wiki/%3F:"; +Blockly.Msg["LOGIC_TERNARY_IF_FALSE"] = "if false"; +Blockly.Msg["LOGIC_TERNARY_IF_TRUE"] = "if true"; +Blockly.Msg["LOGIC_TERNARY_TOOLTIP"] = "Check the condition in 'test'. If the condition is true, returns the 'if true' value; otherwise returns the 'if false' value."; +Blockly.Msg["MATH_ADDITION_SYMBOL"] = "+"; +Blockly.Msg["MATH_ARITHMETIC_HELPURL"] = "https://en.wikipedia.org/wiki/Arithmetic"; +Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_ADD"] = "Return the sum of the two numbers."; +Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_DIVIDE"] = "Return the quotient of the two numbers."; +Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_MINUS"] = "Return the difference of the two numbers."; +Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_MULTIPLY"] = "Return the product of the two numbers."; +Blockly.Msg["MATH_ARITHMETIC_TOOLTIP_POWER"] = "Return the first number raised to the power of the second number."; +Blockly.Msg["MATH_ATAN2_HELPURL"] = "https://en.wikipedia.org/wiki/Atan2"; +Blockly.Msg["MATH_ATAN2_TITLE"] = "atan2 of X:%1 Y:%2"; +Blockly.Msg["MATH_ATAN2_TOOLTIP"] = "Return the arctangent of point (X, Y) in degrees from -180 to 180."; +Blockly.Msg["MATH_CHANGE_HELPURL"] = "https://en.wikipedia.org/wiki/Programming_idiom#Incrementing_a_counter"; +Blockly.Msg["MATH_CHANGE_TITLE"] = "change %1 by %2"; +Blockly.Msg["MATH_CHANGE_TOOLTIP"] = "Add a number to variable '%1'."; +Blockly.Msg["MATH_CONSTANT_HELPURL"] = "https://en.wikipedia.org/wiki/Mathematical_constant"; +Blockly.Msg["MATH_CONSTANT_TOOLTIP"] = "Return one of the common constants: π (3.141…), e (2.718…), φ (1.618…), sqrt(2) (1.414…), sqrt(½) (0.707…), or ∞ (infinity)."; +Blockly.Msg["MATH_CONSTRAIN_HELPURL"] = "https://en.wikipedia.org/wiki/Clamping_(graphics)"; +Blockly.Msg["MATH_CONSTRAIN_TITLE"] = "constrain %1 low %2 high %3"; +Blockly.Msg["MATH_CONSTRAIN_TOOLTIP"] = "Constrain a number to be between the specified limits (inclusive)."; +Blockly.Msg["MATH_DIVISION_SYMBOL"] = "÷"; +Blockly.Msg["MATH_IS_DIVISIBLE_BY"] = "is divisible by"; +Blockly.Msg["MATH_IS_EVEN"] = "is even"; +Blockly.Msg["MATH_IS_NEGATIVE"] = "is negative"; +Blockly.Msg["MATH_IS_ODD"] = "is odd"; +Blockly.Msg["MATH_IS_POSITIVE"] = "is positive"; +Blockly.Msg["MATH_IS_PRIME"] = "is prime"; +Blockly.Msg["MATH_IS_TOOLTIP"] = "Check if a number is an even, odd, prime, whole, positive, negative, or if it is divisible by certain number. Returns true or false."; +Blockly.Msg["MATH_IS_WHOLE"] = "is whole"; +Blockly.Msg["MATH_MODULO_HELPURL"] = "https://en.wikipedia.org/wiki/Modulo_operation"; +Blockly.Msg["MATH_MODULO_TITLE"] = "remainder of %1 ÷ %2"; +Blockly.Msg["MATH_MODULO_TOOLTIP"] = "Return the remainder from dividing the two numbers."; +Blockly.Msg["MATH_MULTIPLICATION_SYMBOL"] = "×"; +Blockly.Msg["MATH_NUMBER_HELPURL"] = "https://en.wikipedia.org/wiki/Number"; +Blockly.Msg["MATH_NUMBER_TOOLTIP"] = "A number."; +Blockly.Msg["MATH_ONLIST_HELPURL"] = ""; +Blockly.Msg["MATH_ONLIST_OPERATOR_AVERAGE"] = "average of list"; +Blockly.Msg["MATH_ONLIST_OPERATOR_MAX"] = "max of list"; +Blockly.Msg["MATH_ONLIST_OPERATOR_MEDIAN"] = "median of list"; +Blockly.Msg["MATH_ONLIST_OPERATOR_MIN"] = "min of list"; +Blockly.Msg["MATH_ONLIST_OPERATOR_MODE"] = "modes of list"; +Blockly.Msg["MATH_ONLIST_OPERATOR_RANDOM"] = "random item of list"; +Blockly.Msg["MATH_ONLIST_OPERATOR_STD_DEV"] = "standard deviation of list"; +Blockly.Msg["MATH_ONLIST_OPERATOR_SUM"] = "sum of list"; +Blockly.Msg["MATH_ONLIST_TOOLTIP_AVERAGE"] = "Return the average (arithmetic mean) of the numeric values in the list."; +Blockly.Msg["MATH_ONLIST_TOOLTIP_MAX"] = "Return the largest number in the list."; +Blockly.Msg["MATH_ONLIST_TOOLTIP_MEDIAN"] = "Return the median number in the list."; +Blockly.Msg["MATH_ONLIST_TOOLTIP_MIN"] = "Return the smallest number in the list."; +Blockly.Msg["MATH_ONLIST_TOOLTIP_MODE"] = "Return a list of the most common item(s) in the list."; +Blockly.Msg["MATH_ONLIST_TOOLTIP_RANDOM"] = "Return a random element from the list."; +Blockly.Msg["MATH_ONLIST_TOOLTIP_STD_DEV"] = "Return the standard deviation of the list."; +Blockly.Msg["MATH_ONLIST_TOOLTIP_SUM"] = "Return the sum of all the numbers in the list."; +Blockly.Msg["MATH_POWER_SYMBOL"] = "^"; +Blockly.Msg["MATH_RANDOM_FLOAT_HELPURL"] = "https://en.wikipedia.org/wiki/Random_number_generation"; +Blockly.Msg["MATH_RANDOM_FLOAT_TITLE_RANDOM"] = "random fraction"; +Blockly.Msg["MATH_RANDOM_FLOAT_TOOLTIP"] = "Return a random fraction between 0.0 (inclusive) and 1.0 (exclusive)."; +Blockly.Msg["MATH_RANDOM_INT_HELPURL"] = "https://en.wikipedia.org/wiki/Random_number_generation"; +Blockly.Msg["MATH_RANDOM_INT_TITLE"] = "random integer from %1 to %2"; +Blockly.Msg["MATH_RANDOM_INT_TOOLTIP"] = "Return a random integer between the two specified limits, inclusive."; +Blockly.Msg["MATH_ROUND_HELPURL"] = "https://en.wikipedia.org/wiki/Rounding"; +Blockly.Msg["MATH_ROUND_OPERATOR_ROUND"] = "round"; +Blockly.Msg["MATH_ROUND_OPERATOR_ROUNDDOWN"] = "round down"; +Blockly.Msg["MATH_ROUND_OPERATOR_ROUNDUP"] = "round up"; +Blockly.Msg["MATH_ROUND_TOOLTIP"] = "Round a number up or down."; +Blockly.Msg["MATH_SINGLE_HELPURL"] = "https://en.wikipedia.org/wiki/Square_root"; +Blockly.Msg["MATH_SINGLE_OP_ABSOLUTE"] = "absolute"; +Blockly.Msg["MATH_SINGLE_OP_ROOT"] = "square root"; +Blockly.Msg["MATH_SINGLE_TOOLTIP_ABS"] = "Return the absolute value of a number."; +Blockly.Msg["MATH_SINGLE_TOOLTIP_EXP"] = "Return e to the power of a number."; +Blockly.Msg["MATH_SINGLE_TOOLTIP_LN"] = "Return the natural logarithm of a number."; +Blockly.Msg["MATH_SINGLE_TOOLTIP_LOG10"] = "Return the base 10 logarithm of a number."; +Blockly.Msg["MATH_SINGLE_TOOLTIP_NEG"] = "Return the negation of a number."; +Blockly.Msg["MATH_SINGLE_TOOLTIP_POW10"] = "Return 10 to the power of a number."; +Blockly.Msg["MATH_SINGLE_TOOLTIP_ROOT"] = "Return the square root of a number."; +Blockly.Msg["MATH_SUBTRACTION_SYMBOL"] = "-"; +Blockly.Msg["MATH_TRIG_ACOS"] = "acos"; +Blockly.Msg["MATH_TRIG_ASIN"] = "asin"; +Blockly.Msg["MATH_TRIG_ATAN"] = "atan"; +Blockly.Msg["MATH_TRIG_COS"] = "cos"; +Blockly.Msg["MATH_TRIG_HELPURL"] = "https://en.wikipedia.org/wiki/Trigonometric_functions"; +Blockly.Msg["MATH_TRIG_SIN"] = "sin"; +Blockly.Msg["MATH_TRIG_TAN"] = "tan"; +Blockly.Msg["MATH_TRIG_TOOLTIP_ACOS"] = "Return the arccosine of a number."; +Blockly.Msg["MATH_TRIG_TOOLTIP_ASIN"] = "Return the arcsine of a number."; +Blockly.Msg["MATH_TRIG_TOOLTIP_ATAN"] = "Return the arctangent of a number."; +Blockly.Msg["MATH_TRIG_TOOLTIP_COS"] = "Return the cosine of a degree (not radian)."; +Blockly.Msg["MATH_TRIG_TOOLTIP_SIN"] = "Return the sine of a degree (not radian)."; +Blockly.Msg["MATH_TRIG_TOOLTIP_TAN"] = "Return the tangent of a degree (not radian)."; +Blockly.Msg["NEW_COLOUR_VARIABLE"] = "Create colour variable..."; +Blockly.Msg["NEW_NUMBER_VARIABLE"] = "Create number variable..."; +Blockly.Msg["NEW_STRING_VARIABLE"] = "Create string variable..."; +Blockly.Msg["NEW_VARIABLE"] = "Create variable..."; +Blockly.Msg["NEW_VARIABLE_TITLE"] = "New variable name:"; +Blockly.Msg["NEW_VARIABLE_TYPE_TITLE"] = "New variable type:"; +Blockly.Msg["ORDINAL_NUMBER_SUFFIX"] = ""; +Blockly.Msg["PROCEDURES_ALLOW_STATEMENTS"] = "allow statements"; +Blockly.Msg["PROCEDURES_BEFORE_PARAMS"] = "with:"; +Blockly.Msg["PROCEDURES_CALLNORETURN_HELPURL"] = "https://en.wikipedia.org/wiki/Subroutine"; +Blockly.Msg["PROCEDURES_CALLNORETURN_TOOLTIP"] = "Run the user-defined function '%1'."; +Blockly.Msg["PROCEDURES_CALLRETURN_HELPURL"] = "https://en.wikipedia.org/wiki/Subroutine"; +Blockly.Msg["PROCEDURES_CALLRETURN_TOOLTIP"] = "Run the user-defined function '%1' and use its output."; +Blockly.Msg["PROCEDURES_CALL_BEFORE_PARAMS"] = "with:"; +Blockly.Msg["PROCEDURES_CREATE_DO"] = "Create '%1'"; +Blockly.Msg["PROCEDURES_DEFNORETURN_COMMENT"] = "Describe this function..."; +Blockly.Msg["PROCEDURES_DEFNORETURN_DO"] = ""; +Blockly.Msg["PROCEDURES_DEFNORETURN_HELPURL"] = "https://en.wikipedia.org/wiki/Subroutine"; +Blockly.Msg["PROCEDURES_DEFNORETURN_PROCEDURE"] = "do something"; +Blockly.Msg["PROCEDURES_DEFNORETURN_TITLE"] = "to"; +Blockly.Msg["PROCEDURES_DEFNORETURN_TOOLTIP"] = "Creates a function with no output."; +Blockly.Msg["PROCEDURES_DEFRETURN_HELPURL"] = "https://en.wikipedia.org/wiki/Subroutine"; +Blockly.Msg["PROCEDURES_DEFRETURN_RETURN"] = "return"; +Blockly.Msg["PROCEDURES_DEFRETURN_TOOLTIP"] = "Creates a function with an output."; +Blockly.Msg["PROCEDURES_DEF_DUPLICATE_WARNING"] = "Warning: This function has duplicate parameters."; +Blockly.Msg["PROCEDURES_HIGHLIGHT_DEF"] = "Highlight function definition"; +Blockly.Msg["PROCEDURES_IFRETURN_HELPURL"] = "http://c2.com/cgi/wiki?GuardClause"; +Blockly.Msg["PROCEDURES_IFRETURN_TOOLTIP"] = "If a value is true, then return a second value."; +Blockly.Msg["PROCEDURES_IFRETURN_WARNING"] = "Warning: This block may be used only within a function definition."; +Blockly.Msg["PROCEDURES_MUTATORARG_TITLE"] = "input name:"; +Blockly.Msg["PROCEDURES_MUTATORARG_TOOLTIP"] = "Add an input to the function."; +Blockly.Msg["PROCEDURES_MUTATORCONTAINER_TITLE"] = "inputs"; +Blockly.Msg["PROCEDURES_MUTATORCONTAINER_TOOLTIP"] = "Add, remove, or reorder inputs to this function."; +Blockly.Msg["REDO"] = "Redo"; +Blockly.Msg["REMOVE_COMMENT"] = "Remove Comment"; +Blockly.Msg["RENAME_VARIABLE"] = "Rename variable..."; +Blockly.Msg["RENAME_VARIABLE_TITLE"] = "Rename all '%1' variables to:"; +Blockly.Msg["TEXT_APPEND_HELPURL"] = "https://github.com/google/blockly/wiki/Text#text-modification"; +Blockly.Msg["TEXT_APPEND_TITLE"] = "to %1 append text %2"; +Blockly.Msg["TEXT_APPEND_TOOLTIP"] = "Append some text to variable '%1'."; +Blockly.Msg["TEXT_CHANGECASE_HELPURL"] = "https://github.com/google/blockly/wiki/Text#adjusting-text-case"; +Blockly.Msg["TEXT_CHANGECASE_OPERATOR_LOWERCASE"] = "to lower case"; +Blockly.Msg["TEXT_CHANGECASE_OPERATOR_TITLECASE"] = "to Title Case"; +Blockly.Msg["TEXT_CHANGECASE_OPERATOR_UPPERCASE"] = "to UPPER CASE"; +Blockly.Msg["TEXT_CHANGECASE_TOOLTIP"] = "Return a copy of the text in a different case."; +Blockly.Msg["TEXT_CHARAT_FIRST"] = "get first letter"; +Blockly.Msg["TEXT_CHARAT_FROM_END"] = "get letter # from end"; +Blockly.Msg["TEXT_CHARAT_FROM_START"] = "get letter #"; +Blockly.Msg["TEXT_CHARAT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#extracting-text"; +Blockly.Msg["TEXT_CHARAT_LAST"] = "get last letter"; +Blockly.Msg["TEXT_CHARAT_RANDOM"] = "get random letter"; +Blockly.Msg["TEXT_CHARAT_TAIL"] = ""; +Blockly.Msg["TEXT_CHARAT_TITLE"] = "in text %1 %2"; +Blockly.Msg["TEXT_CHARAT_TOOLTIP"] = "Returns the letter at the specified position."; +Blockly.Msg["TEXT_COUNT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#counting-substrings"; +Blockly.Msg["TEXT_COUNT_MESSAGE0"] = "count %1 in %2"; +Blockly.Msg["TEXT_COUNT_TOOLTIP"] = "Count how many times some text occurs within some other text."; +Blockly.Msg["TEXT_CREATE_JOIN_ITEM_TOOLTIP"] = "Add an item to the text."; +Blockly.Msg["TEXT_CREATE_JOIN_TITLE_JOIN"] = "join"; +Blockly.Msg["TEXT_CREATE_JOIN_TOOLTIP"] = "Add, remove, or reorder sections to reconfigure this text block."; +Blockly.Msg["TEXT_GET_SUBSTRING_END_FROM_END"] = "to letter # from end"; +Blockly.Msg["TEXT_GET_SUBSTRING_END_FROM_START"] = "to letter #"; +Blockly.Msg["TEXT_GET_SUBSTRING_END_LAST"] = "to last letter"; +Blockly.Msg["TEXT_GET_SUBSTRING_HELPURL"] = "https://github.com/google/blockly/wiki/Text#extracting-a-region-of-text"; +Blockly.Msg["TEXT_GET_SUBSTRING_INPUT_IN_TEXT"] = "in text"; +Blockly.Msg["TEXT_GET_SUBSTRING_START_FIRST"] = "get substring from first letter"; +Blockly.Msg["TEXT_GET_SUBSTRING_START_FROM_END"] = "get substring from letter # from end"; +Blockly.Msg["TEXT_GET_SUBSTRING_START_FROM_START"] = "get substring from letter #"; +Blockly.Msg["TEXT_GET_SUBSTRING_TAIL"] = ""; +Blockly.Msg["TEXT_GET_SUBSTRING_TOOLTIP"] = "Returns a specified portion of the text."; +Blockly.Msg["TEXT_INDEXOF_HELPURL"] = "https://github.com/google/blockly/wiki/Text#finding-text"; +Blockly.Msg["TEXT_INDEXOF_OPERATOR_FIRST"] = "find first occurrence of text"; +Blockly.Msg["TEXT_INDEXOF_OPERATOR_LAST"] = "find last occurrence of text"; +Blockly.Msg["TEXT_INDEXOF_TITLE"] = "in text %1 %2 %3"; +Blockly.Msg["TEXT_INDEXOF_TOOLTIP"] = "Returns the index of the first/last occurrence of the first text in the second text. Returns %1 if text is not found."; +Blockly.Msg["TEXT_ISEMPTY_HELPURL"] = "https://github.com/google/blockly/wiki/Text#checking-for-empty-text"; +Blockly.Msg["TEXT_ISEMPTY_TITLE"] = "%1 is empty"; +Blockly.Msg["TEXT_ISEMPTY_TOOLTIP"] = "Returns true if the provided text is empty."; +Blockly.Msg["TEXT_JOIN_HELPURL"] = "https://github.com/google/blockly/wiki/Text#text-creation"; +Blockly.Msg["TEXT_JOIN_TITLE_CREATEWITH"] = "create text with"; +Blockly.Msg["TEXT_JOIN_TOOLTIP"] = "Create a piece of text by joining together any number of items."; +Blockly.Msg["TEXT_LENGTH_HELPURL"] = "https://github.com/google/blockly/wiki/Text#text-modification"; +Blockly.Msg["TEXT_LENGTH_TITLE"] = "length of %1"; +Blockly.Msg["TEXT_LENGTH_TOOLTIP"] = "Returns the number of letters (including spaces) in the provided text."; +Blockly.Msg["TEXT_PRINT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#printing-text"; +Blockly.Msg["TEXT_PRINT_TITLE"] = "print %1"; +Blockly.Msg["TEXT_PRINT_TOOLTIP"] = "Print the specified text, number or other value."; +Blockly.Msg["TEXT_PROMPT_HELPURL"] = "https://github.com/google/blockly/wiki/Text#getting-input-from-the-user"; +Blockly.Msg["TEXT_PROMPT_TOOLTIP_NUMBER"] = "Prompt for user for a number."; +Blockly.Msg["TEXT_PROMPT_TOOLTIP_TEXT"] = "Prompt for user for some text."; +Blockly.Msg["TEXT_PROMPT_TYPE_NUMBER"] = "prompt for number with message"; +Blockly.Msg["TEXT_PROMPT_TYPE_TEXT"] = "prompt for text with message"; +Blockly.Msg["TEXT_REPLACE_HELPURL"] = "https://github.com/google/blockly/wiki/Text#replacing-substrings"; +Blockly.Msg["TEXT_REPLACE_MESSAGE0"] = "replace %1 with %2 in %3"; +Blockly.Msg["TEXT_REPLACE_TOOLTIP"] = "Replace all occurances of some text within some other text."; +Blockly.Msg["TEXT_REVERSE_HELPURL"] = "https://github.com/google/blockly/wiki/Text#reversing-text"; +Blockly.Msg["TEXT_REVERSE_MESSAGE0"] = "reverse %1"; +Blockly.Msg["TEXT_REVERSE_TOOLTIP"] = "Reverses the order of the characters in the text."; +Blockly.Msg["TEXT_TEXT_HELPURL"] = "https://en.wikipedia.org/wiki/String_(computer_science)"; +Blockly.Msg["TEXT_TEXT_TOOLTIP"] = "A letter, word, or line of text."; +Blockly.Msg["TEXT_TRIM_HELPURL"] = "https://github.com/google/blockly/wiki/Text#trimming-removing-spaces"; +Blockly.Msg["TEXT_TRIM_OPERATOR_BOTH"] = "trim spaces from both sides of"; +Blockly.Msg["TEXT_TRIM_OPERATOR_LEFT"] = "trim spaces from left side of"; +Blockly.Msg["TEXT_TRIM_OPERATOR_RIGHT"] = "trim spaces from right side of"; +Blockly.Msg["TEXT_TRIM_TOOLTIP"] = "Return a copy of the text with spaces removed from one or both ends."; +Blockly.Msg["TODAY"] = "Today"; +Blockly.Msg["UNDO"] = "Undo"; +Blockly.Msg["UNNAMED_KEY"] = "unnamed"; +Blockly.Msg["VARIABLES_DEFAULT_NAME"] = "item"; +Blockly.Msg["VARIABLES_GET_CREATE_SET"] = "Create 'set %1'"; +Blockly.Msg["VARIABLES_GET_HELPURL"] = "https://github.com/google/blockly/wiki/Variables#get"; +Blockly.Msg["VARIABLES_GET_TOOLTIP"] = "Returns the value of this variable."; +Blockly.Msg["VARIABLES_SET"] = "set %1 to %2"; +Blockly.Msg["VARIABLES_SET_CREATE_GET"] = "Create 'get %1'"; +Blockly.Msg["VARIABLES_SET_HELPURL"] = "https://github.com/google/blockly/wiki/Variables#set"; +Blockly.Msg["VARIABLES_SET_TOOLTIP"] = "Sets this variable to be equal to the input."; +Blockly.Msg["VARIABLE_ALREADY_EXISTS"] = "A variable named '%1' already exists."; +Blockly.Msg["VARIABLE_ALREADY_EXISTS_FOR_ANOTHER_TYPE"] = "A variable named '%1' already exists for another type: '%2'."; +Blockly.Msg["WORKSPACE_ARIA_LABEL"] = "Blockly Workspace"; +Blockly.Msg["WORKSPACE_COMMENT_DEFAULT_TEXT"] = "Say something..."; +Blockly.Msg["CONTROLS_FOREACH_INPUT_DO"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"]; +Blockly.Msg["CONTROLS_FOR_INPUT_DO"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"]; +Blockly.Msg["CONTROLS_IF_ELSEIF_TITLE_ELSEIF"] = Blockly.Msg["CONTROLS_IF_MSG_ELSEIF"]; +Blockly.Msg["CONTROLS_IF_ELSE_TITLE_ELSE"] = Blockly.Msg["CONTROLS_IF_MSG_ELSE"]; +Blockly.Msg["CONTROLS_IF_IF_TITLE_IF"] = Blockly.Msg["CONTROLS_IF_MSG_IF"]; +Blockly.Msg["CONTROLS_IF_MSG_THEN"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"]; +Blockly.Msg["CONTROLS_WHILEUNTIL_INPUT_DO"] = Blockly.Msg["CONTROLS_REPEAT_INPUT_DO"]; +Blockly.Msg["LISTS_CREATE_WITH_ITEM_TITLE"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"]; +Blockly.Msg["LISTS_GET_INDEX_HELPURL"] = Blockly.Msg["LISTS_INDEX_OF_HELPURL"]; +Blockly.Msg["LISTS_GET_INDEX_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"]; +Blockly.Msg["LISTS_GET_SUBLIST_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"]; +Blockly.Msg["LISTS_INDEX_OF_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"]; +Blockly.Msg["LISTS_SET_INDEX_INPUT_IN_LIST"] = Blockly.Msg["LISTS_INLIST"]; +Blockly.Msg["MATH_CHANGE_TITLE_ITEM"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"]; +Blockly.Msg["PROCEDURES_DEFRETURN_COMMENT"] = Blockly.Msg["PROCEDURES_DEFNORETURN_COMMENT"]; +Blockly.Msg["PROCEDURES_DEFRETURN_DO"] = Blockly.Msg["PROCEDURES_DEFNORETURN_DO"]; +Blockly.Msg["PROCEDURES_DEFRETURN_PROCEDURE"] = Blockly.Msg["PROCEDURES_DEFNORETURN_PROCEDURE"]; +Blockly.Msg["PROCEDURES_DEFRETURN_TITLE"] = Blockly.Msg["PROCEDURES_DEFNORETURN_TITLE"]; +Blockly.Msg["TEXT_APPEND_VARIABLE"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"]; +Blockly.Msg["TEXT_CREATE_JOIN_ITEM_TITLE_ITEM"] = Blockly.Msg["VARIABLES_DEFAULT_NAME"]; + +Blockly.Msg["MATH_HUE"] = "230"; +Blockly.Msg["LOOPS_HUE"] = "120"; +Blockly.Msg["LISTS_HUE"] = "260"; +Blockly.Msg["LOGIC_HUE"] = "210"; +Blockly.Msg["VARIABLES_HUE"] = "330"; +Blockly.Msg["TEXTS_HUE"] = "160"; +Blockly.Msg["PROCEDURES_HUE"] = "290"; +Blockly.Msg["COLOUR_HUE"] = "20"; +Blockly.Msg["VARIABLES_DYNAMIC_HUE"] = "310"; \ No newline at end of file diff --git a/clover_blocks/www/blockly/python_compressed.js b/clover_blocks/www/blockly/python_compressed.js new file mode 100755 index 000000000..98e61eebd --- /dev/null +++ b/clover_blocks/www/blockly/python_compressed.js @@ -0,0 +1,98 @@ +// Do not edit this file; automatically generated by gulp. + +/* eslint-disable */ +;(function(root, factory) { + if (typeof define === 'function' && define.amd) { // AMD + define(['./blockly_compressed.js'], factory); + } else if (typeof exports === 'object') { // Node.js + module.exports = factory(require('./blockly_compressed.js')); + } else { // Browser + root.Blockly.Python = factory(root.Blockly); + } +}(this, function(Blockly) { + 'use strict';Blockly.Python=new Blockly.Generator("Python");Blockly.Python.addReservedWords("False,None,True,and,as,assert,break,class,continue,def,del,elif,else,except,exec,finally,for,from,global,if,import,in,is,lambda,nonlocal,not,or,pass,print,raise,return,try,while,with,yield,NotImplemented,Ellipsis,__debug__,quit,exit,copyright,license,credits,ArithmeticError,AssertionError,AttributeError,BaseException,BlockingIOError,BrokenPipeError,BufferError,BytesWarning,ChildProcessError,ConnectionAbortedError,ConnectionError,ConnectionRefusedError,ConnectionResetError,DeprecationWarning,EOFError,Ellipsis,EnvironmentError,Exception,FileExistsError,FileNotFoundError,FloatingPointError,FutureWarning,GeneratorExit,IOError,ImportError,ImportWarning,IndentationError,IndexError,InterruptedError,IsADirectoryError,KeyError,KeyboardInterrupt,LookupError,MemoryError,ModuleNotFoundError,NameError,NotADirectoryError,NotImplemented,NotImplementedError,OSError,OverflowError,PendingDeprecationWarning,PermissionError,ProcessLookupError,RecursionError,ReferenceError,ResourceWarning,RuntimeError,RuntimeWarning,StandardError,StopAsyncIteration,StopIteration,SyntaxError,SyntaxWarning,SystemError,SystemExit,TabError,TimeoutError,TypeError,UnboundLocalError,UnicodeDecodeError,UnicodeEncodeError,UnicodeError,UnicodeTranslateError,UnicodeWarning,UserWarning,ValueError,Warning,ZeroDivisionError,_,__build_class__,__debug__,__doc__,__import__,__loader__,__name__,__package__,__spec__,abs,all,any,apply,ascii,basestring,bin,bool,buffer,bytearray,bytes,callable,chr,classmethod,cmp,coerce,compile,complex,copyright,credits,delattr,dict,dir,divmod,enumerate,eval,exec,execfile,exit,file,filter,float,format,frozenset,getattr,globals,hasattr,hash,help,hex,id,input,int,intern,isinstance,issubclass,iter,len,license,list,locals,long,map,max,memoryview,min,next,object,oct,open,ord,pow,print,property,quit,range,raw_input,reduce,reload,repr,reversed,round,set,setattr,slice,sorted,staticmethod,str,sum,super,tuple,type,unichr,unicode,vars,xrange,zip"); +Blockly.Python.ORDER_ATOMIC=0;Blockly.Python.ORDER_COLLECTION=1;Blockly.Python.ORDER_STRING_CONVERSION=1;Blockly.Python.ORDER_MEMBER=2.1;Blockly.Python.ORDER_FUNCTION_CALL=2.2;Blockly.Python.ORDER_EXPONENTIATION=3;Blockly.Python.ORDER_UNARY_SIGN=4;Blockly.Python.ORDER_BITWISE_NOT=4;Blockly.Python.ORDER_MULTIPLICATIVE=5;Blockly.Python.ORDER_ADDITIVE=6;Blockly.Python.ORDER_BITWISE_SHIFT=7;Blockly.Python.ORDER_BITWISE_AND=8;Blockly.Python.ORDER_BITWISE_XOR=9;Blockly.Python.ORDER_BITWISE_OR=10; +Blockly.Python.ORDER_RELATIONAL=11;Blockly.Python.ORDER_LOGICAL_NOT=12;Blockly.Python.ORDER_LOGICAL_AND=13;Blockly.Python.ORDER_LOGICAL_OR=14;Blockly.Python.ORDER_CONDITIONAL=15;Blockly.Python.ORDER_LAMBDA=16;Blockly.Python.ORDER_NONE=99; +Blockly.Python.ORDER_OVERRIDES=[[Blockly.Python.ORDER_FUNCTION_CALL,Blockly.Python.ORDER_MEMBER],[Blockly.Python.ORDER_FUNCTION_CALL,Blockly.Python.ORDER_FUNCTION_CALL],[Blockly.Python.ORDER_MEMBER,Blockly.Python.ORDER_MEMBER],[Blockly.Python.ORDER_MEMBER,Blockly.Python.ORDER_FUNCTION_CALL],[Blockly.Python.ORDER_LOGICAL_NOT,Blockly.Python.ORDER_LOGICAL_NOT],[Blockly.Python.ORDER_LOGICAL_AND,Blockly.Python.ORDER_LOGICAL_AND],[Blockly.Python.ORDER_LOGICAL_OR,Blockly.Python.ORDER_LOGICAL_OR]]; +Blockly.Python.init=function(a){Blockly.Python.PASS=this.INDENT+"pass\n";Blockly.Python.definitions_=Object.create(null);Blockly.Python.functionNames_=Object.create(null);Blockly.Python.variableDB_?Blockly.Python.variableDB_.reset():Blockly.Python.variableDB_=new Blockly.Names(Blockly.Python.RESERVED_WORDS_);Blockly.Python.variableDB_.setVariableMap(a.getVariableMap());for(var b=[],c=Blockly.Variables.allDeveloperVariables(a),d=0;dc?"int("+a+" - "+-c+")":"int("+a+")",d&&(a="-"+a));return a};Blockly.Python.colour={};Blockly.Python.colour_picker=function(a){return[Blockly.Python.quote_(a.getFieldValue("COLOUR")),Blockly.Python.ORDER_ATOMIC]};Blockly.Python.colour_random=function(a){Blockly.Python.definitions_.import_random="import random";return["'#%06x' % random.randint(0, 2**24 - 1)",Blockly.Python.ORDER_FUNCTION_CALL]}; +Blockly.Python.colour_rgb=function(a){var b=Blockly.Python.provideFunction_("colour_rgb",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(r, g, b):"," r = round(min(100, max(0, r)) * 2.55)"," g = round(min(100, max(0, g)) * 2.55)"," b = round(min(100, max(0, b)) * 2.55)"," return '#%02x%02x%02x' % (r, g, b)"]),c=Blockly.Python.valueToCode(a,"RED",Blockly.Python.ORDER_NONE)||0,d=Blockly.Python.valueToCode(a,"GREEN",Blockly.Python.ORDER_NONE)||0;a=Blockly.Python.valueToCode(a,"BLUE",Blockly.Python.ORDER_NONE)|| +0;return[b+"("+c+", "+d+", "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]}; +Blockly.Python.colour_blend=function(a){var b=Blockly.Python.provideFunction_("colour_blend",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(colour1, colour2, ratio):"," r1, r2 = int(colour1[1:3], 16), int(colour2[1:3], 16)"," g1, g2 = int(colour1[3:5], 16), int(colour2[3:5], 16)"," b1, b2 = int(colour1[5:7], 16), int(colour2[5:7], 16)"," ratio = min(1, max(0, ratio))"," r = round(r1 * (1 - ratio) + r2 * ratio)"," g = round(g1 * (1 - ratio) + g2 * ratio)"," b = round(b1 * (1 - ratio) + b2 * ratio)", +" return '#%02x%02x%02x' % (r, g, b)"]),c=Blockly.Python.valueToCode(a,"COLOUR1",Blockly.Python.ORDER_NONE)||"'#000000'",d=Blockly.Python.valueToCode(a,"COLOUR2",Blockly.Python.ORDER_NONE)||"'#000000'";a=Blockly.Python.valueToCode(a,"RATIO",Blockly.Python.ORDER_NONE)||0;return[b+"("+c+", "+d+", "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.lists={};Blockly.Python.lists_create_empty=function(a){return["[]",Blockly.Python.ORDER_ATOMIC]};Blockly.Python.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c",GTE:">="}[a.getFieldValue("OP")],c=Blockly.Python.ORDER_RELATIONAL,d=Blockly.Python.valueToCode(a,"A",c)||"0";a=Blockly.Python.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]}; +Blockly.Python.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?"and":"or",c="and"==b?Blockly.Python.ORDER_LOGICAL_AND:Blockly.Python.ORDER_LOGICAL_OR,d=Blockly.Python.valueToCode(a,"A",c);a=Blockly.Python.valueToCode(a,"B",c);if(d||a){var e="and"==b?"True":"False";d||(d=e);a||(a=e)}else a=d="False";return[d+" "+b+" "+a,c]};Blockly.Python.logic_negate=function(a){return["not "+(Blockly.Python.valueToCode(a,"BOOL",Blockly.Python.ORDER_LOGICAL_NOT)||"True"),Blockly.Python.ORDER_LOGICAL_NOT]}; +Blockly.Python.logic_boolean=function(a){return["TRUE"==a.getFieldValue("BOOL")?"True":"False",Blockly.Python.ORDER_ATOMIC]};Blockly.Python.logic_null=function(a){return["None",Blockly.Python.ORDER_ATOMIC]}; +Blockly.Python.logic_ternary=function(a){var b=Blockly.Python.valueToCode(a,"IF",Blockly.Python.ORDER_CONDITIONAL)||"False",c=Blockly.Python.valueToCode(a,"THEN",Blockly.Python.ORDER_CONDITIONAL)||"None";a=Blockly.Python.valueToCode(a,"ELSE",Blockly.Python.ORDER_CONDITIONAL)||"None";return[c+" if "+b+" else "+a,Blockly.Python.ORDER_CONDITIONAL]};Blockly.Python.loops={};Blockly.Python.controls_repeat_ext=function(a){var b=a.getField("TIMES")?String(parseInt(a.getFieldValue("TIMES"),10)):Blockly.Python.valueToCode(a,"TIMES",Blockly.Python.ORDER_NONE)||"0";b=Blockly.isNumber(b)?parseInt(b,10):"int("+b+")";var c=Blockly.Python.statementToCode(a,"DO");c=Blockly.Python.addLoopTrap(c,a)||Blockly.Python.PASS;return"for "+Blockly.Python.variableDB_.getDistinctName("count",Blockly.VARIABLE_CATEGORY_NAME)+" in range("+b+"):\n"+c}; +Blockly.Python.controls_repeat=Blockly.Python.controls_repeat_ext;Blockly.Python.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.Python.valueToCode(a,"BOOL",b?Blockly.Python.ORDER_LOGICAL_NOT:Blockly.Python.ORDER_NONE)||"False",d=Blockly.Python.statementToCode(a,"DO");d=Blockly.Python.addLoopTrap(d,a)||Blockly.Python.PASS;b&&(c="not "+c);return"while "+c+":\n"+d}; +Blockly.Python.controls_for=function(a){var b=Blockly.Python.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME),c=Blockly.Python.valueToCode(a,"FROM",Blockly.Python.ORDER_NONE)||"0",d=Blockly.Python.valueToCode(a,"TO",Blockly.Python.ORDER_NONE)||"0",e=Blockly.Python.valueToCode(a,"BY",Blockly.Python.ORDER_NONE)||"1",f=Blockly.Python.statementToCode(a,"DO");f=Blockly.Python.addLoopTrap(f,a)||Blockly.Python.PASS;var l="",h=function(){return Blockly.Python.provideFunction_("upRange", +["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(start, stop, step):"," while start <= stop:"," yield start"," start += abs(step)"])},g=function(){return Blockly.Python.provideFunction_("downRange",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(start, stop, step):"," while start >= stop:"," yield start"," start -= abs(step)"])};a=function(a,b,c){return"("+a+" <= "+b+") and "+h()+"("+a+", "+b+", "+c+") or "+g()+"("+a+", "+b+", "+c+")"};if(Blockly.isNumber(c)&&Blockly.isNumber(d)&& +Blockly.isNumber(e))c=Number(c),d=Number(d),e=Math.abs(Number(e)),0===c%1&&0===d%1&&0===e%1?(c<=d?(d++,a=0==c&&1==e?d:c+", "+d,1!=e&&(a+=", "+e)):(d--,a=c+", "+d+", -"+e),a="range("+a+")"):(a=ca?Blockly.Python.ORDER_UNARY_SIGN:Blockly.Python.ORDER_ATOMIC;return[a,b]}; +Blockly.Python.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.Python.ORDER_ADDITIVE],MINUS:[" - ",Blockly.Python.ORDER_ADDITIVE],MULTIPLY:[" * ",Blockly.Python.ORDER_MULTIPLICATIVE],DIVIDE:[" / ",Blockly.Python.ORDER_MULTIPLICATIVE],POWER:[" ** ",Blockly.Python.ORDER_EXPONENTIATION]}[a.getFieldValue("OP")],c=b[0];b=b[1];var d=Blockly.Python.valueToCode(a,"A",b)||"0";a=Blockly.Python.valueToCode(a,"B",b)||"0";return[d+c+a,b]}; +Blockly.Python.math_single=function(a){var b=a.getFieldValue("OP");if("NEG"==b){var c=Blockly.Python.valueToCode(a,"NUM",Blockly.Python.ORDER_UNARY_SIGN)||"0";return["-"+c,Blockly.Python.ORDER_UNARY_SIGN]}Blockly.Python.definitions_.import_math="import math";a="SIN"==b||"COS"==b||"TAN"==b?Blockly.Python.valueToCode(a,"NUM",Blockly.Python.ORDER_MULTIPLICATIVE)||"0":Blockly.Python.valueToCode(a,"NUM",Blockly.Python.ORDER_NONE)||"0";switch(b){case "ABS":c="math.fabs("+a+")";break;case "ROOT":c="math.sqrt("+ +a+")";break;case "LN":c="math.log("+a+")";break;case "LOG10":c="math.log10("+a+")";break;case "EXP":c="math.exp("+a+")";break;case "POW10":c="math.pow(10,"+a+")";break;case "ROUND":c="round("+a+")";break;case "ROUNDUP":c="math.ceil("+a+")";break;case "ROUNDDOWN":c="math.floor("+a+")";break;case "SIN":c="math.sin("+a+" / 180.0 * math.pi)";break;case "COS":c="math.cos("+a+" / 180.0 * math.pi)";break;case "TAN":c="math.tan("+a+" / 180.0 * math.pi)"}if(c)return[c,Blockly.Python.ORDER_FUNCTION_CALL];switch(b){case "ASIN":c= +"math.asin("+a+") / math.pi * 180";break;case "ACOS":c="math.acos("+a+") / math.pi * 180";break;case "ATAN":c="math.atan("+a+") / math.pi * 180";break;default:throw Error("Unknown math operator: "+b);}return[c,Blockly.Python.ORDER_MULTIPLICATIVE]}; +Blockly.Python.math_constant=function(a){var b={PI:["math.pi",Blockly.Python.ORDER_MEMBER],E:["math.e",Blockly.Python.ORDER_MEMBER],GOLDEN_RATIO:["(1 + math.sqrt(5)) / 2",Blockly.Python.ORDER_MULTIPLICATIVE],SQRT2:["math.sqrt(2)",Blockly.Python.ORDER_MEMBER],SQRT1_2:["math.sqrt(1.0 / 2)",Blockly.Python.ORDER_MEMBER],INFINITY:["float('inf')",Blockly.Python.ORDER_ATOMIC]};a=a.getFieldValue("CONSTANT");"INFINITY"!=a&&(Blockly.Python.definitions_.import_math="import math");return b[a]}; +Blockly.Python.math_number_property=function(a){var b=Blockly.Python.valueToCode(a,"NUMBER_TO_CHECK",Blockly.Python.ORDER_MULTIPLICATIVE)||"0",c=a.getFieldValue("PROPERTY");if("PRIME"==c)return Blockly.Python.definitions_.import_math="import math",Blockly.Python.definitions_.from_numbers_import_Number="from numbers import Number",[Blockly.Python.provideFunction_("math_isPrime",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(n):"," # https://en.wikipedia.org/wiki/Primality_test#Naive_methods", +" # If n is not a number but a string, try parsing it."," if not isinstance(n, Number):"," try:"," n = float(n)"," except:"," return False"," if n == 2 or n == 3:"," return True"," # False if n is negative, is 1, or not whole, or if n is divisible by 2 or 3."," if n <= 1 or n % 1 != 0 or n % 2 == 0 or n % 3 == 0:"," return False"," # Check all the numbers of form 6k +/- 1, up to sqrt(n)."," for x in range(6, int(math.sqrt(n)) + 2, 6):"," if n % (x - 1) == 0 or n % (x + 1) == 0:", +" return False"," return True"])+"("+b+")",Blockly.Python.ORDER_FUNCTION_CALL];switch(c){case "EVEN":var d=b+" % 2 == 0";break;case "ODD":d=b+" % 2 == 1";break;case "WHOLE":d=b+" % 1 == 0";break;case "POSITIVE":d=b+" > 0";break;case "NEGATIVE":d=b+" < 0";break;case "DIVISIBLE_BY":a=Blockly.Python.valueToCode(a,"DIVISOR",Blockly.Python.ORDER_MULTIPLICATIVE);if(!a||"0"==a)return["False",Blockly.Python.ORDER_ATOMIC];d=b+" % "+a+" == 0"}return[d,Blockly.Python.ORDER_RELATIONAL]}; +Blockly.Python.math_change=function(a){Blockly.Python.definitions_.from_numbers_import_Number="from numbers import Number";var b=Blockly.Python.valueToCode(a,"DELTA",Blockly.Python.ORDER_ADDITIVE)||"0";a=Blockly.Python.variableDB_.getName(a.getFieldValue("VAR"),Blockly.VARIABLE_CATEGORY_NAME);return a+" = ("+a+" if isinstance("+a+", Number) else 0) + "+b+"\n"};Blockly.Python.math_round=Blockly.Python.math_single;Blockly.Python.math_trig=Blockly.Python.math_single; +Blockly.Python.math_on_list=function(a){var b=a.getFieldValue("OP");a=Blockly.Python.valueToCode(a,"LIST",Blockly.Python.ORDER_NONE)||"[]";switch(b){case "SUM":b="sum("+a+")";break;case "MIN":b="min("+a+")";break;case "MAX":b="max("+a+")";break;case "AVERAGE":Blockly.Python.definitions_.from_numbers_import_Number="from numbers import Number";b=Blockly.Python.provideFunction_("math_mean",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(myList):"," localList = [e for e in myList if isinstance(e, Number)]", +" if not localList: return"," return float(sum(localList)) / len(localList)"]);b=b+"("+a+")";break;case "MEDIAN":Blockly.Python.definitions_.from_numbers_import_Number="from numbers import Number";b=Blockly.Python.provideFunction_("math_median",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(myList):"," localList = sorted([e for e in myList if isinstance(e, Number)])"," if not localList: return"," if len(localList) % 2 == 0:"," return (localList[len(localList) // 2 - 1] + localList[len(localList) // 2]) / 2.0", +" else:"," return localList[(len(localList) - 1) // 2]"]);b=b+"("+a+")";break;case "MODE":b=Blockly.Python.provideFunction_("math_modes",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(some_list):"," modes = []"," # Using a lists of [item, count] to keep count rather than dict",' # to avoid "unhashable" errors when the counted item is itself a list or dict.'," counts = []"," maxCount = 1"," for item in some_list:"," found = False"," for count in counts:"," if count[0] == item:", +" count[1] += 1"," maxCount = max(maxCount, count[1])"," found = True"," if not found:"," counts.append([item, 1])"," for counted_item, item_count in counts:"," if item_count == maxCount:"," modes.append(counted_item)"," return modes"]);b=b+"("+a+")";break;case "STD_DEV":Blockly.Python.definitions_.import_math="import math";b=Blockly.Python.provideFunction_("math_standard_deviation",["def "+Blockly.Python.FUNCTION_NAME_PLACEHOLDER_+"(numbers):"," n = len(numbers)", +" if n == 0: return"," mean = float(sum(numbers)) / n"," variance = sum((x - mean) ** 2 for x in numbers) / n"," return math.sqrt(variance)"]);b=b+"("+a+")";break;case "RANDOM":Blockly.Python.definitions_.import_random="import random";b="random.choice("+a+")";break;default:throw Error("Unknown operator: "+b);}return[b,Blockly.Python.ORDER_FUNCTION_CALL]}; +Blockly.Python.math_modulo=function(a){var b=Blockly.Python.valueToCode(a,"DIVIDEND",Blockly.Python.ORDER_MULTIPLICATIVE)||"0";a=Blockly.Python.valueToCode(a,"DIVISOR",Blockly.Python.ORDER_MULTIPLICATIVE)||"0";return[b+" % "+a,Blockly.Python.ORDER_MULTIPLICATIVE]}; +Blockly.Python.math_constrain=function(a){var b=Blockly.Python.valueToCode(a,"VALUE",Blockly.Python.ORDER_NONE)||"0",c=Blockly.Python.valueToCode(a,"LOW",Blockly.Python.ORDER_NONE)||"0";a=Blockly.Python.valueToCode(a,"HIGH",Blockly.Python.ORDER_NONE)||"float('inf')";return["min(max("+b+", "+c+"), "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]}; +Blockly.Python.math_random_int=function(a){Blockly.Python.definitions_.import_random="import random";var b=Blockly.Python.valueToCode(a,"FROM",Blockly.Python.ORDER_NONE)||"0";a=Blockly.Python.valueToCode(a,"TO",Blockly.Python.ORDER_NONE)||"0";return["random.randint("+b+", "+a+")",Blockly.Python.ORDER_FUNCTION_CALL]};Blockly.Python.math_random_float=function(a){Blockly.Python.definitions_.import_random="import random";return["random.random()",Blockly.Python.ORDER_FUNCTION_CALL]}; +Blockly.Python.math_atan2=function(a){Blockly.Python.definitions_.import_math="import math";var b=Blockly.Python.valueToCode(a,"X",Blockly.Python.ORDER_NONE)||"0";return["math.atan2("+(Blockly.Python.valueToCode(a,"Y",Blockly.Python.ORDER_NONE)||"0")+", "+b+") / math.pi * 180",Blockly.Python.ORDER_MULTIPLICATIVE]};Blockly.Python.procedures={}; +Blockly.Python.procedures_defreturn=function(a){for(var b=[],c,d=a.workspace,e=Blockly.Variables.allUsedVarModels(d)||[],f=0;c=e[f];f++)c=c.name,-1==a.getVars().indexOf(c)&&b.push(Blockly.Python.variableDB_.getName(c,Blockly.VARIABLE_CATEGORY_NAME));e=Blockly.Variables.allDeveloperVariables(d);for(f=0;f/g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:O,mergeStreams:k}=i,M=Symbol("nomatch");return function(t){var a=[],i=Object.create(null),s=Object.create(null),o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void k.addText(A);e=m(y.subLanguage,A,!0,O[y.subLanguage]),O[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),k.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void k.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;k.addText(t),t="",I+=a,k.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),k.addText(t)}(),A=""}function h(e){return e.className&&k.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&k.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,O={},k=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>k.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),k.closeAllNodes(),k.finalize(),N=k.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:k,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:k};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:k,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
    ":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=O(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=k(i,O(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.2";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs);hljs.registerLanguage("properties",function(){"use strict";return function(e){var n="[ \\t\\f]*",t="("+n+"[:=]"+n+"|[ \\t\\f]+)",a="([^\\\\:= \\t\\f\\n]|\\\\.)+",s={end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{begin:"\\\\\\n"}]}};return{name:".properties",case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+"+t,returnBegin:!0,contains:[{className:"attr",begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:"meta",begin:a,endsParent:!0,relevance:0}],starts:s},{className:"attr",relevance:0,begin:a+n+"$"}]}}}());hljs.registerLanguage("python",function(){"use strict";return function(e){var n={keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10",built_in:"Ellipsis NotImplemented",literal:"False None True"},a={className:"meta",begin:/^(>>>|\.\.\.) /},i={className:"subst",begin:/\{/,end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},r={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)"/,end:/"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)"/,end:/"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:"number",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+"[lLjJ]?"},{begin:"\\b(0o[0-7]+)[lLjJ]?"},{begin:e.C_NUMBER_RE+"[lLjJ]?"}]},t={className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:["self",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:"Python",aliases:["py","gyp","ipython"],keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,l,{beginKeywords:"if",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function",beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/,end:/$/},{begin:/\b(print|exec)\(/}]}}}()); \ No newline at end of file diff --git a/clover_blocks/www/highlight/styles/monokai-sublime.css b/clover_blocks/www/highlight/styles/monokai-sublime.css new file mode 100644 index 000000000..2864170da --- /dev/null +++ b/clover_blocks/www/highlight/styles/monokai-sublime.css @@ -0,0 +1,83 @@ +/* + +Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/ + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #23241f; +} + +.hljs, +.hljs-tag, +.hljs-subst { + color: #f8f8f2; +} + +.hljs-strong, +.hljs-emphasis { + color: #a8a8a2; +} + +.hljs-bullet, +.hljs-quote, +.hljs-number, +.hljs-regexp, +.hljs-literal, +.hljs-link { + color: #ae81ff; +} + +.hljs-code, +.hljs-title, +.hljs-section, +.hljs-selector-class { + color: #a6e22e; +} + +.hljs-strong { + font-weight: bold; +} + +.hljs-emphasis { + font-style: italic; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-name, +.hljs-attr { + color: #f92672; +} + +.hljs-symbol, +.hljs-attribute { + color: #66d9ef; +} + +.hljs-params, +.hljs-class .hljs-title { + color: #f8f8f2; +} + +.hljs-string, +.hljs-type, +.hljs-built_in, +.hljs-builtin-name, +.hljs-selector-id, +.hljs-selector-attr, +.hljs-selector-pseudo, +.hljs-addition, +.hljs-variable, +.hljs-template-variable { + color: #e6db74; +} + +.hljs-comment, +.hljs-deletion, +.hljs-meta { + color: #75715e; +} diff --git a/clover_blocks/www/index.html b/clover_blocks/www/index.html new file mode 100644 index 000000000..1fd1fdffb --- /dev/null +++ b/clover_blocks/www/index.html @@ -0,0 +1,483 @@ + + + + Clover Blocks + + + + + + + + + + + + + + + +
      +
    • Blocks
    • +
    • Python
    • +
    + +
    +
    Disconnected
    +
    Failed to contact with clover_blocks node, see configuration documentation.
    +
    Running...
    + + + + +
    + +
    + +
    +
    
    +
    +	
    +
    +
    +
    diff --git a/clover_blocks/www/main.css b/clover_blocks/www/main.css
    new file mode 100644
    index 000000000..c97f1b062
    --- /dev/null
    +++ b/clover_blocks/www/main.css
    @@ -0,0 +1,136 @@
    +body, html {
    +	width: 100%;
    +	height: 100%;
    +}
    +
    +body {
    +	padding: 0;
    +	margin: 0;
    +	display: grid;
    +	grid-template-areas:
    +		"tabs tools"
    +		"content content";
    +	grid-template-rows: min-content 1fr;
    +	grid-template-columns: 1fr min-content ;
    +	font-family: sans-serif;
    +	font-size: 16px;
    +}
    +
    +#tools, #tabs {
    +	background: #dddddd;
    +	border-bottom: 2px rgb(192, 192, 192) solid;
    +	white-space: nowrap;
    +}
    +
    +#tools {
    +	grid-area: tools;
    +	white-space: nowrap;
    +	padding: 10px;
    +	display: flex;
    +	align-items: center;
    +}
    +
    +#program-name {
    +	width: 150px;
    +	border: none;
    +	font-size: 14px;
    +	padding: 5px;
    +	outline: none;
    +	background: transparent;
    +	text-overflow: ellipsis;
    +}
    +
    +#program-name:focus {
    +	background: white;
    +}
    +
    +.changed #program-name {
    +	color: red;
    +}
    +
    +.disconnected {
    +	color: red;
    +	margin-right: 20px;
    +}
    +
    +.connected .disconnected { display: none; }
    +
    +.backend-fail {
    +	margin-right: 20px;
    +	display: none;
    +}
    +
    +@keyframes blink {
    +	0% { opacity: 1; }
    +	50% { opacity: 0.5; }
    +	100% { opacity: 1; }
    +}
    +
    +#running {
    +	color: green;
    +	margin-right: 20px;
    +	animation: 1s blink linear infinite;
    +	display: none;
    +}
    +
    +.running #running { display: block; }
    +
    +#tools button {
    +	font-size: 20px;
    +	margin-left: 10px;
    +}
    +
    +#tabs {
    +	grid-area: tabs;
    +	list-style: none;
    +	margin: 0;
    +	padding: 10px;
    +}
    +
    +#tabs li {
    +	font-size: 18px;
    +	display: inline-block;
    +	padding: 5px 10px;
    +	cursor: pointer;
    +}
    +
    +#tabs li.selected {
    +	background: white;
    +}
    +
    +#run {
    +	width: 70px;
    +}
    +
    +#notifications {
    +	position: absolute;
    +	right: 10px;
    +	background: rgba(0, 0, 0, 0.7);
    +	color: white;
    +	max-width: 350px;
    +	top: 53px;
    +	/* border: white solid 3px; */
    +	z-index: 1;
    +	padding: 10px;
    +	font-family: monospace;
    +	font-size: 16px;
    +	white-space: pre;
    +	max-height: 400px;
    +	overflow: scroll;
    +}
    +
    +#notifications:empty { display: none; }
    +
    +.content {
    +	display: none !important;
    +	grid-area: content;
    +}
    +
    +body[data-tab="blocks"] #blockly { display: block !important; }
    +body[data-tab="python"] #python { display: block !important; }
    +
    +#python {
    +	margin: 0px;
    +	padding: 10px;
    +	z-index: 999999; /* overlap some Blockly's popups */
    +}
    diff --git a/clover_blocks/www/main.js b/clover_blocks/www/main.js
    new file mode 100644
    index 000000000..46b610325
    --- /dev/null
    +++ b/clover_blocks/www/main.js
    @@ -0,0 +1,313 @@
    +import * as ros from './ros.js';
    +import './blocks.js';
    +import {generateCode, generateUserCode} from './python.js';
    +
    +// Tabs
    +document.getElementById('tabs').addEventListener('click', function(e) {
    +	var tab = e.target.getAttribute('data-tab');
    +	if (tab) {
    +		for (let elem of e.target.parentElement.querySelectorAll('[data-tab]')) {
    +			elem.classList.remove('selected');
    +		}
    +		e.target.classList.add('selected');
    +		document.body.setAttribute('data-tab', tab);
    +	}
    +});
    +
    +var workspace = Blockly.inject('blockly', {
    +	toolbox: document.getElementById('toolbox'),
    +	grid: {
    +		spacing: 25,
    +		length: 3,
    +		colour: '#ccc',
    +		snap: true
    +	},
    +	zoom: { controls: true, wheel: true },
    +	media: 'blockly/media/',
    +});
    +
    +function readParams() {
    +	return Promise.all([
    +		ros.readParam('navigate_tolerance', true, 0.2),
    +		ros.readParam('sleep_time', true, 0.2)
    +	]);
    +}
    +
    +var ready = readParams(); // initialization complete promise
    +
    +var pythonArea = document.getElementById('python');
    +
    +// update Python code
    +workspace.addChangeListener(function(e) {
    +	ready.then(function() {
    +		pythonArea.innerHTML = generateUserCode(workspace);
    +		hljs.highlightBlock(pythonArea);
    +	});
    +});
    +
    +var running = false;
    +var runRequest = false;
    +
    +new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'block', messageType: 'std_msgs/String' }).subscribe(function(msg) {
    +	workspace.highlightBlock(msg.data);
    +	runRequest = false;
    +	update();
    +});
    +
    +new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'running' }).subscribe(function(msg) {
    +	running = msg.data;
    +	runRequest = false;
    +	if (!running) {
    +		workspace.highlightBlock('');
    +	}
    +	update();
    +});
    +
    +var notifElem = document.getElementById('notifications');
    +
    +function z(n) { return (n < 10 ? '0' : '') + n; } // add leading zero
    +
    +new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'print', messageType: 'std_msgs/String'}).subscribe(function(msg) {
    +	var d = new Date(); // TODO: use rosgraph_msgs/Log?
    +	var timestamp = `${z(d.getHours())}:${z(d.getMinutes())}:${z(d.getSeconds())}`;
    +	notifElem.innerHTML += `${timestamp}: ${msg.data}\n`;
    +	notifElem.scrollTop = notifElem.scrollHeight;
    +});
    +
    +new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'error', messageType: 'std_msgs/String'}).subscribe(function(msg) {
    +	alert('Error: ' + msg.data);
    +});
    +
    +var runButton = document.getElementById('run');
    +
    +function update() {
    +	document.body.classList.toggle('running', running);
    +	runButton.disabled = !ros.ros.isConnected || runRequest || running;
    +}
    +
    +var shownPrompts = new Set();
    +
    +new ROSLIB.Topic({ ros: ros.ros, name: ros.priv + 'prompt', messageType: 'clover_blocks/Prompt'}).subscribe(function(msg) {
    +	if (shownPrompts.has(msg.id)) return;
    +	shownPrompts.add(msg.id);
    +
    +	var response = prompt(msg.message);
    +	new ROSLIB.Topic({
    +		ros: ros.ros,
    +		name: ros.priv + 'input/' + msg.id,
    +		messageType: 'std_msgs/String',
    +		latch: true
    +	}).publish(new ROSLIB.Message({ data: response }));
    +});
    +
    +window.stopProgram = function() {
    +	ros.stopService.callService(new ROSLIB.ServiceRequest(), function(res) {
    +		if (!res.success) alert(res.message);
    +	}, err => alert(err))
    +}
    +
    +ros.ros.on('connection', update);
    +
    +ros.ros.on('close', update);
    +
    +ready.then(() => runButton.disabled = false);
    +
    +window.runProgram = function() {
    +	if (!confirm('Run program?')) return;
    +
    +	runRequest = true;
    +	update();
    +	var code = generateCode(workspace);
    +	console.log(code);
    +	ros.runService.callService(new ROSLIB.ServiceRequest({ code: code } ), function(res) {
    +		if (!res.success) {
    +			runRequest = false;
    +			update();
    +			alert(res.message);
    +		}
    +	}, function(err) {
    +		runRequest = false;
    +		update();
    +		alert(err);
    +	})
    +}
    +
    +window.land = function() {
    +	window.stopProgram();
    +	ros.landService.callService(new ROSLIB.ServiceRequest(), function(result) {
    +	}, function(err) {
    +		alert('Unable to land: ' + err);
    +	});
    +}
    +
    +function getProgramXml() {
    +	var xmlDom = Blockly.Xml.workspaceToDom(workspace);
    +	return Blockly.Xml.domToPrettyText(xmlDom);
    +}
    +
    +function setProgramXml(xml) {
    +	workspace.clear();
    +	if (xml) {
    +		let xmlDom = Blockly.Xml.textToDom(xml);
    +		Blockly.Xml.domToWorkspace(xmlDom, workspace);
    +	}
    +}
    +
    +workspace.addChangeListener(function(e) {
    +	localStorage.setItem('xml', getProgramXml());
    +});
    +
    +var programSelect = document.querySelector('#program-name');
    +var userPrograms = programSelect.querySelector('optgroup[data-type=user]');
    +var examplePrograms = programSelect.querySelector('optgroup[data-type=example]');
    +
    +var programs = {};
    +var program = '';
    +
    +function loadWorkspace() {
    +	var xml = localStorage.getItem('xml');
    +	if (xml) {
    +		setProgramXml(xml);
    +	}
    +	program = localStorage.getItem('program') || '';
    +}
    +
    +loadWorkspace();
    +
    +function loadPrograms() {
    +	ros.loadService.callService(new ROSLIB.ServiceRequest(), function(res) {
    +		if (!res.success) alert(res.message);
    +
    +		for (let i = 0; i < res.names.length; i++) {
    +			let name = res.names[i];
    +			let program = res.programs[i];
    +			let option = document.createElement('option');
    +			option.innerHTML = res.names[i];
    +			if (name.startsWith('examples/')) {
    +				examplePrograms.appendChild(option);
    +			} else {
    +				userPrograms.appendChild(option);
    +			}
    +
    +			programs[name] = program;
    +		}
    +
    +		if (program) {
    +			programSelect.value = program;
    +		}
    +		updateChanged();
    +	}, function(err) {
    +		document.querySelector('.backend-fail').style.display = 'inline';
    +		alert(`Error loading programs list.\n\nHave you enabled clover_blocks in clover.launch?`);
    +		runButton.disabled = true;
    +	})
    +}
    +
    +loadPrograms();
    +
    +function getProgramName() {
    +	if (programSelect.value.startsWith('@')) {
    +		return ''
    +	}
    +	return programSelect.value;
    +}
    +
    +function updateChanged() {
    +	var name = program;
    +	document.body.classList.toggle('changed', name in programs && (programs[name].trim() != getProgramXml().trim()));
    +}
    +
    +workspace.addChangeListener(function(e) {
    +	if (e instanceof Blockly.Events.Change || e instanceof Blockly.Events.Create || e instanceof Blockly.Events.Delete) {
    +		updateChanged();
    +	}
    +});
    +
    +function saveProgram() {
    +	var name = getProgramName();
    +
    +	if (!name) {
    +		name = prompt('Enter new program name:');
    +		if (!name) {
    +			programSelect.value = program;
    +			return;
    +		}
    +		if (!name.endsWith('.xml')) {
    +			name += '.xml';
    +		}
    +		let option = document.createElement('option');
    +		option.innerHTML = name;
    +		userPrograms.appendChild(option);
    +	}
    +
    +	let xml = getProgramXml();
    +	ros.storeService.callService(new ROSLIB.ServiceRequest({
    +		name: name,
    +		program: xml
    +	}), function(result) {
    +		if (!result.success) {
    +			alert(result.message);
    +			return;
    +		}
    +		console.log(result.message);
    +		programSelect.blur();
    +		program = name;
    +		localStorage.setItem('program', name);
    +		programs[name] = xml;
    +		programSelect.value = program;
    +		updateChanged();
    +	}, function(err) {
    +		// TODO: restore previous state correctly
    +		alert('Unable to store: ' + err);
    +		programSelect.blur();
    +		programSelect.value = program;
    +	});
    +}
    +
    +window.addEventListener('keydown', function(e) {
    +	if ((e.metaKey || e.ctrlKey) && e.key == 's') { // ctrl+s/cmd+s
    +		e.preventDefault();
    +		if (!document.body.classList.contains('changed')) { // if not changed, ignore
    +			return;
    +		}
    +		saveProgram();
    +	}
    +});
    +
    +programSelect.addEventListener('change', function(e) {
    +	if (programSelect.value == '@clear') {
    +		if (!confirm('Clear workspace?')) {
    +			programSelect.value = program;
    +			return;
    +		}
    +		localStorage.removeItem('program');
    +		program = '';
    +		setProgramXml('');
    +		programSelect.value = program;
    +		programSelect.blur();
    +	} else if (programSelect.value == '@save') {
    +		saveProgram();
    +	} else {
    +		// load program
    +		if (program == '' || document.body.classList.contains('changed')) {
    +			if (!confirm('Discard changes?')) {
    +				programSelect.value = program;
    +				return;
    +			}
    +		}
    +		let name = programSelect.value;
    +		let lastProgram = getProgramXml();
    +		programSelect.blur();
    +		try {
    +			setProgramXml(programs[name]);
    +			program = name;
    +			localStorage.setItem('program', name);
    +		} catch(e) {
    +			alert(e);
    +			setProgramXml(lastProgram);
    +			program = ''
    +			programSelect.value = program;
    +		}
    +		updateChanged();
    +	}
    +});
    diff --git a/clover_blocks/www/python.js b/clover_blocks/www/python.js
    new file mode 100644
    index 000000000..16b249e4e
    --- /dev/null
    +++ b/clover_blocks/www/python.js
    @@ -0,0 +1,434 @@
    +import {params} from './ros.js';
    +
    +// If any new block imports any library, add that library name here.
    +Blockly.Python.addReservedWords('_b,_print');
    +Blockly.Python.addReservedWords('rospy,srv,Trigger,get_telemetry,navigate,set_velocity,land');
    +Blockly.Python.addReservedWords('navigate_wait,land_wait,wait_arrival,wait_yaw,get_distance');
    +Blockly.Python.addReservedWords('pigpio,pi,Range');
    +Blockly.Python.addReservedWords('SetLEDEffect,set_effect');
    +Blockly.Python.addReservedWords('SetLEDs,LEDState,set_leds');
    +
    +// TODO: parametrize
    +const navigate_tolerance = 0.2;
    +const sleep_time = 0.2;
    +
    +const IMPORT_SRV = `from clover import srv
    +from std_srvs.srv import Trigger`;
    +
    +// TODO: tolerance to parameters
    +const NAVIGATE_WAIT = () => `\ndef navigate_wait(x=0, y=0, z=0, speed=0.5, frame_id='body', auto_arm=False):
    +    res = navigate(x=x, y=y, z=z, yaw=float('nan'), speed=speed, frame_id=frame_id, auto_arm=auto_arm)
    +
    +    if not res.success:
    +        raise Exception(res.message)
    +
    +    while not rospy.is_shutdown():
    +        telem = get_telemetry(frame_id='navigate_target')
    +        if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_tolerance}:
    +            return
    +        rospy.sleep(${params.sleep_time})\n`;
    +
    +const LAND_WAIT = () => `\ndef land_wait():
    +    land()
    +    while get_telemetry().armed:
    +        rospy.sleep(${params.sleep_time})\n`;
    +
    +// TODO: tolerance to parameters
    +const WAIT_YAW = () => `\ndef wait_yaw():
    +    while not rospy.is_shutdown():
    +        telem = get_telemetry(frame_id='navigate_target')
    +        if abs(telem.yaw) < math.radians(20):
    +            return
    +        rospy.sleep(${params.sleep_time})\n`;
    +
    +// TODO: tolerance to parameters
    +const WAIT_ARRIVAL = () => `\ndef wait_arrival():
    +    while not rospy.is_shutdown():
    +        telem = get_telemetry(frame_id='navigate_target')
    +        if math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_tolerance}:
    +            return
    +        rospy.sleep(${params.sleep_time})\n`;
    +
    +// TODO: tolerance to parameters
    +const ARRIVED = () => `\ndef arrived():
    +    telem = get_telemetry(frame_id='navigate_target')
    +    return math.sqrt(telem.x ** 2 + telem.y ** 2 + telem.z ** 2) < ${params.navigate_tolerance}\n`
    +
    +const GET_DISTANCE = `\ndef get_distance(x, y, z, frame_id):
    +    telem = get_telemetry(frame_id)
    +    return math.sqrt((x - telem.x) ** 2 + (y - telem.y) ** 2 + (z - telem.z) ** 2)\n`;
    +
    +var rosDefinitions = {};
    +
    +function generateROSDefinitions() {
    +	// order for ROS definitions is significant, so generate all ROS definitions as one
    +	var code = `rospy.init_node('flight')\n\n`;
    +	if (rosDefinitions.offboard) {
    +		code += `get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)\n`;
    +		code += `navigate = rospy.ServiceProxy('navigate', srv.Navigate)\n`;
    +		if (rosDefinitions.setVelocity) {
    +			code += `set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)\n`;
    +		}
    +		if (rosDefinitions.setAttitude) {
    +			code += `set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)\n`;
    +		}
    +		if (rosDefinitions.setRates) {
    +			code += `set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)\n`;
    +		}
    +		code += `land = rospy.ServiceProxy('land', Trigger)\n`;
    +	}
    +	if (rosDefinitions.setEffect) {
    +		Blockly.Python.definitions_['import_led_effect'] = 'from clover.srv import SetLEDEffect';
    +		code += `set_effect = rospy.ServiceProxy('led/set_effect', SetLEDEffect, persistent=True)\n`;
    +	}
    +	if (rosDefinitions.setLeds) {
    +		Blockly.Python.definitions_['import_set_led'] = 'from led_msgs.srv import SetLEDs\nfrom led_msgs.msg import LEDState';
    +		code += `set_leds = rospy.ServiceProxy('led/set_leds', SetLEDs, persistent=True)\n`;
    +	}
    +	if (rosDefinitions.navigateWait) {
    +		Blockly.Python.definitions_['import_math'] = 'import math';
    +		code += NAVIGATE_WAIT();
    +	}
    +	if (rosDefinitions.landWait) {
    +		code += LAND_WAIT();
    +	}
    +	if (rosDefinitions.waitArrival) {
    +		Blockly.Python.definitions_['import_math'] = 'import math';
    +		code += WAIT_ARRIVAL();
    +	}
    +	if (rosDefinitions.arrived) {
    +		Blockly.Python.definitions_['import_math'] = 'import math';
    +		code += ARRIVED();
    +	}
    +	if (rosDefinitions.waitYaw) {
    +		Blockly.Python.definitions_['import_math'] = 'import math';
    +		code += WAIT_YAW();
    +	}
    +	if (rosDefinitions.distance) {
    +		Blockly.Python.definitions_['import_math'] = 'import math';
    +		code += GET_DISTANCE;
    +	}
    +	Blockly.Python.definitions_['ros'] = code;
    +}
    +
    +function initNode() {
    +	Blockly.Python.definitions_['import_rospy'] = 'import rospy';
    +	generateROSDefinitions();
    +}
    +
    +function simpleOffboard() {
    +	rosDefinitions.offboard = true;
    +	Blockly.Python.definitions_['import_srv'] = IMPORT_SRV;
    +	initNode();
    +}
    +
    +// Adjust indentation
    +Blockly.Python.INDENT = '    ';
    +
    +export function generateUserCode(workspace) {
    +	rosDefinitions = {};
    +	Blockly.Python.STATEMENT_PREFIX = null;
    +	return Blockly.Python.workspaceToCode(workspace);
    +}
    +
    +export function generateCode(workspace) {
    +	rosDefinitions = {};
    +	Blockly.Python.STATEMENT_PREFIX = '_b(%1)\n';
    +	var code = Blockly.Python.workspaceToCode(workspace);
    +	return code;
    +}
    +
    +function buildFrameId(block) {
    +	let frame = block.getFieldValue('FRAME_ID').toLowerCase();
    +	let id = Blockly.Python.valueToCode(block, 'ID', Blockly.Python.ORDER_NONE);
    +	if (frame == 'aruco') { // aruco marker frame
    +		if (id.match(/^[0-9]+$/)) { // id is positive integer
    +			return `'${frame}_${id}'`;
    +		} else { // something else...
    +			return `'${frame}_' + str(int(${id}))`;
    +		}
    +	} else {
    +		return `'${frame}'`;
    +	}
    +}
    +
    +Blockly.Python.navigate = function(block) {
    +	let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
    +	let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
    +	let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
    +	let frameId = buildFrameId(block);
    +	let speed = Blockly.Python.valueToCode(block, 'SPEED', Blockly.Python.ORDER_NONE);
    +
    +	let params = [`x=${x}`, `y=${y}`, `z=${z}`, `frame_id=${frameId}`, `speed=${speed}`];
    +
    +	simpleOffboard();
    +
    +	if (block.getFieldValue('WAIT') == 'TRUE') {
    +		rosDefinitions.navigateWait = true;
    +		simpleOffboard();
    +
    +		return `navigate_wait(${params.join(', ')})\n`;
    +
    +	} else {
    +		if (frameId != 'body') {
    +			params.push(`yaw=float('nan')`);
    +		}
    +		return `navigate(${params.join(', ')})\n`;
    +	}
    +}
    +
    +Blockly.Python.set_velocity = function(block) {
    +	let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
    +	let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
    +	let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
    +	let frameId = buildFrameId(block);
    +
    +	simpleOffboard();
    +
    +	if (frameId == `'body'`) {
    +		return `set_velocity(vx=${x}, vy=${y}, vz=${z}, frame_id=${frameId})\n`;
    +	} else {
    +		return `set_velocity(vx=${x}, vy=${y}, vz=${z}, yaw=float('nan'), frame_id=${frameId})\n`;
    +	}
    +}
    +
    +Blockly.Python.take_off = function(block) {
    +	simpleOffboard();
    +
    +	let z = Blockly.Python.valueToCode(block, 'ALT', Blockly.Python.ORDER_NONE);
    +
    +	if (block.getFieldValue('WAIT') == 'TRUE') {
    +		rosDefinitions.navigateWait = true;
    +		simpleOffboard();
    +
    +		return `navigate_wait(z=${z}, frame_id='body', auto_arm=True)\n`;
    +	} else {
    +		return `navigate(z=${z}, frame_id='body', auto_arm=True)\n`;
    +	}
    +}
    +
    +Blockly.Python.land = function(block) {
    +	simpleOffboard();
    +
    +	if (block.getFieldValue('WAIT') == 'TRUE') {
    +		rosDefinitions.landWait = true;
    +		simpleOffboard();
    +
    +		return `land_wait()\n`;
    +	} else {
    +		return 'land()\n';
    +	}
    +}
    +
    +Blockly.Python.angle = function(block) {
    +	// return [block.getFieldValue('ANGLE'), Blockly.Python.ORDER_UNARY_SIGN];
    +	Blockly.Python.definitions_['import_math'] = 'import math';
    +	return [`math.radians(${block.getFieldValue('ANGLE')})`, Blockly.Python.ORDER_FUNCTION_CALL];
    +
    +}
    +
    +Blockly.Python.set_yaw = function(block) {
    +	simpleOffboard();
    +	let yaw = Blockly.Python.valueToCode(block, 'YAW', Blockly.Python.ORDER_NONE);
    +	let frameId = buildFrameId(block);
    +	let code = `navigate(x=float('nan'), y=float('nan'), z=float('nan'), yaw=${yaw}, frame_id=${frameId})\n`;
    +	if (block.getFieldValue('WAIT') == 'TRUE') {
    +		rosDefinitions.waitYaw = true;
    +		simpleOffboard();
    +		code += 'wait_yaw()\n';
    +	}
    +	return code;
    +}
    +
    +Blockly.Python.wait_arrival = function(block) {
    +	rosDefinitions.waitArrival = true;
    +	simpleOffboard();
    +	return 'wait_arrival()\n';
    +}
    +
    +Blockly.Python.get_time = function(block) {
    +	initNode();
    +	return ['rospy.get_time()', Blockly.Python.ORDER_FUNCTION_CALL];
    +}
    +
    +Blockly.Python.arrived = function(block) {
    +	rosDefinitions.arrived = true;
    +	simpleOffboard();
    +	return ['arrived()', Blockly.Python.ORDER_FUNCTION_CALL];
    +}
    +
    +Blockly.Python.wait = function(block) {
    +	initNode();
    +	return `rospy.sleep(${Blockly.Python.valueToCode(block, 'TIME', Blockly.Python.ORDER_NONE)})\n`;
    +}
    +
    +Blockly.Python.setpoint = function(block) {
    +	var type = block.getFieldValue('TYPE');
    +	let frameId = buildFrameId(block);
    +	let vx = Blockly.Python.valueToCode(block, 'VX', Blockly.Python.ORDER_NONE);
    +	let vy = Blockly.Python.valueToCode(block, 'VY', Blockly.Python.ORDER_NONE);
    +	let vz = Blockly.Python.valueToCode(block, 'VZ', Blockly.Python.ORDER_NONE);
    +	let yaw = Blockly.Python.valueToCode(block, 'YAW', Blockly.Python.ORDER_NONE);
    +	let pitch = Blockly.Python.valueToCode(block, 'PITCH', Blockly.Python.ORDER_NONE);
    +	let roll = Blockly.Python.valueToCode(block, 'ROLL', Blockly.Python.ORDER_NONE);
    +	let thrust = Blockly.Python.valueToCode(block, 'THRUST', Blockly.Python.ORDER_NONE);
    +
    +	if (type == 'VELOCITY') {
    +		rosDefinitions.setVelocity = true;
    +		simpleOffboard();
    +		return `set_velocity(vx=${vx}, vy=${vy}, vz=${vz}, frame_id=${frameId}, yaw=float('nan'))\n`;
    +	} else if (type == 'ATTITUDE') {
    +		rosDefinitions.setAttitude = true;
    +		simpleOffboard();
    +		return `set_attitude(pitch=${pitch}, roll=${roll}, yaw=${yaw}, thrust=${thrust}, frame_id=${frameId})\n`;
    +	} else if (type == 'RATES') {
    +		rosDefinitions.setRates = true;
    +		simpleOffboard();
    +		return `set_rates(pitch_rate=${pitch}, roll_rate=${roll}, yaw_rate=${yaw}, thrust=${thrust})\n`;
    +	}
    +}
    +
    +Blockly.Python.get_position = function(block) {
    +	simpleOffboard();
    +	let frameId = buildFrameId(block);
    +	var code = `get_telemetry(${frameId}).${block.getFieldValue('FIELD').toLowerCase()}`;
    +	return [code, Blockly.Python.ORDER_FUNCTION_CALL];
    +}
    +
    +Blockly.Python.get_yaw = function(block) {
    +	simpleOffboard();
    +	Blockly.Python.definitions_['import_math'] = 'import math';
    +	let frameId = buildFrameId(block);
    +	var code = `math.degrees(get_telemetry(${frameId}).yaw)`;
    +	return [code, Blockly.Python.ORDER_FUNCTION_CALL];
    +}
    +
    +Blockly.Python.get_attitude = function(block) {
    +	simpleOffboard();
    +	Blockly.Python.definitions_['import_math'] = 'import math';
    +	var code = `math.degrees(get_telemetry().${block.getFieldValue('FIELD').toLowerCase()})`;
    +	return [code, Blockly.Python.ORDER_FUNCTION_CALL];
    +}
    +
    +Blockly.Python.distance = function(block) {
    +	rosDefinitions.distance = true;
    +	simpleOffboard();
    +
    +	let x = Blockly.Python.valueToCode(block, 'X', Blockly.Python.ORDER_NONE);
    +	let y = Blockly.Python.valueToCode(block, 'Y', Blockly.Python.ORDER_NONE);
    +	let z = Blockly.Python.valueToCode(block, 'Z', Blockly.Python.ORDER_NONE);
    +	let frameId = buildFrameId(block);
    +
    +	return [`get_distance(${x}, ${y}, ${z}, ${frameId})`, Blockly.Python.ORDER_FUNCTION_CALL]
    +}
    +
    +Blockly.Python.rangefinder_distance = function(block) {
    +	initNode();
    +	Blockly.Python.definitions_['import_range'] = 'from sensor_msgs.msg import Range';
    +	return [`rospy.wait_for_message('rangefinder/range', Range).range`, Blockly.Python.ORDER_FUNCTION_CALL]
    +}
    +
    +Blockly.Python.mode = function(block) {
    +	simpleOffboard();
    +	return [`get_telemetry().mode`, Blockly.Python.ORDER_FUNCTION_CALL]
    +}
    +
    +Blockly.Python.armed = function(block) {
    +	simpleOffboard();
    +	return [`get_telemetry().armed`, Blockly.Python.ORDER_FUNCTION_CALL]
    +}
    +
    +Blockly.Python.voltage = function(block) {
    +	simpleOffboard();
    +	var code = `get_telemetry().${block.getFieldValue('TYPE').toLowerCase()}`;
    +	return [code, Blockly.Python.ORDER_FUNCTION_CALL];
    +}
    +
    +function parseColor(color) {
    +	return {
    +		r: parseInt(color.substr(2, 2), 16),
    +		g: parseInt(color.substr(4, 2), 16),
    +		b: parseInt(color.substr(6, 2), 16)
    +	}
    +}
    +
    +const PARSE_COLOR = `def ${Blockly.Python.FUNCTION_NAME_PLACEHOLDER_}(color):
    +  return {'r': int(color[1:3], 16), 'g': int(color[3:5], 16), 'b': int(color[5:7], 16)}`;
    +
    +// TODO: weird code with colour_rgb block
    +Blockly.Python.set_effect = function(block) {
    +	rosDefinitions.setEffect = true;
    +	initNode();
    +
    +	var effect = block.getFieldValue('EFFECT').toLowerCase();
    +
    +	if (effect == 'rainbow' || effect == 'rainbow_fill') {
    +		return `set_effect(effect='${effect}')\n`;
    +	} else {
    +		let colorCode = Blockly.Python.valueToCode(block, 'COLOR', Blockly.Python.ORDER_NONE);
    +
    +		if (/^'(.*)'$/.test(colorCode)) { // is simple string
    +			let color = parseColor(colorCode);
    +			return `set_effect(effect='${effect}', r=${color.r}, g=${color.g}, b=${color.b})\n`;
    +		} else {
    +			let parseColor = Blockly.Python.provideFunction_('parse_color', [PARSE_COLOR]);
    +			return `set_effect(effect='${effect}', **${parseColor}(${colorCode}))\n`;
    +		}
    +	}
    +}
    +
    +Blockly.Python.set_led = function(block) {
    +	rosDefinitions.setLeds = true;
    +	initNode();
    +
    +	var index = Blockly.Python.valueToCode(block, 'INDEX', Blockly.Python.ORDER_NONE);
    +	var colorCode = Blockly.Python.valueToCode(block, 'COLOR', Blockly.Python.ORDER_NONE);
    +
    +	if (/^'(.*)'$/.test(colorCode)) { // is simple string
    +		let color = parseColor(colorCode);
    +		return `set_leds([LEDState(index=${index}, r=${color.r}, g=${color.g}, b=${color.b})])\n`;
    +	} else {
    +		let parseColor = Blockly.Python.provideFunction_('parse_color', [PARSE_COLOR]);
    +		return `set_leds([LEDState(index=${index}, **${parseColor}(${colorCode})])\n`;
    +	}
    +}
    +
    +function pigpio() {
    +	Blockly.Python.definitions_['import_pigpio'] = 'import pigpio';
    +	Blockly.Python.definitions_['init_pigpio'] = 'pi = pigpio.pi()';
    +}
    +
    +const GPIO_READ = `\ndef gpio_read(pin):
    +	pi.set_mode(pin, pigpio.INPUT)
    +	return pi.read(pin)\n`;
    +
    +const GPIO_WRITE = `\ndef gpio_write(pin, level):
    +	pi.set_mode(pin, pigpio.OUTPUT)
    +	pi.write(pin, level)\n`;
    +
    +const SET_SERVO = `\ndef set_servo(pin, pwm):
    +	pi.set_mode(pin, pigpio.OUTPUT)
    +	pi.set_servo_pulsewidth(pin, pwm)\n`;
    +
    +Blockly.Python.gpio_read = function(block) {
    +	pigpio();
    +	Blockly.Python.definitions_['gpio_read'] = GPIO_READ;
    +	var pin = Blockly.Python.valueToCode(block, 'PIN', Blockly.Python.ORDER_NONE);
    +	return [`gpio_read(${pin})`, Blockly.Python.ORDER_FUNCTION_CALL];
    +}
    +
    +Blockly.Python.gpio_write = function(block) {
    +	pigpio();
    +	Blockly.Python.definitions_['gpio_write'] = GPIO_WRITE;
    +	var pin = Blockly.Python.valueToCode(block, 'PIN', Blockly.Python.ORDER_NONE);
    +	var level = Blockly.Python.valueToCode(block, 'LEVEL', Blockly.Python.ORDER_NONE);
    +	return `gpio_write(${pin}, ${level})\n`;
    +}
    +
    +Blockly.Python.set_servo = function(block) {
    +	pigpio();
    +	Blockly.Python.definitions_['set_servo'] = SET_SERVO;
    +	var pin = Blockly.Python.valueToCode(block, 'PIN', Blockly.Python.ORDER_NONE);
    +	var pwm = Blockly.Python.valueToCode(block, 'PWM', Blockly.Python.ORDER_NONE);
    +	return `set_servo(${pin}, ${pwm})\n`;
    +}
    diff --git a/clover_blocks/www/ros.js b/clover_blocks/www/ros.js
    new file mode 100644
    index 000000000..313109f20
    --- /dev/null
    +++ b/clover_blocks/www/ros.js
    @@ -0,0 +1,59 @@
    +var url = 'ws://' + location.hostname + ':9090';
    +export var ros = new ROSLIB.Ros({ url });
    +
    +ros.on('connection', function () {
    +	console.log('connection')
    +	document.body.classList.add('connected');
    +});
    +
    +ros.on('close', function () {
    +	console.log('close')
    +	document.body.classList.remove('connected');
    +	setTimeout(function() {
    +		ros.connect(url);
    +	}, 2000);
    +});
    +
    +ros.on('error', function(err) {
    +});
    +
    +export const namespace = '/';
    +export const priv = namespace + 'clover_blocks/';
    +
    +export var params = {}; // parameters storage
    +
    +export function readParam(name, fromUrl, _default) {
    +	return new Promise(function(resolve, reject) {
    +		if (params[name] !== undefined) {
    +			resolve();
    +			return;
    +		}
    +		// read from url
    +		if (fromUrl && ((params[name] = new URL(window.location.href).searchParams.get(name)) !== null)) {
    +			resolve();
    +			return;
    +		}
    +		// read from ROS params
    +		new ROSLIB.Param({ ros: ros, name: priv + name }).get(function(val) {
    +			if (val === null) {
    +				if (_default === undefined) {
    +					alert('Cannot read required parameter ' + name);
    +					reject();
    +				} else {
    +					params[name] = _default;
    +					resolve();
    +				}
    +				return;
    +			}
    +			params[name] = val;
    +			resolve();
    +		})
    +	});
    +}
    +
    +
    +export var runService = new ROSLIB.Service({ ros: ros, name: priv + 'run', serviceType: 'clover_blocks/Run' });
    +export var stopService = new ROSLIB.Service({ ros: ros, name: priv + 'stop', serviceType: 'std_srvs/Trigger' });
    +export var loadService = new ROSLIB.Service({ ros: ros, name : priv + 'load', serviceType : 'clover_blocks/Load' });
    +export var storeService = new ROSLIB.Service({ ros: ros, name : priv + 'store', serviceType : 'clover_blocks/Store' });
    +export var landService = new ROSLIB.Service({ ros: ros, name : namespace + 'land', serviceType : 'std_srvs/Trigger' });
    diff --git a/clover_blocks/www/roslib.js b/clover_blocks/www/roslib.js
    new file mode 100644
    index 000000000..c8628ede7
    --- /dev/null
    +++ b/clover_blocks/www/roslib.js
    @@ -0,0 +1,4560 @@
    +(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i
    + *
    + * Permission is hereby granted, free of charge, to any person obtaining a copy
    + * of this software and associated documentation files (the "Software"), to deal
    + * in the Software without restriction, including without limitation the rights
    + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    + * copies of the Software, and to permit persons to whom the Software is
    + * furnished to do so, subject to the following conditions:
    + *
    + * The above copyright notice and this permission notice shall be included in all
    + * copies or substantial portions of the Software.
    + *
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    + * SOFTWARE.
    + */
    +
    +(function(global, undefined) { "use strict";
    +var POW_2_24 = Math.pow(2, -24),
    +    POW_2_32 = Math.pow(2, 32),
    +    POW_2_53 = Math.pow(2, 53);
    +
    +function encode(value) {
    +  var data = new ArrayBuffer(256);
    +  var dataView = new DataView(data);
    +  var lastLength;
    +  var offset = 0;
    +
    +  function ensureSpace(length) {
    +    var newByteLength = data.byteLength;
    +    var requiredLength = offset + length;
    +    while (newByteLength < requiredLength)
    +      newByteLength *= 2;
    +    if (newByteLength !== data.byteLength) {
    +      var oldDataView = dataView;
    +      data = new ArrayBuffer(newByteLength);
    +      dataView = new DataView(data);
    +      var uint32count = (offset + 3) >> 2;
    +      for (var i = 0; i < uint32count; ++i)
    +        dataView.setUint32(i * 4, oldDataView.getUint32(i * 4));
    +    }
    +
    +    lastLength = length;
    +    return dataView;
    +  }
    +  function write() {
    +    offset += lastLength;
    +  }
    +  function writeFloat64(value) {
    +    write(ensureSpace(8).setFloat64(offset, value));
    +  }
    +  function writeUint8(value) {
    +    write(ensureSpace(1).setUint8(offset, value));
    +  }
    +  function writeUint8Array(value) {
    +    var dataView = ensureSpace(value.length);
    +    for (var i = 0; i < value.length; ++i)
    +      dataView.setUint8(offset + i, value[i]);
    +    write();
    +  }
    +  function writeUint16(value) {
    +    write(ensureSpace(2).setUint16(offset, value));
    +  }
    +  function writeUint32(value) {
    +    write(ensureSpace(4).setUint32(offset, value));
    +  }
    +  function writeUint64(value) {
    +    var low = value % POW_2_32;
    +    var high = (value - low) / POW_2_32;
    +    var dataView = ensureSpace(8);
    +    dataView.setUint32(offset, high);
    +    dataView.setUint32(offset + 4, low);
    +    write();
    +  }
    +  function writeTypeAndLength(type, length) {
    +    if (length < 24) {
    +      writeUint8(type << 5 | length);
    +    } else if (length < 0x100) {
    +      writeUint8(type << 5 | 24);
    +      writeUint8(length);
    +    } else if (length < 0x10000) {
    +      writeUint8(type << 5 | 25);
    +      writeUint16(length);
    +    } else if (length < 0x100000000) {
    +      writeUint8(type << 5 | 26);
    +      writeUint32(length);
    +    } else {
    +      writeUint8(type << 5 | 27);
    +      writeUint64(length);
    +    }
    +  }
    +
    +  function encodeItem(value) {
    +    var i;
    +
    +    if (value === false)
    +      return writeUint8(0xf4);
    +    if (value === true)
    +      return writeUint8(0xf5);
    +    if (value === null)
    +      return writeUint8(0xf6);
    +    if (value === undefined)
    +      return writeUint8(0xf7);
    +
    +    switch (typeof value) {
    +      case "number":
    +        if (Math.floor(value) === value) {
    +          if (0 <= value && value <= POW_2_53)
    +            return writeTypeAndLength(0, value);
    +          if (-POW_2_53 <= value && value < 0)
    +            return writeTypeAndLength(1, -(value + 1));
    +        }
    +        writeUint8(0xfb);
    +        return writeFloat64(value);
    +
    +      case "string":
    +        var utf8data = [];
    +        for (i = 0; i < value.length; ++i) {
    +          var charCode = value.charCodeAt(i);
    +          if (charCode < 0x80) {
    +            utf8data.push(charCode);
    +          } else if (charCode < 0x800) {
    +            utf8data.push(0xc0 | charCode >> 6);
    +            utf8data.push(0x80 | charCode & 0x3f);
    +          } else if (charCode < 0xd800) {
    +            utf8data.push(0xe0 | charCode >> 12);
    +            utf8data.push(0x80 | (charCode >> 6)  & 0x3f);
    +            utf8data.push(0x80 | charCode & 0x3f);
    +          } else {
    +            charCode = (charCode & 0x3ff) << 10;
    +            charCode |= value.charCodeAt(++i) & 0x3ff;
    +            charCode += 0x10000;
    +
    +            utf8data.push(0xf0 | charCode >> 18);
    +            utf8data.push(0x80 | (charCode >> 12)  & 0x3f);
    +            utf8data.push(0x80 | (charCode >> 6)  & 0x3f);
    +            utf8data.push(0x80 | charCode & 0x3f);
    +          }
    +        }
    +
    +        writeTypeAndLength(3, utf8data.length);
    +        return writeUint8Array(utf8data);
    +
    +      default:
    +        var length;
    +        if (Array.isArray(value)) {
    +          length = value.length;
    +          writeTypeAndLength(4, length);
    +          for (i = 0; i < length; ++i)
    +            encodeItem(value[i]);
    +        } else if (value instanceof Uint8Array) {
    +          writeTypeAndLength(2, value.length);
    +          writeUint8Array(value);
    +        } else {
    +          var keys = Object.keys(value);
    +          length = keys.length;
    +          writeTypeAndLength(5, length);
    +          for (i = 0; i < length; ++i) {
    +            var key = keys[i];
    +            encodeItem(key);
    +            encodeItem(value[key]);
    +          }
    +        }
    +    }
    +  }
    +
    +  encodeItem(value);
    +
    +  if ("slice" in data)
    +    return data.slice(0, offset);
    +
    +  var ret = new ArrayBuffer(offset);
    +  var retView = new DataView(ret);
    +  for (var i = 0; i < offset; ++i)
    +    retView.setUint8(i, dataView.getUint8(i));
    +  return ret;
    +}
    +
    +function decode(data, tagger, simpleValue) {
    +  var dataView = new DataView(data);
    +  var offset = 0;
    +
    +  if (typeof tagger !== "function")
    +    tagger = function(value) { return value; };
    +  if (typeof simpleValue !== "function")
    +    simpleValue = function() { return undefined; };
    +
    +  function read(value, length) {
    +    offset += length;
    +    return value;
    +  }
    +  function readArrayBuffer(length) {
    +    return read(new Uint8Array(data, offset, length), length);
    +  }
    +  function readFloat16() {
    +    var tempArrayBuffer = new ArrayBuffer(4);
    +    var tempDataView = new DataView(tempArrayBuffer);
    +    var value = readUint16();
    +
    +    var sign = value & 0x8000;
    +    var exponent = value & 0x7c00;
    +    var fraction = value & 0x03ff;
    +
    +    if (exponent === 0x7c00)
    +      exponent = 0xff << 10;
    +    else if (exponent !== 0)
    +      exponent += (127 - 15) << 10;
    +    else if (fraction !== 0)
    +      return fraction * POW_2_24;
    +
    +    tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13);
    +    return tempDataView.getFloat32(0);
    +  }
    +  function readFloat32() {
    +    return read(dataView.getFloat32(offset), 4);
    +  }
    +  function readFloat64() {
    +    return read(dataView.getFloat64(offset), 8);
    +  }
    +  function readUint8() {
    +    return read(dataView.getUint8(offset), 1);
    +  }
    +  function readUint16() {
    +    return read(dataView.getUint16(offset), 2);
    +  }
    +  function readUint32() {
    +    return read(dataView.getUint32(offset), 4);
    +  }
    +  function readUint64() {
    +    return readUint32() * POW_2_32 + readUint32();
    +  }
    +  function readBreak() {
    +    if (dataView.getUint8(offset) !== 0xff)
    +      return false;
    +    offset += 1;
    +    return true;
    +  }
    +  function readLength(additionalInformation) {
    +    if (additionalInformation < 24)
    +      return additionalInformation;
    +    if (additionalInformation === 24)
    +      return readUint8();
    +    if (additionalInformation === 25)
    +      return readUint16();
    +    if (additionalInformation === 26)
    +      return readUint32();
    +    if (additionalInformation === 27)
    +      return readUint64();
    +    if (additionalInformation === 31)
    +      return -1;
    +    throw "Invalid length encoding";
    +  }
    +  function readIndefiniteStringLength(majorType) {
    +    var initialByte = readUint8();
    +    if (initialByte === 0xff)
    +      return -1;
    +    var length = readLength(initialByte & 0x1f);
    +    if (length < 0 || (initialByte >> 5) !== majorType)
    +      throw "Invalid indefinite length element";
    +    return length;
    +  }
    +
    +  function appendUtf16data(utf16data, length) {
    +    for (var i = 0; i < length; ++i) {
    +      var value = readUint8();
    +      if (value & 0x80) {
    +        if (value < 0xe0) {
    +          value = (value & 0x1f) <<  6
    +                | (readUint8() & 0x3f);
    +          length -= 1;
    +        } else if (value < 0xf0) {
    +          value = (value & 0x0f) << 12
    +                | (readUint8() & 0x3f) << 6
    +                | (readUint8() & 0x3f);
    +          length -= 2;
    +        } else {
    +          value = (value & 0x0f) << 18
    +                | (readUint8() & 0x3f) << 12
    +                | (readUint8() & 0x3f) << 6
    +                | (readUint8() & 0x3f);
    +          length -= 3;
    +        }
    +      }
    +
    +      if (value < 0x10000) {
    +        utf16data.push(value);
    +      } else {
    +        value -= 0x10000;
    +        utf16data.push(0xd800 | (value >> 10));
    +        utf16data.push(0xdc00 | (value & 0x3ff));
    +      }
    +    }
    +  }
    +
    +  function decodeItem() {
    +    var initialByte = readUint8();
    +    var majorType = initialByte >> 5;
    +    var additionalInformation = initialByte & 0x1f;
    +    var i;
    +    var length;
    +
    +    if (majorType === 7) {
    +      switch (additionalInformation) {
    +        case 25:
    +          return readFloat16();
    +        case 26:
    +          return readFloat32();
    +        case 27:
    +          return readFloat64();
    +      }
    +    }
    +
    +    length = readLength(additionalInformation);
    +    if (length < 0 && (majorType < 2 || 6 < majorType))
    +      throw "Invalid length";
    +
    +    switch (majorType) {
    +      case 0:
    +        return length;
    +      case 1:
    +        return -1 - length;
    +      case 2:
    +        if (length < 0) {
    +          var elements = [];
    +          var fullArrayLength = 0;
    +          while ((length = readIndefiniteStringLength(majorType)) >= 0) {
    +            fullArrayLength += length;
    +            elements.push(readArrayBuffer(length));
    +          }
    +          var fullArray = new Uint8Array(fullArrayLength);
    +          var fullArrayOffset = 0;
    +          for (i = 0; i < elements.length; ++i) {
    +            fullArray.set(elements[i], fullArrayOffset);
    +            fullArrayOffset += elements[i].length;
    +          }
    +          return fullArray;
    +        }
    +        return readArrayBuffer(length);
    +      case 3:
    +        var utf16data = [];
    +        if (length < 0) {
    +          while ((length = readIndefiniteStringLength(majorType)) >= 0)
    +            appendUtf16data(utf16data, length);
    +        } else
    +          appendUtf16data(utf16data, length);
    +        return String.fromCharCode.apply(null, utf16data);
    +      case 4:
    +        var retArray;
    +        if (length < 0) {
    +          retArray = [];
    +          while (!readBreak())
    +            retArray.push(decodeItem());
    +        } else {
    +          retArray = new Array(length);
    +          for (i = 0; i < length; ++i)
    +            retArray[i] = decodeItem();
    +        }
    +        return retArray;
    +      case 5:
    +        var retObject = {};
    +        for (i = 0; i < length || length < 0 && !readBreak(); ++i) {
    +          var key = decodeItem();
    +          retObject[key] = decodeItem();
    +        }
    +        return retObject;
    +      case 6:
    +        return tagger(decodeItem(), length);
    +      case 7:
    +        switch (length) {
    +          case 20:
    +            return false;
    +          case 21:
    +            return true;
    +          case 22:
    +            return null;
    +          case 23:
    +            return undefined;
    +          default:
    +            return simpleValue(length);
    +        }
    +    }
    +  }
    +
    +  var ret = decodeItem();
    +  if (offset !== data.byteLength)
    +    throw "Remaining bytes";
    +  return ret;
    +}
    +
    +var obj = { encode: encode, decode: decode };
    +
    +if (typeof define === "function" && define.amd)
    +  define("cbor/cbor", obj);
    +else if (typeof module !== 'undefined' && module.exports)
    +  module.exports = obj;
    +else if (!global.CBOR)
    +  global.CBOR = obj;
    +
    +})(this);
    +
    +},{}],2:[function(require,module,exports){
    +(function (process){
    +/*!
    + * EventEmitter2
    + * https://github.com/hij1nx/EventEmitter2
    + *
    + * Copyright (c) 2013 hij1nx
    + * Licensed under the MIT license.
    + */
    +;!function(undefined) {
    +
    +  var isArray = Array.isArray ? Array.isArray : function _isArray(obj) {
    +    return Object.prototype.toString.call(obj) === "[object Array]";
    +  };
    +  var defaultMaxListeners = 10;
    +
    +  function init() {
    +    this._events = {};
    +    if (this._conf) {
    +      configure.call(this, this._conf);
    +    }
    +  }
    +
    +  function configure(conf) {
    +    if (conf) {
    +      this._conf = conf;
    +
    +      conf.delimiter && (this.delimiter = conf.delimiter);
    +      this._maxListeners = conf.maxListeners !== undefined ? conf.maxListeners : defaultMaxListeners;
    +
    +      conf.wildcard && (this.wildcard = conf.wildcard);
    +      conf.newListener && (this.newListener = conf.newListener);
    +      conf.verboseMemoryLeak && (this.verboseMemoryLeak = conf.verboseMemoryLeak);
    +
    +      if (this.wildcard) {
    +        this.listenerTree = {};
    +      }
    +    } else {
    +      this._maxListeners = defaultMaxListeners;
    +    }
    +  }
    +
    +  function logPossibleMemoryLeak(count, eventName) {
    +    var errorMsg = '(node) warning: possible EventEmitter memory ' +
    +        'leak detected. ' + count + ' listeners added. ' +
    +        'Use emitter.setMaxListeners() to increase limit.';
    +
    +    if(this.verboseMemoryLeak){
    +      errorMsg += ' Event name: ' + eventName + '.';
    +    }
    +
    +    if(typeof process !== 'undefined' && process.emitWarning){
    +      var e = new Error(errorMsg);
    +      e.name = 'MaxListenersExceededWarning';
    +      e.emitter = this;
    +      e.count = count;
    +      process.emitWarning(e);
    +    } else {
    +      console.error(errorMsg);
    +
    +      if (console.trace){
    +        console.trace();
    +      }
    +    }
    +  }
    +
    +  function EventEmitter(conf) {
    +    this._events = {};
    +    this.newListener = false;
    +    this.verboseMemoryLeak = false;
    +    configure.call(this, conf);
    +  }
    +  EventEmitter.EventEmitter2 = EventEmitter; // backwards compatibility for exporting EventEmitter property
    +
    +  //
    +  // Attention, function return type now is array, always !
    +  // It has zero elements if no any matches found and one or more
    +  // elements (leafs) if there are matches
    +  //
    +  function searchListenerTree(handlers, type, tree, i) {
    +    if (!tree) {
    +      return [];
    +    }
    +    var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached,
    +        typeLength = type.length, currentType = type[i], nextType = type[i+1];
    +    if (i === typeLength && tree._listeners) {
    +      //
    +      // If at the end of the event(s) list and the tree has listeners
    +      // invoke those listeners.
    +      //
    +      if (typeof tree._listeners === 'function') {
    +        handlers && handlers.push(tree._listeners);
    +        return [tree];
    +      } else {
    +        for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) {
    +          handlers && handlers.push(tree._listeners[leaf]);
    +        }
    +        return [tree];
    +      }
    +    }
    +
    +    if ((currentType === '*' || currentType === '**') || tree[currentType]) {
    +      //
    +      // If the event emitted is '*' at this part
    +      // or there is a concrete match at this patch
    +      //
    +      if (currentType === '*') {
    +        for (branch in tree) {
    +          if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
    +            listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1));
    +          }
    +        }
    +        return listeners;
    +      } else if(currentType === '**') {
    +        endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*'));
    +        if(endReached && tree._listeners) {
    +          // The next element has a _listeners, add it to the handlers.
    +          listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength));
    +        }
    +
    +        for (branch in tree) {
    +          if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
    +            if(branch === '*' || branch === '**') {
    +              if(tree[branch]._listeners && !endReached) {
    +                listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength));
    +              }
    +              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
    +            } else if(branch === nextType) {
    +              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2));
    +            } else {
    +              // No match on this one, shift into the tree but not in the type array.
    +              listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
    +            }
    +          }
    +        }
    +        return listeners;
    +      }
    +
    +      listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1));
    +    }
    +
    +    xTree = tree['*'];
    +    if (xTree) {
    +      //
    +      // If the listener tree will allow any match for this part,
    +      // then recursively explore all branches of the tree
    +      //
    +      searchListenerTree(handlers, type, xTree, i+1);
    +    }
    +
    +    xxTree = tree['**'];
    +    if(xxTree) {
    +      if(i < typeLength) {
    +        if(xxTree._listeners) {
    +          // If we have a listener on a '**', it will catch all, so add its handler.
    +          searchListenerTree(handlers, type, xxTree, typeLength);
    +        }
    +
    +        // Build arrays of matching next branches and others.
    +        for(branch in xxTree) {
    +          if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) {
    +            if(branch === nextType) {
    +              // We know the next element will match, so jump twice.
    +              searchListenerTree(handlers, type, xxTree[branch], i+2);
    +            } else if(branch === currentType) {
    +              // Current node matches, move into the tree.
    +              searchListenerTree(handlers, type, xxTree[branch], i+1);
    +            } else {
    +              isolatedBranch = {};
    +              isolatedBranch[branch] = xxTree[branch];
    +              searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1);
    +            }
    +          }
    +        }
    +      } else if(xxTree._listeners) {
    +        // We have reached the end and still on a '**'
    +        searchListenerTree(handlers, type, xxTree, typeLength);
    +      } else if(xxTree['*'] && xxTree['*']._listeners) {
    +        searchListenerTree(handlers, type, xxTree['*'], typeLength);
    +      }
    +    }
    +
    +    return listeners;
    +  }
    +
    +  function growListenerTree(type, listener) {
    +
    +    type = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
    +
    +    //
    +    // Looks for two consecutive '**', if so, don't add the event at all.
    +    //
    +    for(var i = 0, len = type.length; i+1 < len; i++) {
    +      if(type[i] === '**' && type[i+1] === '**') {
    +        return;
    +      }
    +    }
    +
    +    var tree = this.listenerTree;
    +    var name = type.shift();
    +
    +    while (name !== undefined) {
    +
    +      if (!tree[name]) {
    +        tree[name] = {};
    +      }
    +
    +      tree = tree[name];
    +
    +      if (type.length === 0) {
    +
    +        if (!tree._listeners) {
    +          tree._listeners = listener;
    +        }
    +        else {
    +          if (typeof tree._listeners === 'function') {
    +            tree._listeners = [tree._listeners];
    +          }
    +
    +          tree._listeners.push(listener);
    +
    +          if (
    +            !tree._listeners.warned &&
    +            this._maxListeners > 0 &&
    +            tree._listeners.length > this._maxListeners
    +          ) {
    +            tree._listeners.warned = true;
    +            logPossibleMemoryLeak.call(this, tree._listeners.length, name);
    +          }
    +        }
    +        return true;
    +      }
    +      name = type.shift();
    +    }
    +    return true;
    +  }
    +
    +  // By default EventEmitters will print a warning if more than
    +  // 10 listeners are added to it. This is a useful default which
    +  // helps finding memory leaks.
    +  //
    +  // Obviously not all Emitters should be limited to 10. This function allows
    +  // that to be increased. Set to zero for unlimited.
    +
    +  EventEmitter.prototype.delimiter = '.';
    +
    +  EventEmitter.prototype.setMaxListeners = function(n) {
    +    if (n !== undefined) {
    +      this._maxListeners = n;
    +      if (!this._conf) this._conf = {};
    +      this._conf.maxListeners = n;
    +    }
    +  };
    +
    +  EventEmitter.prototype.event = '';
    +
    +
    +  EventEmitter.prototype.once = function(event, fn) {
    +    return this._once(event, fn, false);
    +  };
    +
    +  EventEmitter.prototype.prependOnceListener = function(event, fn) {
    +    return this._once(event, fn, true);
    +  };
    +
    +  EventEmitter.prototype._once = function(event, fn, prepend) {
    +    this._many(event, 1, fn, prepend);
    +    return this;
    +  };
    +
    +  EventEmitter.prototype.many = function(event, ttl, fn) {
    +    return this._many(event, ttl, fn, false);
    +  }
    +
    +  EventEmitter.prototype.prependMany = function(event, ttl, fn) {
    +    return this._many(event, ttl, fn, true);
    +  }
    +
    +  EventEmitter.prototype._many = function(event, ttl, fn, prepend) {
    +    var self = this;
    +
    +    if (typeof fn !== 'function') {
    +      throw new Error('many only accepts instances of Function');
    +    }
    +
    +    function listener() {
    +      if (--ttl === 0) {
    +        self.off(event, listener);
    +      }
    +      return fn.apply(this, arguments);
    +    }
    +
    +    listener._origin = fn;
    +
    +    this._on(event, listener, prepend);
    +
    +    return self;
    +  };
    +
    +  EventEmitter.prototype.emit = function() {
    +
    +    this._events || init.call(this);
    +
    +    var type = arguments[0];
    +
    +    if (type === 'newListener' && !this.newListener) {
    +      if (!this._events.newListener) {
    +        return false;
    +      }
    +    }
    +
    +    var al = arguments.length;
    +    var args,l,i,j;
    +    var handler;
    +
    +    if (this._all && this._all.length) {
    +      handler = this._all.slice();
    +      if (al > 3) {
    +        args = new Array(al);
    +        for (j = 0; j < al; j++) args[j] = arguments[j];
    +      }
    +
    +      for (i = 0, l = handler.length; i < l; i++) {
    +        this.event = type;
    +        switch (al) {
    +        case 1:
    +          handler[i].call(this, type);
    +          break;
    +        case 2:
    +          handler[i].call(this, type, arguments[1]);
    +          break;
    +        case 3:
    +          handler[i].call(this, type, arguments[1], arguments[2]);
    +          break;
    +        default:
    +          handler[i].apply(this, args);
    +        }
    +      }
    +    }
    +
    +    if (this.wildcard) {
    +      handler = [];
    +      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
    +      searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
    +    } else {
    +      handler = this._events[type];
    +      if (typeof handler === 'function') {
    +        this.event = type;
    +        switch (al) {
    +        case 1:
    +          handler.call(this);
    +          break;
    +        case 2:
    +          handler.call(this, arguments[1]);
    +          break;
    +        case 3:
    +          handler.call(this, arguments[1], arguments[2]);
    +          break;
    +        default:
    +          args = new Array(al - 1);
    +          for (j = 1; j < al; j++) args[j - 1] = arguments[j];
    +          handler.apply(this, args);
    +        }
    +        return true;
    +      } else if (handler) {
    +        // need to make copy of handlers because list can change in the middle
    +        // of emit call
    +        handler = handler.slice();
    +      }
    +    }
    +
    +    if (handler && handler.length) {
    +      if (al > 3) {
    +        args = new Array(al - 1);
    +        for (j = 1; j < al; j++) args[j - 1] = arguments[j];
    +      }
    +      for (i = 0, l = handler.length; i < l; i++) {
    +        this.event = type;
    +        switch (al) {
    +        case 1:
    +          handler[i].call(this);
    +          break;
    +        case 2:
    +          handler[i].call(this, arguments[1]);
    +          break;
    +        case 3:
    +          handler[i].call(this, arguments[1], arguments[2]);
    +          break;
    +        default:
    +          handler[i].apply(this, args);
    +        }
    +      }
    +      return true;
    +    } else if (!this._all && type === 'error') {
    +      if (arguments[1] instanceof Error) {
    +        throw arguments[1]; // Unhandled 'error' event
    +      } else {
    +        throw new Error("Uncaught, unspecified 'error' event.");
    +      }
    +      return false;
    +    }
    +
    +    return !!this._all;
    +  };
    +
    +  EventEmitter.prototype.emitAsync = function() {
    +
    +    this._events || init.call(this);
    +
    +    var type = arguments[0];
    +
    +    if (type === 'newListener' && !this.newListener) {
    +        if (!this._events.newListener) { return Promise.resolve([false]); }
    +    }
    +
    +    var promises= [];
    +
    +    var al = arguments.length;
    +    var args,l,i,j;
    +    var handler;
    +
    +    if (this._all) {
    +      if (al > 3) {
    +        args = new Array(al);
    +        for (j = 1; j < al; j++) args[j] = arguments[j];
    +      }
    +      for (i = 0, l = this._all.length; i < l; i++) {
    +        this.event = type;
    +        switch (al) {
    +        case 1:
    +          promises.push(this._all[i].call(this, type));
    +          break;
    +        case 2:
    +          promises.push(this._all[i].call(this, type, arguments[1]));
    +          break;
    +        case 3:
    +          promises.push(this._all[i].call(this, type, arguments[1], arguments[2]));
    +          break;
    +        default:
    +          promises.push(this._all[i].apply(this, args));
    +        }
    +      }
    +    }
    +
    +    if (this.wildcard) {
    +      handler = [];
    +      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
    +      searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
    +    } else {
    +      handler = this._events[type];
    +    }
    +
    +    if (typeof handler === 'function') {
    +      this.event = type;
    +      switch (al) {
    +      case 1:
    +        promises.push(handler.call(this));
    +        break;
    +      case 2:
    +        promises.push(handler.call(this, arguments[1]));
    +        break;
    +      case 3:
    +        promises.push(handler.call(this, arguments[1], arguments[2]));
    +        break;
    +      default:
    +        args = new Array(al - 1);
    +        for (j = 1; j < al; j++) args[j - 1] = arguments[j];
    +        promises.push(handler.apply(this, args));
    +      }
    +    } else if (handler && handler.length) {
    +      handler = handler.slice();
    +      if (al > 3) {
    +        args = new Array(al - 1);
    +        for (j = 1; j < al; j++) args[j - 1] = arguments[j];
    +      }
    +      for (i = 0, l = handler.length; i < l; i++) {
    +        this.event = type;
    +        switch (al) {
    +        case 1:
    +          promises.push(handler[i].call(this));
    +          break;
    +        case 2:
    +          promises.push(handler[i].call(this, arguments[1]));
    +          break;
    +        case 3:
    +          promises.push(handler[i].call(this, arguments[1], arguments[2]));
    +          break;
    +        default:
    +          promises.push(handler[i].apply(this, args));
    +        }
    +      }
    +    } else if (!this._all && type === 'error') {
    +      if (arguments[1] instanceof Error) {
    +        return Promise.reject(arguments[1]); // Unhandled 'error' event
    +      } else {
    +        return Promise.reject("Uncaught, unspecified 'error' event.");
    +      }
    +    }
    +
    +    return Promise.all(promises);
    +  };
    +
    +  EventEmitter.prototype.on = function(type, listener) {
    +    return this._on(type, listener, false);
    +  };
    +
    +  EventEmitter.prototype.prependListener = function(type, listener) {
    +    return this._on(type, listener, true);
    +  };
    +
    +  EventEmitter.prototype.onAny = function(fn) {
    +    return this._onAny(fn, false);
    +  };
    +
    +  EventEmitter.prototype.prependAny = function(fn) {
    +    return this._onAny(fn, true);
    +  };
    +
    +  EventEmitter.prototype.addListener = EventEmitter.prototype.on;
    +
    +  EventEmitter.prototype._onAny = function(fn, prepend){
    +    if (typeof fn !== 'function') {
    +      throw new Error('onAny only accepts instances of Function');
    +    }
    +
    +    if (!this._all) {
    +      this._all = [];
    +    }
    +
    +    // Add the function to the event listener collection.
    +    if(prepend){
    +      this._all.unshift(fn);
    +    }else{
    +      this._all.push(fn);
    +    }
    +
    +    return this;
    +  }
    +
    +  EventEmitter.prototype._on = function(type, listener, prepend) {
    +    if (typeof type === 'function') {
    +      this._onAny(type, listener);
    +      return this;
    +    }
    +
    +    if (typeof listener !== 'function') {
    +      throw new Error('on only accepts instances of Function');
    +    }
    +    this._events || init.call(this);
    +
    +    // To avoid recursion in the case that type == "newListeners"! Before
    +    // adding it to the listeners, first emit "newListeners".
    +    this.emit('newListener', type, listener);
    +
    +    if (this.wildcard) {
    +      growListenerTree.call(this, type, listener);
    +      return this;
    +    }
    +
    +    if (!this._events[type]) {
    +      // Optimize the case of one listener. Don't need the extra array object.
    +      this._events[type] = listener;
    +    }
    +    else {
    +      if (typeof this._events[type] === 'function') {
    +        // Change to array.
    +        this._events[type] = [this._events[type]];
    +      }
    +
    +      // If we've already got an array, just add
    +      if(prepend){
    +        this._events[type].unshift(listener);
    +      }else{
    +        this._events[type].push(listener);
    +      }
    +
    +      // Check for listener leak
    +      if (
    +        !this._events[type].warned &&
    +        this._maxListeners > 0 &&
    +        this._events[type].length > this._maxListeners
    +      ) {
    +        this._events[type].warned = true;
    +        logPossibleMemoryLeak.call(this, this._events[type].length, type);
    +      }
    +    }
    +
    +    return this;
    +  }
    +
    +  EventEmitter.prototype.off = function(type, listener) {
    +    if (typeof listener !== 'function') {
    +      throw new Error('removeListener only takes instances of Function');
    +    }
    +
    +    var handlers,leafs=[];
    +
    +    if(this.wildcard) {
    +      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
    +      leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
    +    }
    +    else {
    +      // does not use listeners(), so no side effect of creating _events[type]
    +      if (!this._events[type]) return this;
    +      handlers = this._events[type];
    +      leafs.push({_listeners:handlers});
    +    }
    +
    +    for (var iLeaf=0; iLeaf 0) {
    +          recursivelyGarbageCollect(root[key]);
    +        }
    +        if (Object.keys(obj).length === 0) {
    +          delete root[key];
    +        }
    +      }
    +    }
    +    recursivelyGarbageCollect(this.listenerTree);
    +
    +    return this;
    +  };
    +
    +  EventEmitter.prototype.offAny = function(fn) {
    +    var i = 0, l = 0, fns;
    +    if (fn && this._all && this._all.length > 0) {
    +      fns = this._all;
    +      for(i = 0, l = fns.length; i < l; i++) {
    +        if(fn === fns[i]) {
    +          fns.splice(i, 1);
    +          this.emit("removeListenerAny", fn);
    +          return this;
    +        }
    +      }
    +    } else {
    +      fns = this._all;
    +      for(i = 0, l = fns.length; i < l; i++)
    +        this.emit("removeListenerAny", fns[i]);
    +      this._all = [];
    +    }
    +    return this;
    +  };
    +
    +  EventEmitter.prototype.removeListener = EventEmitter.prototype.off;
    +
    +  EventEmitter.prototype.removeAllListeners = function(type) {
    +    if (arguments.length === 0) {
    +      !this._events || init.call(this);
    +      return this;
    +    }
    +
    +    if (this.wildcard) {
    +      var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
    +      var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
    +
    +      for (var iLeaf=0; iLeaf 1) {
    +        for (var i = 1; i < arguments.length; i++) {
    +            args[i - 1] = arguments[i];
    +        }
    +    }
    +    queue.push(new Item(fun, args));
    +    if (queue.length === 1 && !draining) {
    +        runTimeout(drainQueue);
    +    }
    +};
    +
    +// v8 likes predictible objects
    +function Item(fun, array) {
    +    this.fun = fun;
    +    this.array = array;
    +}
    +Item.prototype.run = function () {
    +    this.fun.apply(null, this.array);
    +};
    +process.title = 'browser';
    +process.browser = true;
    +process.env = {};
    +process.argv = [];
    +process.version = ''; // empty string to avoid regexp issues
    +process.versions = {};
    +
    +function noop() {}
    +
    +process.on = noop;
    +process.addListener = noop;
    +process.once = noop;
    +process.off = noop;
    +process.removeListener = noop;
    +process.removeAllListeners = noop;
    +process.emit = noop;
    +process.prependListener = noop;
    +process.prependOnceListener = noop;
    +
    +process.listeners = function (name) { return [] }
    +
    +process.binding = function (name) {
    +    throw new Error('process.binding is not supported');
    +};
    +
    +process.cwd = function () { return '/' };
    +process.chdir = function (dir) {
    +    throw new Error('process.chdir is not supported');
    +};
    +process.umask = function() { return 0; };
    +
    +},{}],5:[function(require,module,exports){
    +/**
    + * @fileOverview
    + * @author Russell Toris - rctoris@wpi.edu
    + */
    +
    +/**
    + * If you use roslib in a browser, all the classes will be exported to a global variable called ROSLIB.
    + *
    + * If you use nodejs, this is the variable you get when you require('roslib')
    + */
    +var ROSLIB = this.ROSLIB || {
    +  REVISION : '1.0.1'
    +};
    +
    +var assign = require('object-assign');
    +
    +// Add core components
    +assign(ROSLIB, require('./core'));
    +
    +assign(ROSLIB, require('./actionlib'));
    +
    +assign(ROSLIB, require('./math'));
    +
    +assign(ROSLIB, require('./tf'));
    +
    +assign(ROSLIB, require('./urdf'));
    +
    +module.exports = ROSLIB;
    +
    +},{"./actionlib":11,"./core":20,"./math":25,"./tf":28,"./urdf":40,"object-assign":3}],6:[function(require,module,exports){
    +(function (global){
    +global.ROSLIB = require('./RosLib');
    +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
    +},{"./RosLib":5}],7:[function(require,module,exports){
    +/**
    + * @fileOverview
    + * @author Russell Toris - rctoris@wpi.edu
    + */
    +
    +var Topic = require('../core/Topic');
    +var Message = require('../core/Message');
    +var EventEmitter2 = require('eventemitter2').EventEmitter2;
    +
    +/**
    + * An actionlib action client.
    + *
    + * Emits the following events:
    + *  * 'timeout' - if a timeout occurred while sending a goal
    + *  * 'status' - the status messages received from the action server
    + *  * 'feedback' -  the feedback messages received from the action server
    + *  * 'result' - the result returned from the action server
    + *
    + *  @constructor
    + *  @param options - object with following keys:
    + *   * ros - the ROSLIB.Ros connection handle
    + *   * serverName - the action server name, like /fibonacci
    + *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'
    + *   * timeout - the timeout length when connecting to the action server
    + */
    +function ActionClient(options) {
    +  var that = this;
    +  options = options || {};
    +  this.ros = options.ros;
    +  this.serverName = options.serverName;
    +  this.actionName = options.actionName;
    +  this.timeout = options.timeout;
    +  this.omitFeedback = options.omitFeedback;
    +  this.omitStatus = options.omitStatus;
    +  this.omitResult = options.omitResult;
    +  this.goals = {};
    +
    +  // flag to check if a status has been received
    +  var receivedStatus = false;
    +
    +  // create the topics associated with actionlib
    +  this.feedbackListener = new Topic({
    +    ros : this.ros,
    +    name : this.serverName + '/feedback',
    +    messageType : this.actionName + 'Feedback'
    +  });
    +
    +  this.statusListener = new Topic({
    +    ros : this.ros,
    +    name : this.serverName + '/status',
    +    messageType : 'actionlib_msgs/GoalStatusArray'
    +  });
    +
    +  this.resultListener = new Topic({
    +    ros : this.ros,
    +    name : this.serverName + '/result',
    +    messageType : this.actionName + 'Result'
    +  });
    +
    +  this.goalTopic = new Topic({
    +    ros : this.ros,
    +    name : this.serverName + '/goal',
    +    messageType : this.actionName + 'Goal'
    +  });
    +
    +  this.cancelTopic = new Topic({
    +    ros : this.ros,
    +    name : this.serverName + '/cancel',
    +    messageType : 'actionlib_msgs/GoalID'
    +  });
    +
    +  // advertise the goal and cancel topics
    +  this.goalTopic.advertise();
    +  this.cancelTopic.advertise();
    +
    +  // subscribe to the status topic
    +  if (!this.omitStatus) {
    +    this.statusListener.subscribe(function(statusMessage) {
    +      receivedStatus = true;
    +      statusMessage.status_list.forEach(function(status) {
    +        var goal = that.goals[status.goal_id.id];
    +        if (goal) {
    +          goal.emit('status', status);
    +        }
    +      });
    +    });
    +  }
    +
    +  // subscribe the the feedback topic
    +  if (!this.omitFeedback) {
    +    this.feedbackListener.subscribe(function(feedbackMessage) {
    +      var goal = that.goals[feedbackMessage.status.goal_id.id];
    +      if (goal) {
    +        goal.emit('status', feedbackMessage.status);
    +        goal.emit('feedback', feedbackMessage.feedback);
    +      }
    +    });
    +  }
    +
    +  // subscribe to the result topic
    +  if (!this.omitResult) {
    +    this.resultListener.subscribe(function(resultMessage) {
    +      var goal = that.goals[resultMessage.status.goal_id.id];
    +
    +      if (goal) {
    +        goal.emit('status', resultMessage.status);
    +        goal.emit('result', resultMessage.result);
    +      }
    +    });
    +  }
    +
    +  // If timeout specified, emit a 'timeout' event if the action server does not respond
    +  if (this.timeout) {
    +    setTimeout(function() {
    +      if (!receivedStatus) {
    +        that.emit('timeout');
    +      }
    +    }, this.timeout);
    +  }
    +}
    +
    +ActionClient.prototype.__proto__ = EventEmitter2.prototype;
    +
    +/**
    + * Cancel all goals associated with this ActionClient.
    + */
    +ActionClient.prototype.cancel = function() {
    +  var cancelMessage = new Message();
    +  this.cancelTopic.publish(cancelMessage);
    +};
    +
    +/**
    + * Unsubscribe and unadvertise all topics associated with this ActionClient.
    + */
    +ActionClient.prototype.dispose = function() {
    +  this.goalTopic.unadvertise();
    +  this.cancelTopic.unadvertise();
    +  if (!this.omitStatus) {this.statusListener.unsubscribe();}
    +  if (!this.omitFeedback) {this.feedbackListener.unsubscribe();}
    +  if (!this.omitResult) {this.resultListener.unsubscribe();}
    +};
    +
    +module.exports = ActionClient;
    +
    +},{"../core/Message":12,"../core/Topic":19,"eventemitter2":2}],8:[function(require,module,exports){
    +/**
    + * @fileOverview
    + * @author Justin Young - justin@oodar.com.au
    + * @author Russell Toris - rctoris@wpi.edu
    + */
    +
    +var Topic = require('../core/Topic');
    +var Message = require('../core/Message');
    +var EventEmitter2 = require('eventemitter2').EventEmitter2;
    +
    +/**
    + * An actionlib action listener
    + *
    + * Emits the following events:
    + *  * 'status' - the status messages received from the action server
    + *  * 'feedback' -  the feedback messages received from the action server
    + *  * 'result' - the result returned from the action server
    + *
    + *  @constructor
    + *  @param options - object with following keys:
    + *   * ros - the ROSLIB.Ros connection handle
    + *   * serverName - the action server name, like /fibonacci
    + *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'
    + */
    +function ActionListener(options) {
    +  var that = this;
    +  options = options || {};
    +  this.ros = options.ros;
    +  this.serverName = options.serverName;
    +  this.actionName = options.actionName;
    +  this.timeout = options.timeout;
    +  this.omitFeedback = options.omitFeedback;
    +  this.omitStatus = options.omitStatus;
    +  this.omitResult = options.omitResult;
    +
    +
    +  // create the topics associated with actionlib
    +  var goalListener = new Topic({
    +    ros : this.ros,
    +    name : this.serverName + '/goal',
    +    messageType : this.actionName + 'Goal'
    +  });
    +
    +  var feedbackListener = new Topic({
    +    ros : this.ros,
    +    name : this.serverName + '/feedback',
    +    messageType : this.actionName + 'Feedback'
    +  });
    +
    +  var statusListener = new Topic({
    +    ros : this.ros,
    +    name : this.serverName + '/status',
    +    messageType : 'actionlib_msgs/GoalStatusArray'
    +  });
    +
    +  var resultListener = new Topic({
    +    ros : this.ros,
    +    name : this.serverName + '/result',
    +    messageType : this.actionName + 'Result'
    +  });
    +
    +  goalListener.subscribe(function(goalMessage) {
    +      that.emit('goal', goalMessage);
    +  });
    +
    +  statusListener.subscribe(function(statusMessage) {
    +      statusMessage.status_list.forEach(function(status) {
    +          that.emit('status', status);
    +      });
    +  });
    +
    +  feedbackListener.subscribe(function(feedbackMessage) {
    +      that.emit('status', feedbackMessage.status);
    +      that.emit('feedback', feedbackMessage.feedback);
    +  });
    +
    +  // subscribe to the result topic
    +  resultListener.subscribe(function(resultMessage) {
    +      that.emit('status', resultMessage.status);
    +      that.emit('result', resultMessage.result);
    +  });
    +
    +}
    +
    +ActionListener.prototype.__proto__ = EventEmitter2.prototype;
    +
    +module.exports = ActionListener;
    +
    +},{"../core/Message":12,"../core/Topic":19,"eventemitter2":2}],9:[function(require,module,exports){
    +/**
    + * @fileOverview
    + * @author Russell Toris - rctoris@wpi.edu
    + */
    +
    +var Message = require('../core/Message');
    +var EventEmitter2 = require('eventemitter2').EventEmitter2;
    +
    +/**
    + * An actionlib goal goal is associated with an action server.
    + *
    + * Emits the following events:
    + *  * 'timeout' - if a timeout occurred while sending a goal
    + *
    + *  @constructor
    + *  @param object with following keys:
    + *   * actionClient - the ROSLIB.ActionClient to use with this goal
    + *   * goalMessage - The JSON object containing the goal for the action server
    + */
    +function Goal(options) {
    +  var that = this;
    +  this.actionClient = options.actionClient;
    +  this.goalMessage = options.goalMessage;
    +  this.isFinished = false;
    +
    +  // Used to create random IDs
    +  var date = new Date();
    +
    +  // Create a random ID
    +  this.goalID = 'goal_' + Math.random() + '_' + date.getTime();
    +  // Fill in the goal message
    +  this.goalMessage = new Message({
    +    goal_id : {
    +      stamp : {
    +        secs : 0,
    +        nsecs : 0
    +      },
    +      id : this.goalID
    +    },
    +    goal : this.goalMessage
    +  });
    +
    +  this.on('status', function(status) {
    +    that.status = status;
    +  });
    +
    +  this.on('result', function(result) {
    +    that.isFinished = true;
    +    that.result = result;
    +  });
    +
    +  this.on('feedback', function(feedback) {
    +    that.feedback = feedback;
    +  });
    +
    +  // Add the goal
    +  this.actionClient.goals[this.goalID] = this;
    +}
    +
    +Goal.prototype.__proto__ = EventEmitter2.prototype;
    +
    +/**
    + * Send the goal to the action server.
    + *
    + * @param timeout (optional) - a timeout length for the goal's result
    + */
    +Goal.prototype.send = function(timeout) {
    +  var that = this;
    +  that.actionClient.goalTopic.publish(that.goalMessage);
    +  if (timeout) {
    +    setTimeout(function() {
    +      if (!that.isFinished) {
    +        that.emit('timeout');
    +      }
    +    }, timeout);
    +  }
    +};
    +
    +/**
    + * Cancel the current goal.
    + */
    +Goal.prototype.cancel = function() {
    +  var cancelMessage = new Message({
    +    id : this.goalID
    +  });
    +  this.actionClient.cancelTopic.publish(cancelMessage);
    +};
    +
    +module.exports = Goal;
    +},{"../core/Message":12,"eventemitter2":2}],10:[function(require,module,exports){
    +/**
    + * @fileOverview
    + * @author Laura Lindzey - lindzey@gmail.com
    + */
    +
    +var Topic = require('../core/Topic');
    +var Message = require('../core/Message');
    +var EventEmitter2 = require('eventemitter2').EventEmitter2;
    +
    +/**
    + * An actionlib action server client.
    + *
    + * Emits the following events:
    + *  * 'goal' - goal sent by action client
    + *  * 'cancel' - action client has canceled the request
    + *
    + *  @constructor
    + *  @param options - object with following keys:
    + *   * ros - the ROSLIB.Ros connection handle
    + *   * serverName - the action server name, like /fibonacci
    + *   * actionName - the action message name, like 'actionlib_tutorials/FibonacciAction'
    + */
    +
    +function SimpleActionServer(options) {
    +    var that = this;
    +    options = options || {};
    +    this.ros = options.ros;
    +    this.serverName = options.serverName;
    +    this.actionName = options.actionName;
    +
    +    // create and advertise publishers
    +    this.feedbackPublisher = new Topic({
    +        ros : this.ros,
    +        name : this.serverName + '/feedback',
    +        messageType : this.actionName + 'Feedback'
    +    });
    +    this.feedbackPublisher.advertise();
    +
    +    var statusPublisher = new Topic({
    +        ros : this.ros,
    +        name : this.serverName + '/status',
    +        messageType : 'actionlib_msgs/GoalStatusArray'
    +    });
    +    statusPublisher.advertise();
    +
    +    this.resultPublisher = new Topic({
    +        ros : this.ros,
    +        name : this.serverName + '/result',
    +        messageType : this.actionName + 'Result'
    +    });
    +    this.resultPublisher.advertise();
    +
    +    // create and subscribe to listeners
    +    var goalListener = new Topic({
    +        ros : this.ros,
    +        name : this.serverName + '/goal',
    +        messageType : this.actionName + 'Goal'
    +    });
    +
    +    var cancelListener = new Topic({
    +        ros : this.ros,
    +        name : this.serverName + '/cancel',
    +        messageType : 'actionlib_msgs/GoalID'
    +    });
    +
    +    // Track the goals and their status in order to publish status...
    +    this.statusMessage = new Message({
    +        header : {
    +            stamp : {secs : 0, nsecs : 100},
    +            frame_id : ''
    +        },
    +        status_list : []
    +    });
    +
    +    // needed for handling preemption prompted by a new goal being received
    +    this.currentGoal = null; // currently tracked goal
    +    this.nextGoal = null; // the one that'll be preempting
    +
    +    goalListener.subscribe(function(goalMessage) {
    +
    +    if(that.currentGoal) {
    +            that.nextGoal = goalMessage;
    +            // needs to happen AFTER rest is set up
    +            that.emit('cancel');
    +    } else {
    +            that.statusMessage.status_list = [{goal_id : goalMessage.goal_id, status : 1}];
    +            that.currentGoal = goalMessage;
    +            that.emit('goal', goalMessage.goal);
    +    }
    +    });
    +
    +    // helper function for determing ordering of timestamps
    +    // returns t1 < t2
    +    var isEarlier = function(t1, t2) {
    +        if(t1.secs > t2.secs) {
    +            return false;
    +        } else if(t1.secs < t2.secs) {
    +            return true;
    +        } else if(t1.nsecs < t2.nsecs) {
    +            return true;
    +        } else {
    +            return false;
    +        }
    +    };
    +
    +    // TODO: this may be more complicated than necessary, since I'm
    +    // not sure if the callbacks can ever wind up with a scenario
    +    // where we've been preempted by a next goal, it hasn't finished
    +    // processing, and then we get a cancel message
    +    cancelListener.subscribe(function(cancelMessage) {
    +
    +        // cancel ALL goals if both empty
    +        if(cancelMessage.stamp.secs === 0 && cancelMessage.stamp.secs === 0 && cancelMessage.id === '') {
    +            that.nextGoal = null;
    +            if(that.currentGoal) {
    +                that.emit('cancel');
    +            }
    +        } else { // treat id and stamp independently
    +            if(that.currentGoal && cancelMessage.id === that.currentGoal.goal_id.id) {
    +                that.emit('cancel');
    +            } else if(that.nextGoal && cancelMessage.id === that.nextGoal.goal_id.id) {
    +                that.nextGoal = null;
    +            }
    +
    +            if(that.nextGoal && isEarlier(that.nextGoal.goal_id.stamp,
    +                                          cancelMessage.stamp)) {
    +                that.nextGoal = null;
    +            }
    +            if(that.currentGoal && isEarlier(that.currentGoal.goal_id.stamp,
    +                                             cancelMessage.stamp)) {
    +
    +                that.emit('cancel');
    +            }
    +        }
    +    });
    +
    +    // publish status at pseudo-fixed rate; required for clients to know they've connected
    +    var statusInterval = setInterval( function() {
    +        var currentTime = new Date();
    +        var secs = Math.floor(currentTime.getTime()/1000);
    +        var nsecs = Math.round(1000000000*(currentTime.getTime()/1000-secs));
    +        that.statusMessage.header.stamp.secs = secs;
    +        that.statusMessage.header.stamp.nsecs = nsecs;
    +        statusPublisher.publish(that.statusMessage);
    +    }, 500); // publish every 500ms
    +
    +}
    +
    +SimpleActionServer.prototype.__proto__ = EventEmitter2.prototype;
    +
    +/**
    +*  Set action state to succeeded and return to client
    +*/
    +
    +SimpleActionServer.prototype.setSucceeded = function(result2) {
    +
    +
    +    var resultMessage = new Message({
    +        status : {goal_id : this.currentGoal.goal_id, status : 3},
    +        result : result2
    +    });
    +    this.resultPublisher.publish(resultMessage);
    +
    +    this.statusMessage.status_list = [];
    +    if(this.nextGoal) {
    +        this.currentGoal = this.nextGoal;
    +        this.nextGoal = null;
    +        this.emit('goal', this.currentGoal.goal);
    +    } else {
    +        this.currentGoal = null;
    +    }
    +};
    +
    +/**
    +*  Function to send feedback
    +*/
    +
    +SimpleActionServer.prototype.sendFeedback = function(feedback2) {
    +
    +    var feedbackMessage = new Message({
    +        status : {goal_id : this.currentGoal.goal_id, status : 1},
    +        feedback : feedback2
    +    });
    +    this.feedbackPublisher.publish(feedbackMessage);
    +};
    +
    +/**
    +*  Handle case where client requests preemption
    +*/
    +
    +SimpleActionServer.prototype.setPreempted = function() {
    +
    +    this.statusMessage.status_list = [];
    +    var resultMessage = new Message({
    +        status : {goal_id : this.currentGoal.goal_id, status : 2},
    +    });
    +    this.resultPublisher.publish(resultMessage);
    +
    +    if(this.nextGoal) {
    +        this.currentGoal = this.nextGoal;
    +        this.nextGoal = null;
    +        this.emit('goal', this.currentGoal.goal);
    +    } else {
    +        this.currentGoal = null;
    +    }
    +};
    +
    +module.exports = SimpleActionServer;
    +},{"../core/Message":12,"../core/Topic":19,"eventemitter2":2}],11:[function(require,module,exports){
    +var Ros = require('../core/Ros');
    +var mixin = require('../mixin');
    +
    +var action = module.exports = {
    +    ActionClient: require('./ActionClient'),
    +    ActionListener: require('./ActionListener'),
    +    Goal: require('./Goal'),
    +    SimpleActionServer: require('./SimpleActionServer')
    +};
    +
    +mixin(Ros, ['ActionClient', 'SimpleActionServer'], action);
    +
    +},{"../core/Ros":14,"../mixin":26,"./ActionClient":7,"./ActionListener":8,"./Goal":9,"./SimpleActionServer":10}],12:[function(require,module,exports){
    +/**
    + * @fileoverview
    + * @author Brandon Alexander - baalexander@gmail.com
    + */
    +
    +var assign = require('object-assign');
    +
    +/**
    + * Message objects are used for publishing and subscribing to and from topics.
    + *
    + * @constructor
    + * @param values - object matching the fields defined in the .msg definition file
    + */
    +function Message(values) {
    +  assign(this, values);
    +}
    +
    +module.exports = Message;
    +},{"object-assign":3}],13:[function(require,module,exports){
    +/**
    + * @fileoverview
    + * @author Brandon Alexander - baalexander@gmail.com
    + */
    +
    +var Service = require('./Service');
    +var ServiceRequest = require('./ServiceRequest');
    +
    +/**
    + * A ROS parameter.
    + *
    + * @constructor
    + * @param options - possible keys include:
    + *   * ros - the ROSLIB.Ros connection handle
    + *   * name - the param name, like max_vel_x
    + */
    +function Param(options) {
    +  options = options || {};
    +  this.ros = options.ros;
    +  this.name = options.name;
    +}
    +
    +/**
    + * Fetches the value of the param.
    + *
    + * @param callback - function with the following params:
    + *  * value - the value of the param from ROS.
    + */
    +Param.prototype.get = function(callback) {
    +  var paramClient = new Service({
    +    ros : this.ros,
    +    name : '/rosapi/get_param',
    +    serviceType : 'rosapi/GetParam'
    +  });
    +
    +  var request = new ServiceRequest({
    +    name : this.name
    +  });
    +
    +  paramClient.callService(request, function(result) {
    +    var value = JSON.parse(result.value);
    +    callback(value);
    +  });
    +};
    +
    +/**
    + * Sets the value of the param in ROS.
    + *
    + * @param value - value to set param to.
    + */
    +Param.prototype.set = function(value, callback) {
    +  var paramClient = new Service({
    +    ros : this.ros,
    +    name : '/rosapi/set_param',
    +    serviceType : 'rosapi/SetParam'
    +  });
    +
    +  var request = new ServiceRequest({
    +    name : this.name,
    +    value : JSON.stringify(value)
    +  });
    +
    +  paramClient.callService(request, callback);
    +};
    +
    +/**
    + * Delete this parameter on the ROS server.
    + */
    +Param.prototype.delete = function(callback) {
    +  var paramClient = new Service({
    +    ros : this.ros,
    +    name : '/rosapi/delete_param',
    +    serviceType : 'rosapi/DeleteParam'
    +  });
    +
    +  var request = new ServiceRequest({
    +    name : this.name
    +  });
    +
    +  paramClient.callService(request, callback);
    +};
    +
    +module.exports = Param;
    +},{"./Service":15,"./ServiceRequest":16}],14:[function(require,module,exports){
    +/**
    + * @fileoverview
    + * @author Brandon Alexander - baalexander@gmail.com
    + */
    +
    +var WebSocket = require('ws');
    +var socketAdapter = require('./SocketAdapter.js');
    +
    +var Service = require('./Service');
    +var ServiceRequest = require('./ServiceRequest');
    +
    +var assign = require('object-assign');
    +var EventEmitter2 = require('eventemitter2').EventEmitter2;
    +
    +/**
    + * Manages connection to the server and all interactions with ROS.
    + *
    + * Emits the following events:
    + *  * 'error' - there was an error with ROS
    + *  * 'connection' - connected to the WebSocket server
    + *  * 'close' - disconnected to the WebSocket server
    + *  *  - a message came from rosbridge with the given topic name
    + *  *  - a service response came from rosbridge with the given ID
    + *
    + * @constructor
    + * @param options - possible keys include: 
    + * * url (optional) - (can be specified later with `connect`) the WebSocket URL for rosbridge or the node server url to connect using socket.io (if socket.io exists in the page)
    + * * groovyCompatibility - don't use interfaces that changed after the last groovy release or rosbridge_suite and related tools (defaults to true) + * * transportLibrary (optional) - one of 'websocket' (default), 'socket.io' or RTCPeerConnection instance controlling how the connection is created in `connect`. + * * transportOptions (optional) - the options to use use when creating a connection. Currently only used if `transportLibrary` is RTCPeerConnection. + */ +function Ros(options) { + options = options || {}; + this.socket = null; + this.idCounter = 0; + this.isConnected = false; + this.transportLibrary = options.transportLibrary || 'websocket'; + this.transportOptions = options.transportOptions || {}; + + if (typeof options.groovyCompatibility === 'undefined') { + this.groovyCompatibility = true; + } + else { + this.groovyCompatibility = options.groovyCompatibility; + } + + // Sets unlimited event listeners. + this.setMaxListeners(0); + + // begin by checking if a URL was given + if (options.url) { + this.connect(options.url); + } +} + +Ros.prototype.__proto__ = EventEmitter2.prototype; + +/** + * Connect to the specified WebSocket. + * + * @param url - WebSocket URL or RTCDataChannel label for Rosbridge + */ +Ros.prototype.connect = function(url) { + if (this.transportLibrary === 'socket.io') { + this.socket = assign(io(url, {'force new connection': true}), socketAdapter(this)); + this.socket.on('connect', this.socket.onopen); + this.socket.on('data', this.socket.onmessage); + this.socket.on('close', this.socket.onclose); + this.socket.on('error', this.socket.onerror); + } else if (this.transportLibrary.constructor.name === 'RTCPeerConnection') { + this.socket = assign(this.transportLibrary.createDataChannel(url, this.transportOptions), socketAdapter(this)); + } else { + if (!this.socket || this.socket.readyState === WebSocket.CLOSED) { + var sock = new WebSocket(url); + sock.binaryType = 'arraybuffer'; + this.socket = assign(sock, socketAdapter(this)); + } + } + +}; + +/** + * Disconnect from the WebSocket server. + */ +Ros.prototype.close = function() { + if (this.socket) { + this.socket.close(); + } +}; + +/** + * Sends an authorization request to the server. + * + * @param mac - MAC (hash) string given by the trusted source. + * @param client - IP of the client. + * @param dest - IP of the destination. + * @param rand - Random string given by the trusted source. + * @param t - Time of the authorization request. + * @param level - User level as a string given by the client. + * @param end - End time of the client's session. + */ +Ros.prototype.authenticate = function(mac, client, dest, rand, t, level, end) { + // create the request + var auth = { + op : 'auth', + mac : mac, + client : client, + dest : dest, + rand : rand, + t : t, + level : level, + end : end + }; + // send the request + this.callOnConnection(auth); +}; + +/** + * Sends the message over the WebSocket, but queues the message up if not yet + * connected. + */ +Ros.prototype.callOnConnection = function(message) { + var that = this; + var messageJson = JSON.stringify(message); + var emitter = null; + if (this.transportLibrary === 'socket.io') { + emitter = function(msg){that.socket.emit('operation', msg);}; + } else { + emitter = function(msg){that.socket.send(msg);}; + } + + if (!this.isConnected) { + that.once('connection', function() { + emitter(messageJson); + }); + } else { + emitter(messageJson); + } +}; + +/** + * Sends a set_level request to the server + * + * @param level - Status level (none, error, warning, info) + * @param id - Optional: Operation ID to change status level on + */ +Ros.prototype.setStatusLevel = function(level, id){ + var levelMsg = { + op: 'set_level', + level: level, + id: id + }; + + this.callOnConnection(levelMsg); +}; + +/** + * Retrieves Action Servers in ROS as an array of string + * + * * actionservers - Array of action server names + */ +Ros.prototype.getActionServers = function(callback, failedCallback) { + var getActionServers = new Service({ + ros : this, + name : '/rosapi/action_servers', + serviceType : 'rosapi/GetActionServers' + }); + + var request = new ServiceRequest({}); + if (typeof failedCallback === 'function'){ + getActionServers.callService(request, + function(result) { + callback(result.action_servers); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + getActionServers.callService(request, function(result) { + callback(result.action_servers); + }); + } +}; + +/** + * Retrieves list of topics in ROS as an array. + * + * @param callback function with params: + * * topics - Array of topic names + */ +Ros.prototype.getTopics = function(callback, failedCallback) { + var topicsClient = new Service({ + ros : this, + name : '/rosapi/topics', + serviceType : 'rosapi/Topics' + }); + + var request = new ServiceRequest(); + if (typeof failedCallback === 'function'){ + topicsClient.callService(request, + function(result) { + callback(result); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + topicsClient.callService(request, function(result) { + callback(result); + }); + } +}; + +/** + * Retrieves Topics in ROS as an array as specific type + * + * @param topicType topic type to find: + * @param callback function with params: + * * topics - Array of topic names + */ +Ros.prototype.getTopicsForType = function(topicType, callback, failedCallback) { + var topicsForTypeClient = new Service({ + ros : this, + name : '/rosapi/topics_for_type', + serviceType : 'rosapi/TopicsForType' + }); + + var request = new ServiceRequest({ + type: topicType + }); + if (typeof failedCallback === 'function'){ + topicsForTypeClient.callService(request, + function(result) { + callback(result.topics); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + topicsForTypeClient.callService(request, function(result) { + callback(result.topics); + }); + } +}; + +/** + * Retrieves list of active service names in ROS. + * + * @param callback - function with the following params: + * * services - array of service names + */ +Ros.prototype.getServices = function(callback, failedCallback) { + var servicesClient = new Service({ + ros : this, + name : '/rosapi/services', + serviceType : 'rosapi/Services' + }); + + var request = new ServiceRequest(); + if (typeof failedCallback === 'function'){ + servicesClient.callService(request, + function(result) { + callback(result.services); + }, + function(message) { + failedCallback(message); + } + ); + }else{ + servicesClient.callService(request, function(result) { + callback(result.services); + }); + } +}; + +/** + * Retrieves list of services in ROS as an array as specific type + * + * @param serviceType service type to find: + * @param callback function with params: + * * topics - Array of service names + */ +Ros.prototype.getServicesForType = function(serviceType, callback, failedCallback) { + var servicesForTypeClient = new Service({ + ros : this, + name : '/rosapi/services_for_type', + serviceType : 'rosapi/ServicesForType' + }); + + var request = new ServiceRequest({ + type: serviceType + }); + if (typeof failedCallback === 'function'){ + servicesForTypeClient.callService(request, + function(result) { + callback(result.services); + }, + function(message) { + failedCallback(message); + } + ); + }else{ + servicesForTypeClient.callService(request, function(result) { + callback(result.services); + }); + } +}; + +/** + * Retrieves a detail of ROS service request. + * + * @param service name of service: + * @param callback - function with params: + * * type - String of the service type + */ +Ros.prototype.getServiceRequestDetails = function(type, callback, failedCallback) { + var serviceTypeClient = new Service({ + ros : this, + name : '/rosapi/service_request_details', + serviceType : 'rosapi/ServiceRequestDetails' + }); + var request = new ServiceRequest({ + type: type + }); + + if (typeof failedCallback === 'function'){ + serviceTypeClient.callService(request, + function(result) { + callback(result); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + serviceTypeClient.callService(request, function(result) { + callback(result); + }); + } +}; + +/** + * Retrieves a detail of ROS service request. + * + * @param service name of service: + * @param callback - function with params: + * * type - String of the service type + */ +Ros.prototype.getServiceResponseDetails = function(type, callback, failedCallback) { + var serviceTypeClient = new Service({ + ros : this, + name : '/rosapi/service_response_details', + serviceType : 'rosapi/ServiceResponseDetails' + }); + var request = new ServiceRequest({ + type: type + }); + + if (typeof failedCallback === 'function'){ + serviceTypeClient.callService(request, + function(result) { + callback(result); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + serviceTypeClient.callService(request, function(result) { + callback(result); + }); + } +}; + +/** + * Retrieves list of active node names in ROS. + * + * @param callback - function with the following params: + * * nodes - array of node names + */ +Ros.prototype.getNodes = function(callback, failedCallback) { + var nodesClient = new Service({ + ros : this, + name : '/rosapi/nodes', + serviceType : 'rosapi/Nodes' + }); + + var request = new ServiceRequest(); + if (typeof failedCallback === 'function'){ + nodesClient.callService(request, + function(result) { + callback(result.nodes); + }, + function(message) { + failedCallback(message); + } + ); + }else{ + nodesClient.callService(request, function(result) { + callback(result.nodes); + }); + } +}; + +/** + * Retrieves list subscribed topics, publishing topics and services of a specific node + * + * @param node name of the node: + * @param callback - function with params: + * * publications - array of published topic names + * * subscriptions - array of subscribed topic names + * * services - array of service names hosted + */ +Ros.prototype.getNodeDetails = function(node, callback, failedCallback) { + var nodesClient = new Service({ + ros : this, + name : '/rosapi/node_details', + serviceType : 'rosapi/NodeDetails' + }); + + var request = new ServiceRequest({ + node: node + }); + if (typeof failedCallback === 'function'){ + nodesClient.callService(request, + function(result) { + callback(result.subscribing, result.publishing, result.services); + }, + function(message) { + failedCallback(message); + } + ); + } else { + nodesClient.callService(request, function(result) { + callback(result); + }); + } +}; + +/** + * Retrieves list of param names from the ROS Parameter Server. + * + * @param callback function with params: + * * params - array of param names. + */ +Ros.prototype.getParams = function(callback, failedCallback) { + var paramsClient = new Service({ + ros : this, + name : '/rosapi/get_param_names', + serviceType : 'rosapi/GetParamNames' + }); + var request = new ServiceRequest(); + if (typeof failedCallback === 'function'){ + paramsClient.callService(request, + function(result) { + callback(result.names); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + paramsClient.callService(request, function(result) { + callback(result.names); + }); + } +}; + +/** + * Retrieves a type of ROS topic. + * + * @param topic name of the topic: + * @param callback - function with params: + * * type - String of the topic type + */ +Ros.prototype.getTopicType = function(topic, callback, failedCallback) { + var topicTypeClient = new Service({ + ros : this, + name : '/rosapi/topic_type', + serviceType : 'rosapi/TopicType' + }); + var request = new ServiceRequest({ + topic: topic + }); + + if (typeof failedCallback === 'function'){ + topicTypeClient.callService(request, + function(result) { + callback(result.type); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + topicTypeClient.callService(request, function(result) { + callback(result.type); + }); + } +}; + +/** + * Retrieves a type of ROS service. + * + * @param service name of service: + * @param callback - function with params: + * * type - String of the service type + */ +Ros.prototype.getServiceType = function(service, callback, failedCallback) { + var serviceTypeClient = new Service({ + ros : this, + name : '/rosapi/service_type', + serviceType : 'rosapi/ServiceType' + }); + var request = new ServiceRequest({ + service: service + }); + + if (typeof failedCallback === 'function'){ + serviceTypeClient.callService(request, + function(result) { + callback(result.type); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + serviceTypeClient.callService(request, function(result) { + callback(result.type); + }); + } +}; + +/** + * Retrieves a detail of ROS message. + * + * @param callback - function with params: + * * details - Array of the message detail + * @param message - String of a topic type + */ +Ros.prototype.getMessageDetails = function(message, callback, failedCallback) { + var messageDetailClient = new Service({ + ros : this, + name : '/rosapi/message_details', + serviceType : 'rosapi/MessageDetails' + }); + var request = new ServiceRequest({ + type: message + }); + + if (typeof failedCallback === 'function'){ + messageDetailClient.callService(request, + function(result) { + callback(result.typedefs); + }, + function(message){ + failedCallback(message); + } + ); + }else{ + messageDetailClient.callService(request, function(result) { + callback(result.typedefs); + }); + } +}; + +/** + * Decode a typedefs into a dictionary like `rosmsg show foo/bar` + * + * @param defs - array of type_def dictionary + */ +Ros.prototype.decodeTypeDefs = function(defs) { + var that = this; + + // calls itself recursively to resolve type definition using hints. + var decodeTypeDefsRec = function(theType, hints) { + var typeDefDict = {}; + for (var i = 0; i < theType.fieldnames.length; i++) { + var arrayLen = theType.fieldarraylen[i]; + var fieldName = theType.fieldnames[i]; + var fieldType = theType.fieldtypes[i]; + if (fieldType.indexOf('/') === -1) { // check the fieldType includes '/' or not + if (arrayLen === -1) { + typeDefDict[fieldName] = fieldType; + } + else { + typeDefDict[fieldName] = [fieldType]; + } + } + else { + // lookup the name + var sub = false; + for (var j = 0; j < hints.length; j++) { + if (hints[j].type.toString() === fieldType.toString()) { + sub = hints[j]; + break; + } + } + if (sub) { + var subResult = decodeTypeDefsRec(sub, hints); + if (arrayLen === -1) { + } + else { + typeDefDict[fieldName] = [subResult]; + } + } + else { + that.emit('error', 'Cannot find ' + fieldType + ' in decodeTypeDefs'); + } + } + } + return typeDefDict; + }; + + return decodeTypeDefsRec(defs[0], defs); +}; + + +module.exports = Ros; + +},{"./Service":15,"./ServiceRequest":16,"./SocketAdapter.js":18,"eventemitter2":2,"object-assign":3,"ws":42}],15:[function(require,module,exports){ +/** + * @fileoverview + * @author Brandon Alexander - baalexander@gmail.com + */ + +var ServiceResponse = require('./ServiceResponse'); +var ServiceRequest = require('./ServiceRequest'); +var EventEmitter2 = require('eventemitter2').EventEmitter2; + +/** + * A ROS service client. + * + * @constructor + * @params options - possible keys include: + * * ros - the ROSLIB.Ros connection handle + * * name - the service name, like /add_two_ints + * * serviceType - the service type, like 'rospy_tutorials/AddTwoInts' + */ +function Service(options) { + options = options || {}; + this.ros = options.ros; + this.name = options.name; + this.serviceType = options.serviceType; + this.isAdvertised = false; + + this._serviceCallback = null; +} +Service.prototype.__proto__ = EventEmitter2.prototype; +/** + * Calls the service. Returns the service response in the + * callback. Does nothing if this service is currently advertised. + * + * @param request - the ROSLIB.ServiceRequest to send + * @param callback - function with params: + * * response - the response from the service request + * @param failedCallback - the callback function when the service call failed (optional). Params: + * * error - the error message reported by ROS + */ +Service.prototype.callService = function(request, callback, failedCallback) { + if (this.isAdvertised) { + return; + } + + var serviceCallId = 'call_service:' + this.name + ':' + (++this.ros.idCounter); + + if (callback || failedCallback) { + this.ros.once(serviceCallId, function(message) { + if (message.result !== undefined && message.result === false) { + if (typeof failedCallback === 'function') { + failedCallback(message.values); + } + } else if (typeof callback === 'function') { + callback(new ServiceResponse(message.values)); + } + }); + } + + var call = { + op : 'call_service', + id : serviceCallId, + service : this.name, + type: this.serviceType, + args : request + }; + this.ros.callOnConnection(call); +}; + +/** + * Advertise the service. This turns the Service object from a client + * into a server. The callback will be called with every request + * that's made on this service. + * + * @param callback - This works similarly to the callback for a C++ service and should take the following params: + * * request - the service request + * * response - an empty dictionary. Take care not to overwrite this. Instead, only modify the values within. + * It should return true if the service has finished successfully, + * i.e. without any fatal errors. + */ +Service.prototype.advertise = function(callback) { + if (this.isAdvertised || typeof callback !== 'function') { + return; + } + + this._serviceCallback = callback; + this.ros.on(this.name, this._serviceResponse.bind(this)); + this.ros.callOnConnection({ + op: 'advertise_service', + type: this.serviceType, + service: this.name + }); + this.isAdvertised = true; +}; + +Service.prototype.unadvertise = function() { + if (!this.isAdvertised) { + return; + } + this.ros.callOnConnection({ + op: 'unadvertise_service', + service: this.name + }); + this.isAdvertised = false; +}; + +Service.prototype._serviceResponse = function(rosbridgeRequest) { + var response = {}; + var success = this._serviceCallback(rosbridgeRequest.args, response); + + var call = { + op: 'service_response', + service: this.name, + values: new ServiceResponse(response), + result: success + }; + + if (rosbridgeRequest.id) { + call.id = rosbridgeRequest.id; + } + + this.ros.callOnConnection(call); +}; + +module.exports = Service; + +},{"./ServiceRequest":16,"./ServiceResponse":17,"eventemitter2":2}],16:[function(require,module,exports){ +/** + * @fileoverview + * @author Brandon Alexander - balexander@willowgarage.com + */ + +var assign = require('object-assign'); + +/** + * A ServiceRequest is passed into the service call. + * + * @constructor + * @param values - object matching the fields defined in the .srv definition file + */ +function ServiceRequest(values) { + assign(this, values); +} + +module.exports = ServiceRequest; +},{"object-assign":3}],17:[function(require,module,exports){ +/** + * @fileoverview + * @author Brandon Alexander - balexander@willowgarage.com + */ + +var assign = require('object-assign'); + +/** + * A ServiceResponse is returned from the service call. + * + * @constructor + * @param values - object matching the fields defined in the .srv definition file + */ +function ServiceResponse(values) { + assign(this, values); +} + +module.exports = ServiceResponse; +},{"object-assign":3}],18:[function(require,module,exports){ +/** + * Socket event handling utilities for handling events on either + * WebSocket and TCP sockets + * + * Note to anyone reviewing this code: these functions are called + * in the context of their parent object, unless bound + * @fileOverview + */ +'use strict'; + +var decompressPng = require('../util/decompressPng'); +var CBOR = require('cbor-js'); +var typedArrayTagger = require('../util/cborTypedArrayTags'); +var WebSocket = require('ws'); +var BSON = null; +if(typeof bson !== 'undefined'){ + BSON = bson().BSON; +} + +/** + * Events listeners for a WebSocket or TCP socket to a JavaScript + * ROS Client. Sets up Messages for a given topic to trigger an + * event on the ROS client. + * + * @namespace SocketAdapter + * @private + */ +function SocketAdapter(client) { + function handleMessage(message) { + if (message.op === 'publish') { + client.emit(message.topic, message.msg); + } else if (message.op === 'service_response') { + client.emit(message.id, message); + } else if (message.op === 'call_service') { + client.emit(message.service, message); + } else if(message.op === 'status'){ + if(message.id){ + client.emit('status:'+message.id, message); + } else { + client.emit('status', message); + } + } + } + + function handlePng(message, callback) { + if (message.op === 'png') { + decompressPng(message.data, callback); + } else { + callback(message); + } + } + + function decodeBSON(data, callback) { + if (!BSON) { + throw 'Cannot process BSON encoded message without BSON header.'; + } + var reader = new FileReader(); + reader.onload = function() { + var uint8Array = new Uint8Array(this.result); + var msg = BSON.deserialize(uint8Array); + callback(msg); + }; + reader.readAsArrayBuffer(data); + } + + return { + /** + * Emits a 'connection' event on WebSocket connection. + * + * @param event - the argument to emit with the event. + * @memberof SocketAdapter + */ + onopen: function onOpen(event) { + client.isConnected = true; + client.emit('connection', event); + }, + + /** + * Emits a 'close' event on WebSocket disconnection. + * + * @param event - the argument to emit with the event. + * @memberof SocketAdapter + */ + onclose: function onClose(event) { + client.isConnected = false; + client.emit('close', event); + }, + + /** + * Emits an 'error' event whenever there was an error. + * + * @param event - the argument to emit with the event. + * @memberof SocketAdapter + */ + onerror: function onError(event) { + client.emit('error', event); + }, + + /** + * Parses message responses from rosbridge and sends to the appropriate + * topic, service, or param. + * + * @param message - the raw JSON message from rosbridge. + * @memberof SocketAdapter + */ + onmessage: function onMessage(data) { + if (typeof Blob !== 'undefined' && data.data instanceof Blob) { + decodeBSON(data.data, function (message) { + handlePng(message, handleMessage); + }); + } else if (data.data instanceof ArrayBuffer) { + var decoded = CBOR.decode(data.data, typedArrayTagger); + handleMessage(decoded); + } else { + var message = JSON.parse(typeof data === 'string' ? data : data.data); + handlePng(message, handleMessage); + } + } + }; +} + +module.exports = SocketAdapter; + +},{"../util/cborTypedArrayTags":41,"../util/decompressPng":44,"cbor-js":1,"ws":42}],19:[function(require,module,exports){ +/** + * @fileoverview + * @author Brandon Alexander - baalexander@gmail.com + */ + +var EventEmitter2 = require('eventemitter2').EventEmitter2; +var Message = require('./Message'); + +/** + * Publish and/or subscribe to a topic in ROS. + * + * Emits the following events: + * * 'warning' - if there are any warning during the Topic creation + * * 'message' - the message data from rosbridge + * + * @constructor + * @param options - object with following keys: + * * ros - the ROSLIB.Ros connection handle + * * name - the topic name, like /cmd_vel + * * messageType - the message type, like 'std_msgs/String' + * * compression - the type of compression to use, like 'png' or 'cbor' + * * throttle_rate - the rate (in ms in between messages) at which to throttle the topics + * * queue_size - the queue created at bridge side for re-publishing webtopics (defaults to 100) + * * latch - latch the topic when publishing + * * queue_length - the queue length at bridge side used when subscribing (defaults to 0, no queueing). + * * reconnect_on_close - the flag to enable resubscription and readvertisement on close event(defaults to true). + */ +function Topic(options) { + options = options || {}; + this.ros = options.ros; + this.name = options.name; + this.messageType = options.messageType; + this.isAdvertised = false; + this.compression = options.compression || 'none'; + this.throttle_rate = options.throttle_rate || 0; + this.latch = options.latch || false; + this.queue_size = options.queue_size || 100; + this.queue_length = options.queue_length || 0; + this.reconnect_on_close = options.reconnect_on_close || true; + + // Check for valid compression types + if (this.compression && this.compression !== 'png' && + this.compression !== 'cbor' && this.compression !== 'none') { + this.emit('warning', this.compression + + ' compression is not supported. No compression will be used.'); + this.compression = 'none'; + } + + // Check if throttle rate is negative + if (this.throttle_rate < 0) { + this.emit('warning', this.throttle_rate + ' is not allowed. Set to 0'); + this.throttle_rate = 0; + } + + var that = this; + if (this.reconnect_on_close) { + this.callForSubscribeAndAdvertise = function(message) { + that.ros.callOnConnection(message); + + that.waitForReconnect = false; + that.reconnectFunc = function() { + if(!that.waitForReconnect) { + that.waitForReconnect = true; + that.ros.callOnConnection(message); + that.ros.once('connection', function() { + that.waitForReconnect = false; + }); + } + }; + that.ros.on('close', that.reconnectFunc); + }; + } + else { + this.callForSubscribeAndAdvertise = this.ros.callOnConnection; + } + + this._messageCallback = function(data) { + that.emit('message', new Message(data)); + }; +} +Topic.prototype.__proto__ = EventEmitter2.prototype; + +/** + * Every time a message is published for the given topic, the callback + * will be called with the message object. + * + * @param callback - function with the following params: + * * message - the published message + */ +Topic.prototype.subscribe = function(callback) { + if (typeof callback === 'function') { + this.on('message', callback); + } + + if (this.subscribeId) { return; } + this.ros.on(this.name, this._messageCallback); + this.subscribeId = 'subscribe:' + this.name + ':' + (++this.ros.idCounter); + + this.callForSubscribeAndAdvertise({ + op: 'subscribe', + id: this.subscribeId, + type: this.messageType, + topic: this.name, + compression: this.compression, + throttle_rate: this.throttle_rate, + queue_length: this.queue_length + }); +}; + +/** + * Unregisters as a subscriber for the topic. Unsubscribing stop remove + * all subscribe callbacks. To remove a call back, you must explicitly + * pass the callback function in. + * + * @param callback - the optional callback to unregister, if + * * provided and other listeners are registered the topic won't + * * unsubscribe, just stop emitting to the passed listener + */ +Topic.prototype.unsubscribe = function(callback) { + if (callback) { + this.off('message', callback); + // If there is any other callbacks still subscribed don't unsubscribe + if (this.listeners('message').length) { return; } + } + if (!this.subscribeId) { return; } + // Note: Don't call this.removeAllListeners, allow client to handle that themselves + this.ros.off(this.name, this._messageCallback); + if(this.reconnect_on_close) { + this.ros.off('close', this.reconnectFunc); + } + this.emit('unsubscribe'); + this.ros.callOnConnection({ + op: 'unsubscribe', + id: this.subscribeId, + topic: this.name + }); + this.subscribeId = null; +}; + + +/** + * Registers as a publisher for the topic. + */ +Topic.prototype.advertise = function() { + if (this.isAdvertised) { + return; + } + this.advertiseId = 'advertise:' + this.name + ':' + (++this.ros.idCounter); + this.callForSubscribeAndAdvertise({ + op: 'advertise', + id: this.advertiseId, + type: this.messageType, + topic: this.name, + latch: this.latch, + queue_size: this.queue_size + }); + this.isAdvertised = true; + + if(!this.reconnect_on_close) { + var that = this; + this.ros.on('close', function() { + that.isAdvertised = false; + }); + } +}; + +/** + * Unregisters as a publisher for the topic. + */ +Topic.prototype.unadvertise = function() { + if (!this.isAdvertised) { + return; + } + if(this.reconnect_on_close) { + this.ros.off('close', this.reconnectFunc); + } + this.emit('unadvertise'); + this.ros.callOnConnection({ + op: 'unadvertise', + id: this.advertiseId, + topic: this.name + }); + this.isAdvertised = false; +}; + +/** + * Publish the message. + * + * @param message - A ROSLIB.Message object. + */ +Topic.prototype.publish = function(message) { + if (!this.isAdvertised) { + this.advertise(); + } + + this.ros.idCounter++; + var call = { + op: 'publish', + id: 'publish:' + this.name + ':' + this.ros.idCounter, + topic: this.name, + msg: message, + latch: this.latch + }; + this.ros.callOnConnection(call); +}; + +module.exports = Topic; + +},{"./Message":12,"eventemitter2":2}],20:[function(require,module,exports){ +var mixin = require('../mixin'); + +var core = module.exports = { + Ros: require('./Ros'), + Topic: require('./Topic'), + Message: require('./Message'), + Param: require('./Param'), + Service: require('./Service'), + ServiceRequest: require('./ServiceRequest'), + ServiceResponse: require('./ServiceResponse') +}; + +mixin(core.Ros, ['Param', 'Service', 'Topic'], core); + +},{"../mixin":26,"./Message":12,"./Param":13,"./Ros":14,"./Service":15,"./ServiceRequest":16,"./ServiceResponse":17,"./Topic":19}],21:[function(require,module,exports){ +/** + * @fileoverview + * @author David Gossow - dgossow@willowgarage.com + */ + +var Vector3 = require('./Vector3'); +var Quaternion = require('./Quaternion'); + +/** + * A Pose in 3D space. Values are copied into this object. + * + * @constructor + * @param options - object with following keys: + * * position - the Vector3 describing the position + * * orientation - the ROSLIB.Quaternion describing the orientation + */ +function Pose(options) { + options = options || {}; + // copy the values into this object if they exist + this.position = new Vector3(options.position); + this.orientation = new Quaternion(options.orientation); +} + +/** + * Apply a transform against this pose. + * + * @param tf the transform + */ +Pose.prototype.applyTransform = function(tf) { + this.position.multiplyQuaternion(tf.rotation); + this.position.add(tf.translation); + var tmp = tf.rotation.clone(); + tmp.multiply(this.orientation); + this.orientation = tmp; +}; + +/** + * Clone a copy of this pose. + * + * @returns the cloned pose + */ +Pose.prototype.clone = function() { + return new Pose(this); +}; + +/** + * Multiplies this pose with another pose without altering this pose. + * + * @returns Result of multiplication. + */ +Pose.prototype.multiply = function(pose) { + var p = pose.clone(); + p.applyTransform({ rotation: this.orientation, translation: this.position }); + return p; +}; + +/** + * Computes the inverse of this pose. + * + * @returns Inverse of pose. + */ +Pose.prototype.getInverse = function() { + var inverse = this.clone(); + inverse.orientation.invert(); + inverse.position.multiplyQuaternion(inverse.orientation); + inverse.position.x *= -1; + inverse.position.y *= -1; + inverse.position.z *= -1; + return inverse; +}; + +module.exports = Pose; +},{"./Quaternion":22,"./Vector3":24}],22:[function(require,module,exports){ +/** + * @fileoverview + * @author David Gossow - dgossow@willowgarage.com + */ + +/** + * A Quaternion. + * + * @constructor + * @param options - object with following keys: + * * x - the x value + * * y - the y value + * * z - the z value + * * w - the w value + */ +function Quaternion(options) { + options = options || {}; + this.x = options.x || 0; + this.y = options.y || 0; + this.z = options.z || 0; + this.w = (typeof options.w === 'number') ? options.w : 1; +} + +/** + * Perform a conjugation on this quaternion. + */ +Quaternion.prototype.conjugate = function() { + this.x *= -1; + this.y *= -1; + this.z *= -1; +}; + +/** + * Return the norm of this quaternion. + */ +Quaternion.prototype.norm = function() { + return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); +}; + +/** + * Perform a normalization on this quaternion. + */ +Quaternion.prototype.normalize = function() { + var l = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w); + if (l === 0) { + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 1; + } else { + l = 1 / l; + this.x = this.x * l; + this.y = this.y * l; + this.z = this.z * l; + this.w = this.w * l; + } +}; + +/** + * Convert this quaternion into its inverse. + */ +Quaternion.prototype.invert = function() { + this.conjugate(); + this.normalize(); +}; + +/** + * Set the values of this quaternion to the product of itself and the given quaternion. + * + * @param q the quaternion to multiply with + */ +Quaternion.prototype.multiply = function(q) { + var newX = this.x * q.w + this.y * q.z - this.z * q.y + this.w * q.x; + var newY = -this.x * q.z + this.y * q.w + this.z * q.x + this.w * q.y; + var newZ = this.x * q.y - this.y * q.x + this.z * q.w + this.w * q.z; + var newW = -this.x * q.x - this.y * q.y - this.z * q.z + this.w * q.w; + this.x = newX; + this.y = newY; + this.z = newZ; + this.w = newW; +}; + +/** + * Clone a copy of this quaternion. + * + * @returns the cloned quaternion + */ +Quaternion.prototype.clone = function() { + return new Quaternion(this); +}; + +module.exports = Quaternion; + +},{}],23:[function(require,module,exports){ +/** + * @fileoverview + * @author David Gossow - dgossow@willowgarage.com + */ + +var Vector3 = require('./Vector3'); +var Quaternion = require('./Quaternion'); + +/** + * A Transform in 3-space. Values are copied into this object. + * + * @constructor + * @param options - object with following keys: + * * translation - the Vector3 describing the translation + * * rotation - the ROSLIB.Quaternion describing the rotation + */ +function Transform(options) { + options = options || {}; + // Copy the values into this object if they exist + this.translation = new Vector3(options.translation); + this.rotation = new Quaternion(options.rotation); +} + +/** + * Clone a copy of this transform. + * + * @returns the cloned transform + */ +Transform.prototype.clone = function() { + return new Transform(this); +}; + +module.exports = Transform; +},{"./Quaternion":22,"./Vector3":24}],24:[function(require,module,exports){ +/** + * @fileoverview + * @author David Gossow - dgossow@willowgarage.com + */ + +/** + * A 3D vector. + * + * @constructor + * @param options - object with following keys: + * * x - the x value + * * y - the y value + * * z - the z value + */ +function Vector3(options) { + options = options || {}; + this.x = options.x || 0; + this.y = options.y || 0; + this.z = options.z || 0; +} + +/** + * Set the values of this vector to the sum of itself and the given vector. + * + * @param v the vector to add with + */ +Vector3.prototype.add = function(v) { + this.x += v.x; + this.y += v.y; + this.z += v.z; +}; + +/** + * Set the values of this vector to the difference of itself and the given vector. + * + * @param v the vector to subtract with + */ +Vector3.prototype.subtract = function(v) { + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; +}; + +/** + * Multiply the given Quaternion with this vector. + * + * @param q - the quaternion to multiply with + */ +Vector3.prototype.multiplyQuaternion = function(q) { + var ix = q.w * this.x + q.y * this.z - q.z * this.y; + var iy = q.w * this.y + q.z * this.x - q.x * this.z; + var iz = q.w * this.z + q.x * this.y - q.y * this.x; + var iw = -q.x * this.x - q.y * this.y - q.z * this.z; + this.x = ix * q.w + iw * -q.x + iy * -q.z - iz * -q.y; + this.y = iy * q.w + iw * -q.y + iz * -q.x - ix * -q.z; + this.z = iz * q.w + iw * -q.z + ix * -q.y - iy * -q.x; +}; + +/** + * Clone a copy of this vector. + * + * @returns the cloned vector + */ +Vector3.prototype.clone = function() { + return new Vector3(this); +}; + +module.exports = Vector3; +},{}],25:[function(require,module,exports){ +module.exports = { + Pose: require('./Pose'), + Quaternion: require('./Quaternion'), + Transform: require('./Transform'), + Vector3: require('./Vector3') +}; + +},{"./Pose":21,"./Quaternion":22,"./Transform":23,"./Vector3":24}],26:[function(require,module,exports){ +/** + * Mixin a feature to the core/Ros prototype. + * For example, mixin(Ros, ['Topic'], {Topic: }) + * will add a topic bound to any Ros instances so a user + * can call `var topic = ros.Topic({name: '/foo'});` + * + * @author Graeme Yeates - github.com/megawac + */ +module.exports = function(Ros, classes, features) { + classes.forEach(function(className) { + var Class = features[className]; + Ros.prototype[className] = function(options) { + options.ros = this; + return new Class(options); + }; + }); +}; + +},{}],27:[function(require,module,exports){ +/** + * @fileoverview + * @author David Gossow - dgossow@willowgarage.com + */ + +var ActionClient = require('../actionlib/ActionClient'); +var Goal = require('../actionlib/Goal'); + +var Service = require('../core/Service.js'); +var ServiceRequest = require('../core/ServiceRequest.js'); + +var Transform = require('../math/Transform'); + +/** + * A TF Client that listens to TFs from tf2_web_republisher. + * + * @constructor + * @param options - object with following keys: + * * ros - the ROSLIB.Ros connection handle + * * fixedFrame - the fixed frame, like /base_link + * * angularThres - the angular threshold for the TF republisher + * * transThres - the translation threshold for the TF republisher + * * rate - the rate for the TF republisher + * * updateDelay - the time (in ms) to wait after a new subscription + * to update the TF republisher's list of TFs + * * topicTimeout - the timeout parameter for the TF republisher + * * serverName (optional) - the name of the tf2_web_republisher server + * * repubServiceName (optional) - the name of the republish_tfs service (non groovy compatibility mode only) + * default: '/republish_tfs' + */ +function TFClient(options) { + options = options || {}; + this.ros = options.ros; + this.fixedFrame = options.fixedFrame || '/base_link'; + this.angularThres = options.angularThres || 2.0; + this.transThres = options.transThres || 0.01; + this.rate = options.rate || 10.0; + this.updateDelay = options.updateDelay || 50; + var seconds = options.topicTimeout || 2.0; + var secs = Math.floor(seconds); + var nsecs = Math.floor((seconds - secs) * 1000000000); + this.topicTimeout = { + secs: secs, + nsecs: nsecs + }; + this.serverName = options.serverName || '/tf2_web_republisher'; + this.repubServiceName = options.repubServiceName || '/republish_tfs'; + + this.currentGoal = false; + this.currentTopic = false; + this.frameInfos = {}; + this.republisherUpdateRequested = false; + + // Create an Action client + this.actionClient = this.ros.ActionClient({ + serverName : this.serverName, + actionName : 'tf2_web_republisher/TFSubscriptionAction', + omitStatus : true, + omitResult : true + }); + + // Create a Service client + this.serviceClient = this.ros.Service({ + name: this.repubServiceName, + serviceType: 'tf2_web_republisher/RepublishTFs' + }); +} + +/** + * Process the incoming TF message and send them out using the callback + * functions. + * + * @param tf - the TF message from the server + */ +TFClient.prototype.processTFArray = function(tf) { + var that = this; + tf.transforms.forEach(function(transform) { + var frameID = transform.child_frame_id; + if (frameID[0] === '/') + { + frameID = frameID.substring(1); + } + var info = this.frameInfos[frameID]; + if (info) { + info.transform = new Transform({ + translation : transform.transform.translation, + rotation : transform.transform.rotation + }); + info.cbs.forEach(function(cb) { + cb(info.transform); + }); + } + }, this); +}; + +/** + * Create and send a new goal (or service request) to the tf2_web_republisher + * based on the current list of TFs. + */ +TFClient.prototype.updateGoal = function() { + var goalMessage = { + source_frames : Object.keys(this.frameInfos), + target_frame : this.fixedFrame, + angular_thres : this.angularThres, + trans_thres : this.transThres, + rate : this.rate + }; + + // if we're running in groovy compatibility mode (the default) + // then use the action interface to tf2_web_republisher + if(this.ros.groovyCompatibility) { + if (this.currentGoal) { + this.currentGoal.cancel(); + } + this.currentGoal = new Goal({ + actionClient : this.actionClient, + goalMessage : goalMessage + }); + + this.currentGoal.on('feedback', this.processTFArray.bind(this)); + this.currentGoal.send(); + } + else { + // otherwise, use the service interface + // The service interface has the same parameters as the action, + // plus the timeout + goalMessage.timeout = this.topicTimeout; + var request = new ServiceRequest(goalMessage); + + this.serviceClient.callService(request, this.processResponse.bind(this)); + } + + this.republisherUpdateRequested = false; +}; + +/** + * Process the service response and subscribe to the tf republisher + * topic + * + * @param response the service response containing the topic name + */ +TFClient.prototype.processResponse = function(response) { + // if we subscribed to a topic before, unsubscribe so + // the republisher stops publishing it + if (this.currentTopic) { + this.currentTopic.unsubscribe(); + } + + this.currentTopic = this.ros.Topic({ + name: response.topic_name, + messageType: 'tf2_web_republisher/TFArray' + }); + this.currentTopic.subscribe(this.processTFArray.bind(this)); +}; + +/** + * Subscribe to the given TF frame. + * + * @param frameID - the TF frame to subscribe to + * @param callback - function with params: + * * transform - the transform data + */ +TFClient.prototype.subscribe = function(frameID, callback) { + // remove leading slash, if it's there + if (frameID[0] === '/') + { + frameID = frameID.substring(1); + } + // if there is no callback registered for the given frame, create emtpy callback list + if (!this.frameInfos[frameID]) { + this.frameInfos[frameID] = { + cbs: [] + }; + if (!this.republisherUpdateRequested) { + setTimeout(this.updateGoal.bind(this), this.updateDelay); + this.republisherUpdateRequested = true; + } + } + // if we already have a transform, call back immediately + else if (this.frameInfos[frameID].transform) { + callback(this.frameInfos[frameID].transform); + } + this.frameInfos[frameID].cbs.push(callback); +}; + +/** + * Unsubscribe from the given TF frame. + * + * @param frameID - the TF frame to unsubscribe from + * @param callback - the callback function to remove + */ +TFClient.prototype.unsubscribe = function(frameID, callback) { + // remove leading slash, if it's there + if (frameID[0] === '/') + { + frameID = frameID.substring(1); + } + var info = this.frameInfos[frameID]; + for (var cbs = info && info.cbs || [], idx = cbs.length; idx--;) { + if (cbs[idx] === callback) { + cbs.splice(idx, 1); + } + } + if (!callback || cbs.length === 0) { + delete this.frameInfos[frameID]; + } +}; + +/** + * Unsubscribe and unadvertise all topics associated with this TFClient. + */ +TFClient.prototype.dispose = function() { + this.actionClient.dispose(); + if (this.currentTopic) { + this.currentTopic.unsubscribe(); + } +}; + +module.exports = TFClient; + +},{"../actionlib/ActionClient":7,"../actionlib/Goal":9,"../core/Service.js":15,"../core/ServiceRequest.js":16,"../math/Transform":23}],28:[function(require,module,exports){ +var Ros = require('../core/Ros'); +var mixin = require('../mixin'); + +var tf = module.exports = { + TFClient: require('./TFClient') +}; + +mixin(Ros, ['TFClient'], tf); +},{"../core/Ros":14,"../mixin":26,"./TFClient":27}],29:[function(require,module,exports){ +/** + * @fileOverview + * @author Benjamin Pitzer - ben.pitzer@gmail.com + * @author Russell Toris - rctoris@wpi.edu + */ + +var Vector3 = require('../math/Vector3'); +var UrdfTypes = require('./UrdfTypes'); + +/** + * A Box element in a URDF. + * + * @constructor + * @param options - object with following keys: + * * xml - the XML element to parse + */ +function UrdfBox(options) { + this.dimension = null; + this.type = UrdfTypes.URDF_BOX; + + // Parse the xml string + var xyz = options.xml.getAttribute('size').split(' '); + this.dimension = new Vector3({ + x : parseFloat(xyz[0]), + y : parseFloat(xyz[1]), + z : parseFloat(xyz[2]) + }); +} + +module.exports = UrdfBox; +},{"../math/Vector3":24,"./UrdfTypes":38}],30:[function(require,module,exports){ +/** + * @fileOverview + * @author Benjamin Pitzer - ben.pitzer@gmail.com + * @author Russell Toris - rctoris@wpi.edu + */ + +/** + * A Color element in a URDF. + * + * @constructor + * @param options - object with following keys: + * * xml - the XML element to parse + */ +function UrdfColor(options) { + // Parse the xml string + var rgba = options.xml.getAttribute('rgba').split(' '); + this.r = parseFloat(rgba[0]); + this.g = parseFloat(rgba[1]); + this.b = parseFloat(rgba[2]); + this.a = parseFloat(rgba[3]); +} + +module.exports = UrdfColor; +},{}],31:[function(require,module,exports){ +/** + * @fileOverview + * @author Benjamin Pitzer - ben.pitzer@gmail.com + * @author Russell Toris - rctoris@wpi.edu + */ + +var UrdfTypes = require('./UrdfTypes'); + +/** + * A Cylinder element in a URDF. + * + * @constructor + * @param options - object with following keys: + * * xml - the XML element to parse + */ +function UrdfCylinder(options) { + this.type = UrdfTypes.URDF_CYLINDER; + this.length = parseFloat(options.xml.getAttribute('length')); + this.radius = parseFloat(options.xml.getAttribute('radius')); +} + +module.exports = UrdfCylinder; +},{"./UrdfTypes":38}],32:[function(require,module,exports){ +/** + * @fileOverview + * @author David V. Lu!! davidvlu@gmail.com + */ + +var Pose = require('../math/Pose'); +var Vector3 = require('../math/Vector3'); +var Quaternion = require('../math/Quaternion'); + +/** + * A Joint element in a URDF. + * + * @constructor + * @param options - object with following keys: + * * xml - the XML element to parse + */ +function UrdfJoint(options) { + this.name = options.xml.getAttribute('name'); + this.type = options.xml.getAttribute('type'); + + var parents = options.xml.getElementsByTagName('parent'); + if(parents.length > 0) { + this.parent = parents[0].getAttribute('link'); + } + + var children = options.xml.getElementsByTagName('child'); + if(children.length > 0) { + this.child = children[0].getAttribute('link'); + } + + var limits = options.xml.getElementsByTagName('limit'); + if (limits.length > 0) { + this.minval = parseFloat( limits[0].getAttribute('lower') ); + this.maxval = parseFloat( limits[0].getAttribute('upper') ); + } + + // Origin + var origins = options.xml.getElementsByTagName('origin'); + if (origins.length === 0) { + // use the identity as the default + this.origin = new Pose(); + } else { + // Check the XYZ + var xyz = origins[0].getAttribute('xyz'); + var position = new Vector3(); + if (xyz) { + xyz = xyz.split(' '); + position = new Vector3({ + x : parseFloat(xyz[0]), + y : parseFloat(xyz[1]), + z : parseFloat(xyz[2]) + }); + } + + // Check the RPY + var rpy = origins[0].getAttribute('rpy'); + var orientation = new Quaternion(); + if (rpy) { + rpy = rpy.split(' '); + // Convert from RPY + var roll = parseFloat(rpy[0]); + var pitch = parseFloat(rpy[1]); + var yaw = parseFloat(rpy[2]); + var phi = roll / 2.0; + var the = pitch / 2.0; + var psi = yaw / 2.0; + var x = Math.sin(phi) * Math.cos(the) * Math.cos(psi) - Math.cos(phi) * Math.sin(the) + * Math.sin(psi); + var y = Math.cos(phi) * Math.sin(the) * Math.cos(psi) + Math.sin(phi) * Math.cos(the) + * Math.sin(psi); + var z = Math.cos(phi) * Math.cos(the) * Math.sin(psi) - Math.sin(phi) * Math.sin(the) + * Math.cos(psi); + var w = Math.cos(phi) * Math.cos(the) * Math.cos(psi) + Math.sin(phi) * Math.sin(the) + * Math.sin(psi); + + orientation = new Quaternion({ + x : x, + y : y, + z : z, + w : w + }); + orientation.normalize(); + } + this.origin = new Pose({ + position : position, + orientation : orientation + }); + } +} + +module.exports = UrdfJoint; + +},{"../math/Pose":21,"../math/Quaternion":22,"../math/Vector3":24}],33:[function(require,module,exports){ +/** + * @fileOverview + * @author Benjamin Pitzer - ben.pitzer@gmail.com + * @author Russell Toris - rctoris@wpi.edu + */ + +var UrdfVisual = require('./UrdfVisual'); + +/** + * A Link element in a URDF. + * + * @constructor + * @param options - object with following keys: + * * xml - the XML element to parse + */ +function UrdfLink(options) { + this.name = options.xml.getAttribute('name'); + this.visuals = []; + var visuals = options.xml.getElementsByTagName('visual'); + + for( var i=0; i 0) { + this.textureFilename = textures[0].getAttribute('filename'); + } + + // Color + var colors = options.xml.getElementsByTagName('color'); + if (colors.length > 0) { + // Parse the RBGA string + this.color = new UrdfColor({ + xml : colors[0] + }); + } +} + +UrdfMaterial.prototype.isLink = function() { + return this.color === null && this.textureFilename === null; +}; + +var assign = require('object-assign'); + +UrdfMaterial.prototype.assign = function(obj) { + return assign(this, obj); +}; + +module.exports = UrdfMaterial; + +},{"./UrdfColor":30,"object-assign":3}],35:[function(require,module,exports){ +/** + * @fileOverview + * @author Benjamin Pitzer - ben.pitzer@gmail.com + * @author Russell Toris - rctoris@wpi.edu + */ + +var Vector3 = require('../math/Vector3'); +var UrdfTypes = require('./UrdfTypes'); + +/** + * A Mesh element in a URDF. + * + * @constructor + * @param options - object with following keys: + * * xml - the XML element to parse + */ +function UrdfMesh(options) { + this.scale = null; + + this.type = UrdfTypes.URDF_MESH; + this.filename = options.xml.getAttribute('filename'); + + // Check for a scale + var scale = options.xml.getAttribute('scale'); + if (scale) { + // Get the XYZ + var xyz = scale.split(' '); + this.scale = new Vector3({ + x : parseFloat(xyz[0]), + y : parseFloat(xyz[1]), + z : parseFloat(xyz[2]) + }); + } +} + +module.exports = UrdfMesh; +},{"../math/Vector3":24,"./UrdfTypes":38}],36:[function(require,module,exports){ +/** + * @fileOverview + * @author Benjamin Pitzer - ben.pitzer@gmail.com + * @author Russell Toris - rctoris@wpi.edu + */ + +var UrdfMaterial = require('./UrdfMaterial'); +var UrdfLink = require('./UrdfLink'); +var UrdfJoint = require('./UrdfJoint'); +var DOMParser = require('xmldom').DOMParser; + +// See https://developer.mozilla.org/docs/XPathResult#Constants +var XPATH_FIRST_ORDERED_NODE_TYPE = 9; + +/** + * A URDF Model can be used to parse a given URDF into the appropriate elements. + * + * @constructor + * @param options - object with following keys: + * * xml - the XML element to parse + * * string - the XML element to parse as a string + */ +function UrdfModel(options) { + options = options || {}; + var xmlDoc = options.xml; + var string = options.string; + this.materials = {}; + this.links = {}; + this.joints = {}; + + // Check if we are using a string or an XML element + if (string) { + // Parse the string + var parser = new DOMParser(); + xmlDoc = parser.parseFromString(string, 'text/xml'); + } + + // Initialize the model with the given XML node. + // Get the robot tag + var robotXml = xmlDoc.documentElement; + + // Get the robot name + this.name = robotXml.getAttribute('name'); + + // Parse all the visual elements we need + for (var nodes = robotXml.childNodes, i = 0; i < nodes.length; i++) { + var node = nodes[i]; + if (node.tagName === 'material') { + var material = new UrdfMaterial({ + xml : node + }); + // Make sure this is unique + if (this.materials[material.name] !== void 0) { + if( this.materials[material.name].isLink() ) { + this.materials[material.name].assign( material ); + } else { + console.warn('Material ' + material.name + 'is not unique.'); + } + } else { + this.materials[material.name] = material; + } + } else if (node.tagName === 'link') { + var link = new UrdfLink({ + xml : node + }); + // Make sure this is unique + if (this.links[link.name] !== void 0) { + console.warn('Link ' + link.name + ' is not unique.'); + } else { + // Check for a material + for( var j=0; j 0) { + var geom = geoms[0]; + var shape = null; + // Check for the shape + for (var i = 0; i < geom.childNodes.length; i++) { + var node = geom.childNodes[i]; + if (node.nodeType === 1) { + shape = node; + break; + } + } + // Check the type + var type = shape.nodeName; + if (type === 'sphere') { + this.geometry = new UrdfSphere({ + xml : shape + }); + } else if (type === 'box') { + this.geometry = new UrdfBox({ + xml : shape + }); + } else if (type === 'cylinder') { + this.geometry = new UrdfCylinder({ + xml : shape + }); + } else if (type === 'mesh') { + this.geometry = new UrdfMesh({ + xml : shape + }); + } else { + console.warn('Unknown geometry type ' + type); + } + } + + // Material + var materials = xml.getElementsByTagName('material'); + if (materials.length > 0) { + this.material = new UrdfMaterial({ + xml : materials[0] + }); + } +} + +module.exports = UrdfVisual; +},{"../math/Pose":21,"../math/Quaternion":22,"../math/Vector3":24,"./UrdfBox":29,"./UrdfCylinder":31,"./UrdfMaterial":34,"./UrdfMesh":35,"./UrdfSphere":37}],40:[function(require,module,exports){ +module.exports = require('object-assign')({ + UrdfBox: require('./UrdfBox'), + UrdfColor: require('./UrdfColor'), + UrdfCylinder: require('./UrdfCylinder'), + UrdfLink: require('./UrdfLink'), + UrdfMaterial: require('./UrdfMaterial'), + UrdfMesh: require('./UrdfMesh'), + UrdfModel: require('./UrdfModel'), + UrdfSphere: require('./UrdfSphere'), + UrdfVisual: require('./UrdfVisual') +}, require('./UrdfTypes')); + +},{"./UrdfBox":29,"./UrdfColor":30,"./UrdfCylinder":31,"./UrdfLink":33,"./UrdfMaterial":34,"./UrdfMesh":35,"./UrdfModel":36,"./UrdfSphere":37,"./UrdfTypes":38,"./UrdfVisual":39,"object-assign":3}],41:[function(require,module,exports){ +'use strict'; + +var UPPER32 = Math.pow(2, 32); + +var warnedPrecision = false; +function warnPrecision() { + if (!warnedPrecision) { + warnedPrecision = true; + console.warn('CBOR 64-bit integer array values may lose precision. No further warnings.'); + } +} + +/** + * Unpacks 64-bit unsigned integer from byte array. + * @param {Uint8Array} bytes +*/ +function decodeUint64LE(bytes) { + warnPrecision(); + + var byteLen = bytes.byteLength; + var offset = bytes.byteOffset; + var arrLen = byteLen / 8; + + var buffer = bytes.buffer.slice(offset, offset + byteLen); + var uint32View = new Uint32Array(buffer); + + var arr = new Array(arrLen); + for (var i = 0; i < arrLen; i++) { + var si = i * 2; + var lo = uint32View[si]; + var hi = uint32View[si+1]; + arr[i] = lo + UPPER32 * hi; + } + + return arr; +} + +/** + * Unpacks 64-bit signed integer from byte array. + * @param {Uint8Array} bytes +*/ +function decodeInt64LE(bytes) { + warnPrecision(); + + var byteLen = bytes.byteLength; + var offset = bytes.byteOffset; + var arrLen = byteLen / 8; + + var buffer = bytes.buffer.slice(offset, offset + byteLen); + var uint32View = new Uint32Array(buffer); + var int32View = new Int32Array(buffer); + + var arr = new Array(arrLen); + for (var i = 0; i < arrLen; i++) { + var si = i * 2; + var lo = uint32View[si]; + var hi = int32View[si+1]; + arr[i] = lo + UPPER32 * hi; + } + + return arr; +} + +/** + * Unpacks typed array from byte array. + * @param {Uint8Array} bytes + * @param {type} ArrayType - desired output array type +*/ +function decodeNativeArray(bytes, ArrayType) { + var byteLen = bytes.byteLength; + var offset = bytes.byteOffset; + var buffer = bytes.buffer.slice(offset, offset + byteLen); + return new ArrayType(buffer); +} + +/** + * Support a subset of draft CBOR typed array tags: + * + * Only support little-endian tags for now. + */ +var nativeArrayTypes = { + 64: Uint8Array, + 69: Uint16Array, + 70: Uint32Array, + 72: Int8Array, + 77: Int16Array, + 78: Int32Array, + 85: Float32Array, + 86: Float64Array +}; + +/** + * We can also decode 64-bit integer arrays, since ROS has these types. + */ +var conversionArrayTypes = { + 71: decodeUint64LE, + 79: decodeInt64LE +}; + +/** + * Handles CBOR typed array tags during decoding. + * @param {Uint8Array} data + * @param {Number} tag + */ +function cborTypedArrayTagger(data, tag) { + if (tag in nativeArrayTypes) { + var arrayType = nativeArrayTypes[tag]; + return decodeNativeArray(data, arrayType); + } + if (tag in conversionArrayTypes) { + return conversionArrayTypes[tag](data); + } + return data; +} + +if (typeof module !== 'undefined' && module.exports) { + module.exports = cborTypedArrayTagger; +} + +},{}],42:[function(require,module,exports){ +module.exports = window.WebSocket; + +},{}],43:[function(require,module,exports){ +/* global document */ +module.exports = function Canvas() { + return document.createElement('canvas'); +}; +},{}],44:[function(require,module,exports){ +/** + * @fileOverview + * @author Graeme Yeates - github.com/megawac + */ + +'use strict'; + +var Canvas = require('canvas'); +var Image = Canvas.Image || window.Image; + +/** + * If a message was compressed as a PNG image (a compression hack since + * gzipping over WebSockets * is not supported yet), this function places the + * "image" in a canvas element then decodes the * "image" as a Base64 string. + * + * @private + * @param data - object containing the PNG data. + * @param callback - function with params: + * * data - the uncompressed data + */ +function decompressPng(data, callback) { + // Uncompresses the data before sending it through (use image/canvas to do so). + var image = new Image(); + // When the image loads, extracts the raw data (JSON message). + image.onload = function() { + // Creates a local canvas to draw on. + var canvas = new Canvas(); + var context = canvas.getContext('2d'); + + // Sets width and height. + canvas.width = image.width; + canvas.height = image.height; + + // Prevents anti-aliasing and loosing data + context.imageSmoothingEnabled = false; + context.webkitImageSmoothingEnabled = false; + context.mozImageSmoothingEnabled = false; + + // Puts the data into the image. + context.drawImage(image, 0, 0); + // Grabs the raw, uncompressed data. + var imageData = context.getImageData(0, 0, image.width, image.height).data; + + // Constructs the JSON. + var jsonData = ''; + for (var i = 0; i < imageData.length; i += 4) { + // RGB + jsonData += String.fromCharCode(imageData[i], imageData[i + 1], imageData[i + 2]); + } + callback(JSON.parse(jsonData)); + }; + // Sends the image data to load. + image.src = 'data:image/png;base64,' + data; +} + +module.exports = decompressPng; + +},{"canvas":43}],45:[function(require,module,exports){ +exports.DOMImplementation = window.DOMImplementation; +exports.XMLSerializer = window.XMLSerializer; +exports.DOMParser = window.DOMParser; + +},{}]},{},[6]); diff --git a/docs/assets/blocks/blockly.svg b/docs/assets/blocks/blockly.svg new file mode 100644 index 000000000..5e7e10d54 --- /dev/null +++ b/docs/assets/blocks/blockly.svg @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/blocks/blocks.png b/docs/assets/blocks/blocks.png new file mode 100644 index 000000000..e1ef0f38c Binary files /dev/null and b/docs/assets/blocks/blocks.png differ diff --git a/docs/assets/blocks/get-position.png b/docs/assets/blocks/get-position.png new file mode 100644 index 000000000..49a59fd04 Binary files /dev/null and b/docs/assets/blocks/get-position.png differ diff --git a/docs/assets/blocks/land.png b/docs/assets/blocks/land.png new file mode 100644 index 000000000..1838e617e Binary files /dev/null and b/docs/assets/blocks/land.png differ diff --git a/docs/assets/blocks/navigate.png b/docs/assets/blocks/navigate.png new file mode 100644 index 000000000..35c3a20bc Binary files /dev/null and b/docs/assets/blocks/navigate.png differ diff --git a/docs/assets/blocks/random-color.png b/docs/assets/blocks/random-color.png new file mode 100644 index 000000000..77a6720e0 Binary files /dev/null and b/docs/assets/blocks/random-color.png differ diff --git a/docs/assets/blocks/save.png b/docs/assets/blocks/save.png new file mode 100644 index 000000000..b33539831 Binary files /dev/null and b/docs/assets/blocks/save.png differ diff --git a/docs/assets/blocks/set-effect.png b/docs/assets/blocks/set-effect.png new file mode 100644 index 000000000..5823ddbc6 Binary files /dev/null and b/docs/assets/blocks/set-effect.png differ diff --git a/docs/assets/blocks/take-off.png b/docs/assets/blocks/take-off.png new file mode 100644 index 000000000..6fe9df90a Binary files /dev/null and b/docs/assets/blocks/take-off.png differ diff --git a/docs/assets/blocks/wait-arrival.png b/docs/assets/blocks/wait-arrival.png new file mode 100644 index 000000000..23e5ecec6 Binary files /dev/null and b/docs/assets/blocks/wait-arrival.png differ diff --git a/docs/assets/blocks/wait.png b/docs/assets/blocks/wait.png new file mode 100644 index 000000000..46f5a551b Binary files /dev/null and b/docs/assets/blocks/wait.png differ diff --git a/docs/assets/versions/clover_2.jpg b/docs/assets/versions/clover_2.jpg new file mode 100644 index 000000000..5f4343d01 Binary files /dev/null and b/docs/assets/versions/clover_2.jpg differ diff --git a/docs/assets/versions/clover_3.jpg b/docs/assets/versions/clover_3.jpg new file mode 100644 index 000000000..371718a53 Binary files /dev/null and b/docs/assets/versions/clover_3.jpg differ diff --git a/docs/assets/versions/clover_4.jpg b/docs/assets/versions/clover_4.jpg new file mode 100644 index 000000000..8f24d44e2 Binary files /dev/null and b/docs/assets/versions/clover_4.jpg differ diff --git a/docs/clover.css b/docs/clover.css index aea9e5162..fe2c8c48f 100644 --- a/docs/clover.css +++ b/docs/clover.css @@ -65,3 +65,15 @@ footer img { .type_table tr td { padding: 14px 20px; } + +table.versions td { + text-align: center; + background: white; + +.circle { + width: 0.8em; + height: 0.8em; + border-radius: 50%; + display: inline-block; + margin-right: 0.5em; +} diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md index 005ea7d5e..398512afe 100644 --- a/docs/en/SUMMARY.md +++ b/docs/en/SUMMARY.md @@ -1,26 +1,24 @@ # Summary * [Introduction](README.md) -* [Glossary](gloss.md) +* [Glossary](glossary.md) * [Safety tips](safety.md) -* Assembly +* [Assembly](assembly.md) * [Clover 4.2 assembly](assemble_4_2.md) * [Clover 4 assembly](assemble_4.md) * [Clover 3 assembly](assemble_3.md) * [Clover 2 assembly](assemble_2.md) -* Configuration - * [Initial setup](setup.md) +* [Configuration](setup.md) * [Sensor calibration](calibration.md) * [RC setup](radio.md) * [Using FS-A8S](rc_flysky_a8s.md) * [Flight modes](modes.md) * [Power setup](power.md) * [Failsafe configuration](failsafe.md) -* Manual flight +* [Manual flight](flight.md) * [Basics](flight.md) * [Exercises](flight_exercises.md) -* Working with Raspberry Pi - * [Raspberry Pi](raspberry.md) +* [Working with Raspberry Pi](raspberry.md) * [RPi Image](image.md) * [Wi-Fi connection](wifi.md) * [Connection to the Pixracer](connection.md) @@ -29,11 +27,9 @@ * [Command line interface](cli.md) * [Automated self-checks](selfcheck.md) * [Viewing images from cameras](web_video_server.md) -* Programming - * [Overview](programming.md) +* [Programming](programming.md) * [Camera setup](camera_setup.md) - * Fiducial markers (ArUco) - * [Overview](aruco.md) + * [Fiducial markers (ArUco)](aruco.md) * [Marker detection](aruco_marker.md) * [Map-based navigation](aruco_map.md) * [Optical Flow](optical_flow.md) @@ -48,14 +44,14 @@ * [Using rviz and rqt](rviz.md) * [Software autorun](autolaunch.md) * [Using JavaScript](javascript.md) - * [ROS](ros.md) - * [MAVROS](mavros.md) - * Simulation - * [Overview](simulation.md) + * [Blocks programming](blocks.md) + * [Simulation](simulation.md) * [Native setup](simulation_native.md) * [VM setup](simulation_vm.md) * [Usage](simulation_usage.md) -* Supplementary materials + * [ROS](ros.md) + * [MAVROS](mavros.md) +* [Supplementary materials](supplementary.md) * [COEX Pix](coex_pix.md) * [Guide on autonomous flight](auto_setup.md) * [Hostname](hostname.md) @@ -88,7 +84,7 @@ * [LED strip (legacy)](leds_old.md) * [Contribution Guidelines](contributing.md) * [Migration to v0.20](migrate20.md) -* Clover-based projects +* [Clover-based projects](projects.md) * [Drone show](clever-show.md) * [Innopolis Open 2020 (L22_ÆRO)](innopolis_open_L22_AERO.md) * [Copter spheric guard](shield.md) diff --git a/docs/en/assemble_4.md b/docs/en/assemble_4.md index f3813dca5..1c5d4d847 100644 --- a/docs/en/assemble_4.md +++ b/docs/en/assemble_4.md @@ -410,7 +410,7 @@ Check the quadrotor assembly: -Be sure to install and setup the voltage indicator before flying, so as not to overdischarge the battery. To configure the indicator, use the button located at its base. The displayed numbers during setup indicate the minimum possible voltage in each [cell](gloss.md#battery-cell) of the battery, the recommended value is **3.5**. +Be sure to install and setup the voltage indicator before flying, so as not to overdischarge the battery. To configure the indicator, use the button located at its base. The displayed numbers during setup indicate the minimum possible voltage in each [cell](glossary.md#battery-cell) of the battery, the recommended value is **3.5**. > **Info** Sound indication means that your battery is low and needs to be charged. diff --git a/docs/en/assembly.md b/docs/en/assembly.md new file mode 100644 index 000000000..4790cd287 --- /dev/null +++ b/docs/en/assembly.md @@ -0,0 +1,27 @@ +# Clover drone assembly + +This section contains articles describing the assembly of each version of Clover. + + + + + + + + + + + + + + + + + + + + + + + +
    VersionImage
    Clover 4.2
    Clover 4.1
    Clover 4
    Clover 3
    Clover 2
    diff --git a/docs/en/blocks.md b/docs/en/blocks.md new file mode 100644 index 000000000..070d6e8ac --- /dev/null +++ b/docs/en/blocks.md @@ -0,0 +1,110 @@ +# Blocks programming for Clover + + + +Visual blocks programming feature has been added to the [RPi image](image.md), starting with the version **0.21**. Blocks programming is implemented using [Google Blockly](https://developers.google.com/blockly) platform. Blocks programming integration can lower the entry barrier to a minimum. + +## Configuration + +For correct work of the blocks programming, `blocks` argument in the Clover launch-file (`~/catkin_ws/src/clover/clover/launch/clover.launch`) [should be equal to](cli.md#editing) `true`: + +```xml + +``` + +## Running + +To run Clover's blocks programming interface, [connect to Clover's Wi-Fi](wifi.md) and go to web-page http://192.168.11.1:8080/clover_blocks/ or click the link *Blocks programming* at the [main page](wifi.md#web-interface). + +The page looks as follows: + + + +Assemble your program using blocks in the menu at the left and then click *Run* button for running. You can also view generated Python-code, switching to *Python* tab. + +The *Stop* button stops the program. Clicking *Land* button also stops the program and lands the drone. + +## Storing and loading + + + +To store the program, open the menu at the top right, select *Save* item and input your program's name. The name should contain only Latin characters, hyphen, underline and dot characters. All your stored programs are available at the same menu. + +Your programs are stored as XML-files in the `/catkin_ws/src/clover/clover_blocks/programs/` directory of the SD-card. + +> **Note** Note also example programs, available at the same menu. + +## Blocks + +The set of blocks is somewhat similar to the set of ROS-services of [Clover's autonomous flights API](simple_offboard.md). This section contains descriptions of some of them. + +Clover's blocks are separated into 4 categories: + +* Flight – autonomous flight related commands. +* State – blocks for obtaining the drone state parameters. +* LED – blocks for controlling [LED strip](leds.md). +* GPIO – blocks for working with [GPIO pins](gpio.md). + +The rest of categories contains standard Blockly's blocks. + +### take_off + + + +Take off to specified altitude in meters. The altitude may be an arbitrary block, that returns a number. + +The `wait` flag specifies, if the drone should wait until take off is complete, before executing the next block. + +### navigate + + + +Navigate to specified point. Coordinates are specified in meters. + +The `wait` flag specifies, if the drone should wait until navigation is complete, before executing the next block. + +#### *Relative to* field {#relative_to} + +This block allows to specify the [coordinate frame](frames.md) of the target point: + +* *body* – coordinates, relative to the drone: *forward*, *left*, *up*. +* *markers map* – coordinates, relative to the [map of ArUco-markers](aruco_map.md). +* *marker* – coordinates, relative to an [ArUco-marker](aruco_marker.md); marker's ID input fields appears. +* *last navigate target* – coordinates, relative to the last specified navigate point. +* *map* – drone's local coordinate system, linked with the point of its initialization. + +### land + + + +Land the drone. + +The `wait` flag specifies, if the drone should wait until landing is complete, before executing the next block. + +### wait + + + +Wait specified time period in seconds. The time period may be an arbitrary block, that returns a number. + +### wait_arrival + + + +Wait, until the drone reaches [navigate](#navigate)-block's target point. + +### get_position + + + +The block returns current position, velocity or yaw angle of the drone relative to the specified [coordinate frame](#relative_to). + +### set_effect + + + +The block allows to set animations to LED strip, similarly to [`set_effect`](leds.md#set_effect) ROS-service. + +Example of using the block with a random color (colors-related blocks are located in *Colour* category): + + diff --git a/docs/en/book.json b/docs/en/book.json index 2de25d9bd..49f491da5 100644 --- a/docs/en/book.json +++ b/docs/en/book.json @@ -1,4 +1,5 @@ { "language": "en", - "root": "." + "root": ".", + "structure": {"glossary": "_GLOSSARY.md" } } diff --git a/docs/en/gloss.md b/docs/en/glossary.md similarity index 100% rename from docs/en/gloss.md rename to docs/en/glossary.md diff --git a/docs/en/leds.md b/docs/en/leds.md index 76a780fb6..4cecd3dfc 100644 --- a/docs/en/leds.md +++ b/docs/en/leds.md @@ -95,16 +95,26 @@ disconnected: { effect: blink, r: 255, g: 50, b: 50 } ``` -The left part is one of the possible events that the strip reacts to. The right part contains the effect description that you want to execute for this event. Here is the list of supported events: - -* `startup` – Clover system startup; -* `connected` – successful connection to the flight controller; -* `disconnected` – connection to the flight controller lost; -* `armed` – flight controller transitioned to armed state; -* `disarmed` – flight controller transitioned to disarmed state; -* `stabilized`, `acro`, `rattitude`, `altctl`, `posctl`, `offboard`, `mission`, `rtl`, `land` – transition to said flight mode; -* `error` – an error occured in one of ROS nodes or in the flight controller (*ERROR* message in `/rosout`); -* `low_battery` – low battery (threshold is set in the `threshold` parameter). +The left part is one of the possible events that the strip reacts to. The right part contains the effect description that you want to execute for this event. + +Here is the list of supported events: + + + + + + + + + + + + + + + + +
    EventDescriptionDefault effect
    startupClover system startupWhite
    connectedSuccessful connection to flight controllerRainbow
    disconnectedConnection to flight controller lost
    Red blink
    armedTransition to Armed state
    disarmedTransition to Disarmed state
    acroAcro mode
    Orange
    stabilizedStabilized mode
    Green
    altctlAltitude mode
    Yellow
    posctlPosition mode
    Blue
    offboardOffboard mode
    Violet
    rattitude, mission, rtl, landCorresponding mode
    errorError in one of ROS nodes or in the flight controller (ERROR message in /rosout)
    Red flash
    low_batteryLow battery (threshold is set in the threshold parameter)
    Red fast blink
    > **Note** You need to [calibrate the power sensor](power.md#calibrating-the-power-sensor) for the `low_battery` event to work properly. diff --git a/docs/en/projects.md b/docs/en/projects.md new file mode 100644 index 000000000..3044fb823 --- /dev/null +++ b/docs/en/projects.md @@ -0,0 +1,3 @@ +# Clover-based projects + +Clover drone kit is widely used in design activities. This section contains user articles describing the implemented projects. diff --git a/docs/en/supplementary.md b/docs/en/supplementary.md new file mode 100644 index 000000000..8f56608a4 --- /dev/null +++ b/docs/en/supplementary.md @@ -0,0 +1,5 @@ +# Supplementary materials + +This section contains articles that are not included in the main sections, as well as articles by the users on various subjects related to UAV. + +To learn more about publishing text in this section, see the article "[Contributing to Clover](contributing.md)". diff --git a/docs/ru/SUMMARY.md b/docs/ru/SUMMARY.md index 5ee46fa39..a52b81fd4 100644 --- a/docs/ru/SUMMARY.md +++ b/docs/ru/SUMMARY.md @@ -1,27 +1,24 @@ # Содержание * [Введение](README.md) -* [Глоссарий](gloss.md) +* [Глоссарий](glossary.md) * [Безопасность](safety.md) -* Сборка +* [Сборка](assembly.md) * [Клевер 4.2](assemble_4_2.md) * [Клевер 4.1](assemble_4_1.md) * [Клевер 4](assemble_4.md) * [Клевер 3](assemble_3.md) * [Клевер 2](assemble_2.md) -* Настройка - * [Первоначальная настройка](setup.md) +* [Настройка](setup.md) * [Калибровка датчиков](calibration.md) * [Настройка пульта](radio.md) * [Работа с FS-A8S](rc_flysky_a8s.md) * [Полетные режимы](modes.md) * [Настройка питания](power.md) * [Настройка failsafe](failsafe.md) -* Ручной полет - * [Основы](flight.md) +* [Ручной полет](flight.md) * [Упражнения](flight_exercises.md) -* Работа с Raspberry Pi - * [Raspberry Pi](raspberry.md) +* [Работа с Raspberry Pi](raspberry.md) * [Образ для RPi](image.md) * [Подключение по Wi-Fi](wifi.md) * [Подключение к Pixracer](connection.md) @@ -30,11 +27,9 @@ * [Командная строка](cli.md) * [Автоматическая проверка](selfcheck.md) * [Просмотр видеострима с камер](web_video_server.md) -* Программирование - * [Общая информация](programming.md) +* [Программирование](programming.md) * [Настройка камеры](camera_setup.md) - * Визуальные маркеры (ArUco) - * [Общая информация](aruco.md) + * [Визуальные маркеры (ArUco)](aruco.md) * [Распознавание маркеров](aruco_marker.md) * [Навигация по карте маркеров](aruco_map.md) * [Навигация по Optical Flow](optical_flow.md) @@ -46,23 +41,21 @@ * [Работа с GPIO](gpio.md) * [Ультразвуковой дальномер](sonar.md) * [Компьютерное зрение](camera.md) - * [Визуализация с помощью rviz](rviz.md) + * [Визуализация с помощью rviz и rqt](rviz.md) * [Автозапуск ПО](autolaunch.md) * [Использование JavaScript](javascript.md) - * [`mavros`](mavros.md) - * Симулятор - * [Общая информация](simulation.md) + * [Блочное программирование](blocks.md) + * [Симулятор](simulation.md) * [Сборка на собственной машине](simulation_native.md) * [Установка виртуальной машины](simulation_vm.md) * [Использование симулятора](simulation_usage.md) -* ROS: учебник - * [Общая информация](ros.md) +* [ROS: учебник](ros.md) * [Графические инструменты](ros_gui.md) * [Соглашения ROS](ros_conventions.md) * [Продвинутое использование](ros_advanced.md) * [ROS](ros.md) * [MAVROS](mavros.md) -* Дополнительные материалы +* [Дополнительные материалы](supplementary.md) * [COEX Pix](coex_pix.md) * [Гид по автономному полету](auto_setup.md) * [Имя хоста](hostname.md) @@ -99,14 +92,14 @@ * [Светодиодная лента (legacy)](leds_old.md) * [Вклад в Клевер](contributing.md) * [Переход на версию 0.20](migrate20.md) -* Мероприятия +* [Мероприятия](events.md) + * [CopterHack-2021](copterhack2021.md) * [CopterHack-2019](copterhack2019.md) * [Олимпиада НТИ 2019](nti2019.md) * [Робокросс-2019](robocross2019.md) * [CopterHack-2018](copterhack2018.md) * [CopterHack-2017](copterhack2017.md) -* Проекты на базе Клевера - * [Список проектов](projects.md) +* [Проекты на базе Клевера](projects.md) * [Шоу коптеров](clever-show.md) * [Innopolis Open 2020 (L22_ÆRO)](innopolis_open_L22_AERO.md) * [Олимпиада НТИ 2020 (P4DF2)](nti2020_p4df2.md) @@ -126,4 +119,4 @@ * [Теория и видеоуроки](lessons.md) * [Учебно-методическое пособие](metod.md) -* [Контрольные и проверочные материалы](tests.md) +* [Контрольные материалы](tests.md) diff --git a/docs/ru/assemble_4.md b/docs/ru/assemble_4.md index a40606cc3..4e099d76c 100644 --- a/docs/ru/assemble_4.md +++ b/docs/ru/assemble_4.md @@ -413,7 +413,7 @@ -Обязательно установите и настройте индикатор напряжения перед полетом, чтобы не переразрядить аккумулятор. Для настройки индикатора используйте конпку расположенную в его основании. Отображаемые цифры во время настройки обозначают минимально возможное напряжение в каждой [ячейке](gloss.md#ячейка--банка-акб) аккумулятора, рекомендуемое значение **3.5**. +Обязательно установите и настройте индикатор напряжения перед полетом, чтобы не переразрядить аккумулятор. Для настройки индикатора используйте конпку расположенную в его основании. Отображаемые цифры во время настройки обозначают минимально возможное напряжение в каждой [ячейке](glossary.md#ячейка--банка-акб) аккумулятора, рекомендуемое значение **3.5**. > **Info** Звуковая индикация означает, что ваш аккумулятор разряжен и его нужно зарядить. diff --git a/docs/ru/assemble_4_1.md b/docs/ru/assemble_4_1.md index 30cba5110..74403e90c 100644 --- a/docs/ru/assemble_4_1.md +++ b/docs/ru/assemble_4_1.md @@ -234,7 +234,7 @@ -Обязательно установите и настройте индикатор напряжения перед полетом, чтобы не переразрядить аккумулятор. Для настройки индикатора используйте кнопку, расположенную в его основании. Отображаемые цифры во время настройки обозначают минимально возможное напряжение в каждой [ячейке](gloss.md#ячейка--банка-акб) аккумулятора, рекомендуемое значение **3.5**. +Обязательно установите и настройте индикатор напряжения перед полетом, чтобы не переразрядить аккумулятор. Для настройки индикатора используйте кнопку, расположенную в его основании. Отображаемые цифры во время настройки обозначают минимально возможное напряжение в каждой [ячейке](glossary.md#ячейка--банка-акб) аккумулятора, рекомендуемое значение **3.5**. > **Info** Звуковая индикация означает, что ваш аккумулятор разряжен и его нужно зарядить. diff --git a/docs/ru/assemble_4_2.md b/docs/ru/assemble_4_2.md index 0da703710..76c4166b8 100644 --- a/docs/ru/assemble_4_2.md +++ b/docs/ru/assemble_4_2.md @@ -174,6 +174,12 @@ 5. На деку захвата установите дальномер с помощью самоконтрящихся гаек и винтов М3х8, и приклейте радиоприемник с помощью двустороннего скотча. + > **Сaution** Обратите внимание, что крепящие гайки расположены обратной стороны от платы дальномера, как на схеме, иначе есть большая вероятность повредить плату. + + + + > **Hint** Также плата может быть закреплена на саморезы М2 в соседние отверстия. + 6. Установите 4 стойки 20мм и закрепите их с помощью винтов М3х8. diff --git a/docs/ru/assembly.md b/docs/ru/assembly.md new file mode 100644 index 000000000..69f9d0ce1 --- /dev/null +++ b/docs/ru/assembly.md @@ -0,0 +1,27 @@ +# Сборка Клевера + +В этом разделе находится статьи, описывающие сборку каждой из версии Клевера. + + + + + + + + + + + + + + + + + + + + + + + +
    ВерсияИзображение
    Клевер 4.2
    Клевер 4.1
    Клевер 4
    Клевер 3
    Клевер 2
    diff --git a/docs/ru/blocks.md b/docs/ru/blocks.md new file mode 100644 index 000000000..121ebc4e2 --- /dev/null +++ b/docs/ru/blocks.md @@ -0,0 +1,110 @@ +# Блочное программирование Клевера + + + +Возможность блочного визуального программирования автономных полетов Клевера добавлена в [образ для RPi](image.md), начиная с версии **0.21**. Реализация блочного программирования основана на [Google Blockly](https://developers.google.com/blockly). Интеграция Blockly в Клевер позволяет понизить входной порог в программирование автономных полетов до минимального уровня. + +## Конфигурация + +Для корректной работы работы блочного программирования аргумент `blocks` в launch-файле Клевера (`~/catkin_ws/src/clover/clover/launch/clover.launch`) [должен быть в значении](cli.md#editing) `true`: + +```xml + +``` + +## Запуск + +Для того, чтобы открыть интерфейс блочного программирования в Клевере, [подключитесь к Клеверу по Wi-Fi](wifi.md) и перейдите на страницу http://192.168.11.1:8080/clover_blocks/ либо нажмите ссылку *Blocks programming* на [основной веб-странице Клевера](wifi.md#веб-интерфейс). + +Интерфейс выглядит следующим образом: + + + +Соберите необходимую программу из блоков в меню слева а затем нажмите кнопку *Run* для ее запуска. Также вы можете просмотреть сгенерированный код на языке Python, переключившись во вкладку *Python*. + +Кнопка *Stop* позволяет остановить программу. Нажатие кнопки *Land* также останавливает программу и сажает дрон. + +## Сохранение и загрузка + + + +Для сохранения программы откройте меню справа сверху, выберите пункт меню *Save* и введите название программы. Название программы может содержать только латинские буквы, дефис, подчеркивание и точку. Все ранее сохраненные программы будут доступны в этом же меню. + +На карте памяти сохраненные XML-файлы программ хранятся в каталоге `/catkin_ws/src/clover/clover_blocks/programs/`. + +В этом же меню доступны примеры программ (подкаталог `examples`). + +## Блоки + +Набор блоков приблизительно аналогичен набору ROS-сервисов [API автономных полетов Клевера](simple_offboard.md). В этом разделе приведено описание некоторых из них. + +Блоки Клевера поделены на 4 категории: + +* Flight – команды, имеющие отношение к полету. +* State – блоки, позволяющие получить те или иные параметры текущего состояния коптера. +* LED – блоки для управления [LED-лентой](leds.md). +* GPIO – блоки для работы с [GPIO-пинами](gpio.md). + +В остальных категориях находятся стандартные блоки Google Blockly. + +### take_off + + + +Взлететь на указанную высоту в метрах. Высота может быть произвольным блоком, возвращающим числовое значение. + +Флаг `wait` определяет, должен ли дрон ожидать окончания взлета перед выполнением следующего блока. + +### navigate + + + +Прилететь в заданную точку. Координаты точки задаются в метрах. + +Флаг `wait` определяет, должен ли дрон ожидать завершения полета в точку перед выполнением следующего блока. + +#### Поле *relative to* {#relative_to} + +В блоке может быть выбрана [система координат](frames.md), в которой задана целевая точка: + +* *body* – координаты относительно коптера: вперед (*forward*), влево (*left*), вверх (*up*). +* *markers map* – система координат, связанная с [картой ArUco-маркеров](aruco_map.md). +* *marker* – система координта, связанная с [ArUco-маркером](aruco_marker.md); появляется поле для ввода ID маркеа. +* *last navigate target* – координаты относительно последней заданной точки для навигации. +* *map* – локальная система координат коптера, связана с местом его инициализации. + +### land + + + +Произвести посадку. + +Флаг `wait` определяет, должен ли дрон ожидать окончания посадки перед выполнением следующего блока. + +### wait + + + +Ожидать заданное время в секундах. Время ожидания может быть произвольным блоком, возвращающим числовое значение. + +### wait_arrival + + + +Ожидать, пока дрон долетит до целевой точки (заданной в [navigate](#navigate)-блоке). + +### get_position + + + +Блок позволяет получить позицию, скорость и угол по рысканью дрона в заданной [системе координат](#relative_to). + +### set_effect + + + +Блок позволяет устанавливать различные анимации на LED-ленту аналогично [ROS-сервису `set_effect`](leds.md#set_effect). + +Пример использования блока для установки случайного цвета (блоки, связанные с цветами находятся в категории *Colour*): + + diff --git a/docs/ru/book.json b/docs/ru/book.json index 566fc6810..7a9c8206f 100644 --- a/docs/ru/book.json +++ b/docs/ru/book.json @@ -1,4 +1,5 @@ { "language": "ru", - "root": "." + "root": ".", + "structure": {"glossary": "_GLOSSARY.md" } } diff --git a/docs/ru/clever_blocks.md b/docs/ru/clever_blocks.md index 942eeeed7..ab9da906d 100644 --- a/docs/ru/clever_blocks.md +++ b/docs/ru/clever_blocks.md @@ -1,5 +1,7 @@ # Блочное программирование Клевера +> **Warning** В этой статье описана неофициальная реализация блочного программирования. [Официальная поддержка](blocks.md) появилась в образе Клевера версии **0.21**. + В этой статье я опишу процесс скачивания, установки и запуска блочного конструктора программ для квадрокоптера Клевер. diff --git a/docs/ru/copterhack2021.md b/docs/ru/copterhack2021.md new file mode 100644 index 000000000..2cd1a41b2 --- /dev/null +++ b/docs/ru/copterhack2021.md @@ -0,0 +1,5 @@ +# CopterHack 2021 + +CopterHack 2021 – это командный конкурс по разработке проектов с открытым исходным кодом для платформы квадрокоптера "Клевер". + +Все информацию о мероприятии смотрите на официальном сайте: https://ru.coex.tech/copterhack. diff --git a/docs/ru/events.md b/docs/ru/events.md new file mode 100644 index 000000000..15134d331 --- /dev/null +++ b/docs/ru/events.md @@ -0,0 +1,5 @@ +# Мероприятия + +"Клевер" применяется в в большом количестве образовательных мероприятий и соревнований: WorldSkills, Олимпиада НТИ, Copter Hack, Всероссийская Робототехническая Олимпиада и других. + +Этот раздел содержит статьи, написанные специально к тому или иному мероприятию. diff --git a/docs/ru/gloss.md b/docs/ru/glossary.md similarity index 100% rename from docs/ru/gloss.md rename to docs/ru/glossary.md diff --git a/docs/ru/gpio.md b/docs/ru/gpio.md index 43cbb8aac..1d6ca5611 100644 --- a/docs/ru/gpio.md +++ b/docs/ru/gpio.md @@ -4,6 +4,10 @@ GPIO (General-Purpose Input/Output) – это тип пинов на Raspberry > **Info** Используйте [распиновку](https://pinout.xyz), чтобы понять, какие из пинов на Raspberry Pi поддерживают GPIO и ШИМ. + + +> **Info** Для того, чтобы не создавалось конфликтов при использовании портов *GPIO* в образе закрыт доступ для портов 0, 1, 2, 3, 14, 15, на которые выведены интерфейсы подключения I2C и UART. + Для работы с GPIO на [образе для RPi](image.md) предустановлена библиотека [`pigpio`](http://abyz.me.uk/rpi/pigpio/). Чтобы взаимодействовать с этой библиотекой, запустите соответствующий демон: ```bash @@ -77,7 +81,7 @@ pi.set_servo_pulsewidth(13, 2000) ![GPIO Mosfet Magnet Connection](../assets/gpio_mosfet_magnet.png) -Для подключения электромагнита используйте полевой транзистор (MOSFET). Подключите транзистор к одному из GPIO-пинов Raspberry Pi. Для управления магнитом, подключенным к 15 пину, используйте такой код: +Для подключения электромагнита используйте полевой транзистор (MOSFET). Подключите транзистор к одному из GPIO-пинов Raspberry Pi. Для управления магнитом, подключенным к 18 пину, используйте такой код: ```python import time @@ -85,14 +89,14 @@ import pigpio pi = pigpio.pi() -# устанавливаем режим 15 пина на вывод -pi.set_mode(15, pigpio.OUTPUT) +# устанавливаем режим 18 пина на вывод +pi.set_mode(18, pigpio.OUTPUT) # включаем электромагнит -pi.write(15, 1) +pi.write(18, 1) time.sleep(2) # отключаем электромагнит -pi.write(15, 0) +pi.write(18, 0) ``` diff --git a/docs/ru/leds.md b/docs/ru/leds.md index 63215898b..ab353c3f4 100644 --- a/docs/ru/leds.md +++ b/docs/ru/leds.md @@ -14,7 +14,7 @@ > **Caution** Обратите внимание, что светодиодную ленту нужно питать от стабильного источника энергии. Если вы подключите питание напрямую к Raspberry, то это создаст слишком большую нагрузку на ваш микрокомпьютер. Для снятия нагрузки с Raspberry можно подключить питание к преобразователю BEC. -## Высокоуровневое управление лентой +## Высокоуровневое управление лентой {#set_effect} 1. Для работы с лентой подключите ее к питанию +5v – 5v, земле GND – GND и сигнальному порту DIN – GPIO21. Обратитесь [к инструкции по сборке](assemble_4_2.md#установка-led-ленты) для подробностей. 2. Включите поддержку LED-ленты в файле `~/catkin_ws/src/clever/clever/launch/clever.launch`: @@ -95,16 +95,26 @@ disconnected: { effect: blink, r: 255, g: 50, b: 50 } ``` -В левой части таблицы указывается событие, на которая лента должна среагировать. В правой части указывается эффект (анимация), который необходимо включить при возникновении события. Список поддерживаемых событий: - -* `startup` – запуск всех систем Клевера; -* `connected` – успешное подключение к полетному контроллеру; -* `disconnected` – разрыв связи с полетным контроллером; -* `armed` – переключение полетного контроллера в состояние Armed; -* `disarmed` – переключение полетного контроллера в состояние Disarmed; -* `stabilized`, `acro`, `rattitude`, `altctl`, `posctl`, `offboard`, `mission`, `rtl`, `land` – переключение полетных режимов; -* `error` – возникновение ошибки в ROS-нодах или полетном контроллере (*ERROR*-сообщение в топике `/rosout`); -* `low_battery` – низкий заряд батареи (порог настраивается в параметре `threshold`). +В левой части таблицы указывается событие, на которая лента должна среагировать. В правой части указывается эффект (анимация), который необходимо включить при возникновении события. + +Список поддерживаемых событий: + + + + + + + + + + + + + + + + +
    СобытиеОписаниеЭффект по умолчанию
    startupЗапуск всех систем КлевераБелый
    connectedУспешное подключение к полетному контроллеруЭффект радуги
    disconnectedРазрыв связи с полетным контроллером
    Мигание красным
    armedПереход в состояние Armed
    disarmedПереход в состояние Disarmed
    acroРежим Acro
    Оранжевый
    stabilizedРежим Stabilized
    Зеленый
    altctlРежим Altitude
    Желтый
    posctlРежим Position
    Синий
    offboardРежим Offboard
    Фиолетовый
    rattitude, mission, rtl, landПереход в соответствующие режимы
    errorВозникновение ошибки в ROS-нодах или полетном контроллере (ERROR-сообщение в топике /rosout)
    Мигнуть красным
    low_batteryНизкий заряд батареи (порог настраивается в параметре threshold)
    Быстрое мигание красным
    > **Note** Для корректной работы сигнализации LED-лентой о низком заряде батареи необходимо корректная [калибровка электропитания](power.md#Калибровка-делителя-напряжения). diff --git a/docs/ru/projects.md b/docs/ru/projects.md index b378a62db..58e08f264 100644 --- a/docs/ru/projects.md +++ b/docs/ru/projects.md @@ -1,5 +1,9 @@ # Проекты на базе Клевера +Конструктор "Клевер" широко используется в для проектной деятельности. В этом разделе приведены проектные идеи, а также статьи пользователей, рассказывающие о реализованных проектах. + +## Примеры + Список возможных проектов для стажировок и практических работ в вузах и колледжах. |Проект/Идея|Примечания| diff --git a/docs/ru/supplementary.md b/docs/ru/supplementary.md new file mode 100644 index 000000000..52c8e5969 --- /dev/null +++ b/docs/ru/supplementary.md @@ -0,0 +1,5 @@ +# Дополнительные материалы + +Этот раздел содержит статьи, не вошедшие в основные разделы, а так же статьи пользователей по той или иной проблематике, связанной с БПЛА. + +Чтобы узнать больше о публикации текста в этом разделе, смотрите статью "[Вклад в Клевер](contributing.md)". diff --git a/redirects.json b/redirects.json index 8ed31b27c..cc6db68bc 100644 --- a/redirects.json +++ b/redirects.json @@ -2,7 +2,9 @@ "redirects": [ { "from": "assemble.html", "to": "ru/assemble_2.html" }, { "from": "assemble_clever3_4in1.html", "to": "ru/assemble_3.html" }, - { "from": "glossary.html", "to": "ru/gloss.html" }, + { "from": "glossary.html", "to": "ru/glossary.html" }, + { "from": "ru/gloss.html", "to": "glossary.html" }, + { "from": "en/gloss.html", "to": "glossary.html" }, { "from": "cl3_connectESC4in1.html", "to": "ru/4in1.html" }, { "from": "uart.html", "to": "ru/uart.html" }, { "from": "fpv.html", "to": "ru/fpv.html" },