mirror of
https://github.com/google/blockly.git
synced 2026-06-16 16:15:14 +02:00
Develop to master (#1209)
* Adding new minimap demo * Basic code style changes. Adding a few more comments. Return early if disableScrollChange in onScrollChange listener. * Adding horizontal scrolling. Changed scroll change callbacks from onScroll_ to setHandlePosition. onScroll_ is not challed when workspace is dragged. * Registering mousemove and mouseup listener in mousedown event. Mousemove and Mouseup events are now listening over document. * Adding the remove variable modal and functionality to accessible Blockly. (#1011) * Minimap position bug fix for browsers other than chrome. Added touch support. * Adding an add variable modal to accessible Blockly. (#1015) * Adding the remove variable modal and functionality to accessible Blockly. * Adding the add variable modal for accessible Blockly. * Block browser context menu in the toolbox and flyout * Add links to the dev registration form and contributor guidelines * Miscellaneous comment cleanup * Adding the common modal class. (#1017) Centralizes accessible modal behavior. * - Changed error message referencing 'procedure' instead of 'function' (#1019) - Added iOS specific UI messages - Fixed bug with js_to_json.py script where it didn't recognize ' character * - Allows use of Blockly's messaging format for category name, colour,… (#1028) ...in toolbox XML. - Updated code editor demo to use this message format - Re-built blockly_compressed.js * Making text_count use a text color (like text_length, which also returns a number). (#1027) * Enable google/blockly with continuous build on travis ci (#1023) (#1035) * create .travis for ci job * initial checkin for blocky-web travis ci job * rename file to .travis.yaml for typo * remove after_script * added cache * rename .travis.yaml to .travis.yml * Update .travis.yml * include build script * fix yaml file format issue * debug install part * debug build issue * Update .travis.yml * remove cache for now * Update .travis.yml * Update .travis.yml * Update .travis.yml * more debug info * Update .travis.yml * Update .travis.yml * fix typo * installing chrome browser * remove chrome setting config * run build.py as part of npm install * Update .travis.yml * update karma dependency * use karma as test runner * fix typo * remove karma test for now * Update .travis.yml * Update package.json * add npm test target * add browserstack-runner depdendency * update browser support * fix typo for test target * fix chrome typo * added closure dependency * add google-closure-library * include blockly_uncompressed.js and core.js dependency * uncomment out core/*.js files * add kama job as part of install * remove browserstack add on for now * fix karma config typo * add karma-closure * add os support * remove typo config * include more closure files * change os back to linux * use closure-library from node_modules * change log level back to INFO * change npm test target to use open browser command instead of karma * change travis test target to use open command instead of karma * list current directory * find what's in current dir * typo command * Update .travis.yml * typo again * open right index.html * use right path for index.html * xdg-open to open default browser on travis * exit browser after 5s wait * change timeout to 1 min * exit after opening up browser * use browser only * use karma * remove un-needed dependency * clean up script section * fix typo * update build status on readme * initial commit for selenium integration tests * update selenium jar path * fix test_runner.js typo * add more debug info * check java version * add && instead of 9288 * fix java path * add logic to check if selenium is running or not * add some deugging info * initial commit to get chromedriver * add chromedriver flag * add get_chromedriver.sh to package.json and .travel * change browser to chrome for now * fix path issue * update chromdriver path * fix path issue again * more debugging * add debug msg * fix typo * minor fix for getting chromedriver * install latest chrome browser * clean up pakcage.json * use npm target for test run * remove removing trailing comma * fix another trailing comma * updated travis test target * clean up scripts * not sure nmp run preinstall * redirect selenium log to tmp file * revert writing console log to file * update test summary * more clean up * minor clean up before pull request * resolved closure-library conflict 1. add closure-library to dependencies instead of devDependencies. 2. add lint back in scripts block * fix typo (adding comma) in script section * Renames Blockly.workspaceDragSurface to Blockly.WorkspaceDragSurface. Fixes #880. * Ensure useDragSurface is a boolean. Fixed #988 * use pretest instead of preinstall in package.json (#1043) * cherry pick for pretest fix * put pretest target to test_setup.sh * fix conflict * cherry pick for get_chromedriver.sh * add some sleep to wait download to finish * use node.js stable * use npm test target * field_angle renders degree symbol consistently. Fixes #973 * bumpNeighbours_ function moved to block_svg. Fixed #1009 * Update RegEx in js-to-json to match windowi eol (#1050) The current regex only works with the "\n" line endings as it expects no characters after the optional ";" at the end of the line. In windows, if it adds the "\r" it counts as a characters and is not part of the line terminator so it doesn't match. * Fix French translation of "colour with rgb" block (#1053) "colorier", which is currently used, is a verb and proposed "couleur" is a noun: the block in question does not change colour of anything, it creates new colour instead, thus noun is more applicable. Also, noun is used in French translation of "random colour" block: "couleur aléatoire". * Enforcing non-empty names on value inputs and statement inputs. (#1054) * Correcting #1054 (#1056) single quotes. better logic. * Created a variable model with name, id, and type. Created a jsunit test file for variable model. * Change how blockly handles cursors. The old way was quite slow becau… (#1057) * Change how blockly handles cursors. The old way was quite slow because it changed the stylesheet directly. See issue #981 for more details on implementation and tradeoffs. This changes makes the following high level changes: deprecate Blockly.Css.setCursor, use built in open and closed hand cursor instead of custom .cur files, add css to draggable objects to set the open and closed hand cursors. * Rebuild blockly_uncompressed to pick up a testing change to make travis happy. Fix a build warning from a multi-line string in the process. (#1059) * Merge master into develop (#1063) - pick up translation changes - clean up trailing spaces * use goog.string.startswith instead of string.startswith (#1065) * New jsinterpreter demo includes wait block. Both demos have improved UI for clarity. (#1001) Refactor of interpreter demo * Renamed demos/interpreter/index.html as demos/interpreter/step-execution.html (including redirect), and added demos/interpreter/async-execution.html. * Refactored code to automatically generate/parse the blocks, eliminating the need for a "Parse JavaScript" button. Code is still shown in alert upon stepping to the first statement. Print statements now write to output <textarea> instead of modal dialogs. * VariableMap and functions added. * Create separate file for VariableMap and its functions. * Fix #1069 (#1073) * VariableMap and functions added. * Fix #1051 (#1084) * Improve errors when validating JSON block definitions. (#1086) goog.asserts to not run from blockly_compressed.js. User data validation should always run. * Dragging changes, rebased on develop (#1078) * Add block drag surface translateSurfaceBy * Add dragged connection manager * Add gesture.js * Add GestureHandler * Implemented gesture skeleton * Most basic workspace dragging * Add dragged connection manager * cleanup * doc * more cleanup * Add gesture handler * Add translateSurfaceInternal * core/block_dragger.js * cleanup * Pull in changes to dragged connection manager * Pull in changes to dragged connection manager * comments * more annotations * Add workspace dragger * Add coordinate annotations * Start on block dragging * Limit number of concurrent gestures * Add some TODOs * start using dragged connection manager * Set origin correctly for dragging blocks * Connect or delete at the end of a block drag. * cleanup * handle field clicks and block + workspace right-clicks * move code into BlockDragger class, but still reach into Gesture internals a lot * Clean up block dragger * Call blockDragger constructor with correct arguments * Enable block dragging in a mutator workspace * Add workspace dragger * click todos * Drag flyout with background * more dragging from flyout * nit * fix dragging from flyouts * Remove unused code and rename gestureHandler to gestureDB * Rename gesture handler * Added some jsdoc in gesture.js * Update some docs * Move some code to block_svg and clean up code * Lots of coordinate annotations * Fix block dragging when zoomed. * Remove built files from branch * More dragging work (#1026) -- Drag bubbles while dragging blocks -- Use bindEventWithChecks to work in touch on Android. Not tested anywhere else yet. -- Handle dragging blocks while zoomed -- Handle dragging blocks in mutators -- Handle right-clicks (I hope) -- Removed lots of unused code * More dragging work (#1048) - Removed gestureDB - Removing uses of terminateDrag - Cleaned up disposal code * Dragging bugfixes (#1058) - Get rid of flyout.dragMode_ and blockly.dragMode_ - Make drags from the flyout start from the top block in the group - Block tooltips from being scheduled or shown during gestures - Don't resize mutator bubbles mid-drag * Fix events in new dragging (#1060) * rebuild for testing * unbuild * Fix events * rebuild * Fix up cursors * Use language files from develop * Remove handled TODOS * attempt to fix IE rerendering bug, and recalculate workspace positions on scroll * Rebuild all the things * Comment cleanup; annotations; delete unused variables. * Tidy up context menu code. (#1081) * add osx travis test run job (#1074) * Names are correctly fetched from VariableModels! * add more wait time for test setup (#1091) * Work around timing issue with travis osx issue (#1092) * add more wait time for test setup * increase selenium wait time * add more wait * Fix #1077 by adding a rule to cover the toolbox labels too. (#1099) * Assign variable UUID to field_variable dropdown. * Change registration link to a static one (#1106) This lets us redirect to a different form if we change it in the future. * Edit generators to read in Variable Models. * Add VariableMap requirement to workspace. * Changed parameter name in workspace for clarity. * Add type, id, and info to the generated xml. Add xml tests for fieldToDom. Update workspace tests to pass with new changes. * Fix apostrophe in tooltips and helpurls (#1111) * Click events on shadow blocks have the correct id (#1089) * Add image_onclick option (#1080) * Cleanup: semicolons, spacing, etc. (#1116) * Spelling. Spelling is hard. * Add variable info to xml generated in variables.js * Add missing CLAs info to the contributing file (#1119) * Add missing CLAs info to the contributing file * Added larger changes paragraph * Replacing latest prettifier hosted in repo with latest version at rawgit CDN. (#1120) * Forgot update code demo (#1121) * Move audio code to a new file (#1122) * move audio code to a new file * dispose * null check * Make flyout get variables from target workspace's variableMap. * Require VariableModel in field_variable.js. * Update contributing.md (#1126) * Include variables at top of serialization. * Move blockSvg.getHeightWidth to block_render_svg.js (#1118) * Deserialization variables at top. * Create grid object (#1131) * Create grid object * Doc * Units! Thanks @RoboErikG * Appease eslint: semicolons and such. * Deserialization variables at top. * Setting up accessible Blockly for Closure compilation. (#1134) Moving closure compilation into the build file. Fixing up goog.requires for accessible blockly. Adding accessible to the build script. * Fix #1109 * Adding command-line options to the buildscript. (#1136) * Fix for travis-ci testing failure (#1141) It looks like the default configuration for Travis might have changed. Adding a manual step to install webdriverio. * Convert text_join block to JSON + mutator format (#1140) * Clean up and create test utilities file. * Add BLOCK events and constructor tests. * Convert more text blocks to json (#1147) * Convert more text blocks to json Converts the mutator blocks for text_join and the text_append block to JSON format. * Fixing modals so they're announced on focus, and changing variables t… (#1030) * Fixing modals so they're announced on focus, and changing variables to only react to enter, not onChange. * Adding a temp index. * Whoops - added it in the wrong spot. * Adding automatically-generated variable lists to the accessible toolbox. (#1149) Fixing a bug with the core-only uncompressed file not finding its own directory. * Field Variable setValue() looks up variable. * Wrap Error in Try Finally Block. * Changing the build file to allow forced rebuilding of the msg files. (#1158) * Split flyout into flyout_base, flyout_horizontal, and flyou_vertical * Rename flyout to flyout_base * flyout_base minus horizontal and vertical code * Add flyout_vertical and flyout_horizontal * review fixes + toolbox and workspace use * Fix hat block case * rebuild uncompressed * Fix travis problem * Fix build problem * Add VAR events. * Correctly named block events called. * Fire VAR events and test. * Create utility function for checking variable values. * In DropdownCreate check for Msg.DELETE_VARIABLE. * Test Delete Variable Twice. * Convert more text blocks to JSON format (#1163) * Convert more text blocks to JSON format Converts text_charAt, text_indexOf, text_isEmpty, and text_length to JSON. Includes a rebuild to pick up message changes. * Fixing the accessible variable stuff so it interacts correctly with (#1170) variableMap. * createVariable in workspaceSvg takes in id and type. * Followup RefreshToolbox. * Fixing an error with block messages and ordering. (#1171) * Remove out of date todo comments. * Fix checkbox delete bug. * Fixing variable dropdowns so they select the correct option. (#1184) * Always open flyout fields are editable. * Fixing the tree service so it doesn't treat unknown block deletion (#1182) as an error, and turning off keypresses on the workspace when the variable modals are open. * Revamping mostly gesture tests. * Merge master to develop (#1189) * Merge master into develop (#1063) - pick up translation changes - clean up trailing spaces * Rebuild for translations * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Cherrypick a fix for #1069 and rebuild (#1075) * Fix #1069 (#1073) * rebuild * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Localisation updates from https://translatewiki.net. * Rebuild compressed files * Add some translatewiki annotations back into msg/messages.js * Rebuild msg files * Fix flyout dropdown bug. * Fix python and php procedures. * Dropdown Create does not create a missing variable twice. * Fire field variable change event with variable value, not name. * Fix #1160 (#1197) Fix "Connection UI Effect not playing on block connect" * Only add a block in the flyout if it is not disabled (#1204) * Only add a block in the flyout if it is not disabled * PR feedback * remove previous fix * Fix issue with compression stripping dropdown options (#1207) * Get some accessible files back from develop * rebuild
This commit is contained in:
+18
-15
@@ -1,27 +1,30 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "stable"
|
||||
sudo: required
|
||||
dist: trusty
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- google-chrome
|
||||
packages:
|
||||
- google-chrome-stable
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
node_js: stable
|
||||
sudo: required
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- google-chrome-stable
|
||||
- os: osx
|
||||
node_js: stable
|
||||
osx_image: xcode8.3
|
||||
|
||||
before_install:
|
||||
- npm install google-closure-library
|
||||
- npm install webdriverio
|
||||
# Symlink closure library
|
||||
- ln -s $(npm root)/google-closure-library ../closure-library
|
||||
|
||||
before_script:
|
||||
- export CHROME_BIN=/usr/bin/google-chrome
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- if [ "${TRAVIS_OS_NAME}" == "linux" ]; then ( scripts/setup_linux_env.sh ) fi
|
||||
- if [ "${TRAVIS_OS_NAME}" == "osx" ]; then ( scripts/setup_osx_env.sh ) fi
|
||||
- sleep 2
|
||||
|
||||
script:
|
||||
- set -x
|
||||
- npm test
|
||||
|
||||
os:
|
||||
- linux
|
||||
|
||||
+37
-2
@@ -1,5 +1,40 @@
|
||||
# Contributing to Blockly
|
||||
|
||||
Please make pull requests against develop, not master. If your patch needs to go into master immediately, include a note in your PR.
|
||||
Want to contribute? Great!
|
||||
- First, read this page (including the small print at the end).
|
||||
- Second, please make pull requests against develop, not master. If your patch
|
||||
needs to go into master immediately, include a note in your PR.
|
||||
|
||||
For more information, head over to the [Blockly Developers site](https://developers.google.com/blockly/guides/modify/contributing).
|
||||
For more information on style guide and other details, head over to the [Blockly Developers site](https://developers.google.com/blockly/guides/modify/contributing).
|
||||
|
||||
### Before you contribute
|
||||
Before we can use your code, you must sign the
|
||||
[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual)
|
||||
(CLA), which you can do online. The CLA is necessary mainly because you own the
|
||||
copyright to your changes, even after your contribution becomes part of our
|
||||
codebase, so we need your permission to use and distribute your code. We also
|
||||
need to be sure of various other things—for instance that you'll tell us if you
|
||||
know that your code infringes on other people's patents. You don't have to sign
|
||||
the CLA until after you've submitted your code for review and a member has
|
||||
approved it, but you must do it before we can put your code into our codebase.
|
||||
|
||||
### Larger changes
|
||||
Before you start working on a larger contribution, you should get in touch with
|
||||
us first through the issue tracker with your idea so that we can help out and
|
||||
possibly guide you. Coordinating up front makes it much easier to avoid
|
||||
frustration later on.
|
||||
|
||||
### Code reviews
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use Github pull requests for this purpose.
|
||||
|
||||
### Browser compatibility
|
||||
We care strongly about making Blockly work on all browsers. As of 2017 we
|
||||
support IE 10 and 11, Edge, Chrome, Safari, and Firefox. We will not accept
|
||||
changes that only work on a subset of those browsers. You can check [caniuse.com](https://caniuse.com/)
|
||||
for compatibility information.
|
||||
|
||||
### The small print
|
||||
Contributions made by corporations are covered by a different agreement than
|
||||
the one above, the
|
||||
[Software Grant and Corporate Contributor License Agreement](https://cla.developers.google.com/about/google-corporate).
|
||||
|
||||
@@ -11,7 +11,7 @@ blocks together to build programs. All code is free and open source.
|
||||
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.
|
||||
|
||||
Help us focus our development efforts by telling us [what you are doing with
|
||||
Blockly](https://goo.gl/forms/kZTsO9wGLmpoPXC02). The questionnaire only takes
|
||||
Blockly](https://developers.google.com/blockly/registration). The questionnaire only takes
|
||||
a few minutes and will help us better support the Blockly community.
|
||||
|
||||
Want to contribute? Great! First, read [our guidelines for contributors](https://developers.google.com/blockly/guides/modify/contributing).
|
||||
|
||||
@@ -22,6 +22,29 @@
|
||||
* @author madeeha@google.com (Madeeha Ghori)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.AppComponent');
|
||||
|
||||
goog.require('Blockly');
|
||||
|
||||
goog.require('blocklyApp.AudioService');
|
||||
goog.require('blocklyApp.BlockConnectionService');
|
||||
goog.require('blocklyApp.BlockOptionsModalComponent');
|
||||
goog.require('blocklyApp.BlockOptionsModalService');
|
||||
goog.require('blocklyApp.KeyboardInputService');
|
||||
goog.require('blocklyApp.NotificationsService');
|
||||
goog.require('blocklyApp.SidebarComponent');
|
||||
goog.require('blocklyApp.ToolboxModalComponent');
|
||||
goog.require('blocklyApp.ToolboxModalService');
|
||||
goog.require('blocklyApp.TranslatePipe');
|
||||
goog.require('blocklyApp.TreeService');
|
||||
goog.require('blocklyApp.UtilsService');
|
||||
goog.require('blocklyApp.VariableAddModalComponent');
|
||||
goog.require('blocklyApp.VariableModalService');
|
||||
goog.require('blocklyApp.VariableRenameModalComponent');
|
||||
goog.require('blocklyApp.VariableRemoveModalComponent');
|
||||
goog.require('blocklyApp.WorkspaceComponent');
|
||||
|
||||
|
||||
blocklyApp.workspace = new Blockly.Workspace();
|
||||
|
||||
blocklyApp.AppComponent = ng.core.Component({
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
* @author sll@google.com (Sean Lip)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.AudioService');
|
||||
|
||||
goog.require('blocklyApp.NotificationsService');
|
||||
|
||||
|
||||
blocklyApp.AudioService = ng.core.Class({
|
||||
constructor: [
|
||||
blocklyApp.NotificationsService, function(notificationsService) {
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
* @author sll@google.com (Sean Lip)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.BlockConnectionService');
|
||||
|
||||
goog.require('blocklyApp.AudioService');
|
||||
goog.require('blocklyApp.NotificationsService');
|
||||
|
||||
|
||||
blocklyApp.BlockConnectionService = ng.core.Class({
|
||||
constructor: [
|
||||
blocklyApp.NotificationsService, blocklyApp.AudioService,
|
||||
|
||||
@@ -23,6 +23,16 @@
|
||||
* @author sll@google.com (Sean Lip)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.BlockOptionsModalComponent');
|
||||
|
||||
goog.require('blocklyApp.AudioService');
|
||||
goog.require('blocklyApp.BlockOptionsModalService');
|
||||
goog.require('blocklyApp.KeyboardInputService');
|
||||
goog.require('blocklyApp.TranslatePipe');
|
||||
|
||||
goog.require('Blockly.CommonModal');
|
||||
|
||||
|
||||
blocklyApp.BlockOptionsModalComponent = ng.core.Component({
|
||||
selector: 'blockly-block-options-modal',
|
||||
template: `
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
* @author sll@google.com (Sean Lip)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.BlockOptionsModalService');
|
||||
|
||||
|
||||
blocklyApp.BlockOptionsModalService = ng.core.Class({
|
||||
constructor: [function() {
|
||||
this.actionButtonsInfo = [];
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
goog.provide('Blockly.CommonModal');
|
||||
|
||||
|
||||
Blockly.CommonModal = function() {};
|
||||
|
||||
Blockly.CommonModal.setupKeyboardOverrides = function(component) {
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
* @author madeeha@google.com (Madeeha Ghori)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.FieldSegmentComponent');
|
||||
|
||||
goog.require('blocklyApp.NotificationsService');
|
||||
goog.require('blocklyApp.TranslatePipe');
|
||||
goog.require('blocklyApp.VariableModalService');
|
||||
|
||||
|
||||
blocklyApp.FieldSegmentComponent = ng.core.Component({
|
||||
selector: 'blockly-field-segment',
|
||||
template: `
|
||||
@@ -51,11 +58,10 @@ blocklyApp.FieldSegmentComponent = ng.core.Component({
|
||||
<template [ngIf]="isDropdown()">
|
||||
{{getPrefixText()}}
|
||||
<select [id]="mainFieldId" [name]="mainFieldId"
|
||||
[ngModel]="mainField.getValue()" (ngModelChange)="setDropdownValue($event)"
|
||||
[ngModel]="selectedOption" (ngModelChange)="setDropdownValue($event)"
|
||||
(keydown.enter)="selectOption()"
|
||||
tabindex="-1">
|
||||
<option *ngFor="#option of dropdownOptions" value="{{option.value}}"
|
||||
[attr.select]="optionValue === option.value ? true : null">
|
||||
<option *ngFor="#option of dropdownOptions" value="{{option.value}}">
|
||||
{{option.text}}
|
||||
</option>
|
||||
</select>
|
||||
@@ -85,6 +91,7 @@ blocklyApp.FieldSegmentComponent = ng.core.Component({
|
||||
ngDoCheck: function() {
|
||||
if (this.isDropdown() && this.shouldBreakCache()) {
|
||||
this.optionValue = this.mainField.getValue();
|
||||
this.fieldValue = this.mainField.getValue();
|
||||
this.rawOptions = this.mainField.getOptions();
|
||||
this.dropdownOptions = this.rawOptions.map(function(valueAndText) {
|
||||
return {
|
||||
@@ -92,6 +99,13 @@ blocklyApp.FieldSegmentComponent = ng.core.Component({
|
||||
value: valueAndText[1]
|
||||
};
|
||||
});
|
||||
|
||||
// Set the currently selected value to the variable on the field.
|
||||
for (var i = 0; i < this.dropdownOptions.length; i++) {
|
||||
if (this.dropdownOptions[i].text === this.fieldValue) {
|
||||
this.selectedOption = this.dropdownOptions[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Returns whether the mutable, cached information needs to be refreshed.
|
||||
@@ -103,11 +117,15 @@ blocklyApp.FieldSegmentComponent = ng.core.Component({
|
||||
|
||||
for (var i = 0; i < this.rawOptions.length; i++) {
|
||||
// Compare the value of the cached options with the values in the field.
|
||||
if (newOptions[i][1] != this.rawOptions[i][1]) {
|
||||
if (newOptions[i][0] != this.rawOptions[i][0]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fieldValue != this.mainField.getValue()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
// Gets the prefix text, to be printed before a field.
|
||||
@@ -149,12 +167,16 @@ blocklyApp.FieldSegmentComponent = ng.core.Component({
|
||||
},
|
||||
// Confirm a selection for dropdown fields.
|
||||
selectOption: function() {
|
||||
if (this.optionValue == Blockly.Msg.RENAME_VARIABLE) {
|
||||
if (this.optionValue != Blockly.RENAME_VARIABLE_ID && this.optionValue !=
|
||||
Blockly.DELETE_VARIABLE_ID) {
|
||||
this.mainField.setValue(this.optionValue);
|
||||
}
|
||||
|
||||
if (this.optionValue == Blockly.RENAME_VARIABLE_ID) {
|
||||
this.variableModalService.showRenameModal_(this.mainField.getValue());
|
||||
}
|
||||
|
||||
if (this.optionValue ==
|
||||
Blockly.Msg.DELETE_VARIABLE.replace('%1', this.mainField.getValue())) {
|
||||
if (this.optionValue == Blockly.DELETE_VARIABLE_ID) {
|
||||
this.variableModalService.showRemoveModal_(this.mainField.getValue());
|
||||
}
|
||||
},
|
||||
@@ -165,11 +187,6 @@ blocklyApp.FieldSegmentComponent = ng.core.Component({
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.optionValue != Blockly.Msg.RENAME_VARIABLE && this.optionValue !=
|
||||
Blockly.Msg.DELETE_VARIABLE.replace('%1', this.mainField.getValue())) {
|
||||
this.mainField.setValue(this.optionValue);
|
||||
}
|
||||
|
||||
var optionText = undefined;
|
||||
for (var i = 0; i < this.dropdownOptions.length; i++) {
|
||||
if (this.dropdownOptions[i].value == optionValue) {
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
* @author sll@google.com (Sean Lip)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.KeyboardInputService');
|
||||
|
||||
|
||||
blocklyApp.KeyboardInputService = ng.core.Class({
|
||||
constructor: [function() {
|
||||
// Default custom actions for global keystrokes. The keys of this object
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
* @author sll@google.com (Sean Lip)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.NotificationsService');
|
||||
|
||||
|
||||
blocklyApp.NotificationsService = ng.core.Class({
|
||||
constructor: [function() {
|
||||
this.currentMessage = '';
|
||||
|
||||
@@ -24,6 +24,17 @@
|
||||
* @author sll@google.com (Sean Lip)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.SidebarComponent');
|
||||
|
||||
goog.require('blocklyApp.UtilsService');
|
||||
|
||||
goog.require('blocklyApp.BlockConnectionService');
|
||||
goog.require('blocklyApp.ToolboxModalService');
|
||||
goog.require('blocklyApp.TranslatePipe');
|
||||
goog.require('blocklyApp.TreeService');
|
||||
goog.require('blocklyApp.VariableModalService');
|
||||
|
||||
|
||||
blocklyApp.SidebarComponent = ng.core.Component({
|
||||
selector: 'blockly-sidebar',
|
||||
template: `
|
||||
|
||||
@@ -23,6 +23,17 @@
|
||||
* @author sll@google.com (Sean Lip)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.ToolboxModalComponent');
|
||||
|
||||
goog.require('Blockly.CommonModal');
|
||||
goog.require('blocklyApp.AudioService');
|
||||
goog.require('blocklyApp.KeyboardInputService');
|
||||
goog.require('blocklyApp.ToolboxModalService');
|
||||
goog.require('blocklyApp.TranslatePipe');
|
||||
goog.require('blocklyApp.TreeService');
|
||||
goog.require('blocklyApp.UtilsService');
|
||||
|
||||
|
||||
blocklyApp.ToolboxModalComponent = ng.core.Component({
|
||||
selector: 'blockly-toolbox-modal',
|
||||
template: `
|
||||
|
||||
@@ -23,6 +23,15 @@
|
||||
* @author sll@google.com (Sean Lip)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.ToolboxModalService');
|
||||
|
||||
goog.require('blocklyApp.UtilsService');
|
||||
|
||||
goog.require('blocklyApp.BlockConnectionService');
|
||||
goog.require('blocklyApp.NotificationsService');
|
||||
goog.require('blocklyApp.TreeService');
|
||||
|
||||
|
||||
blocklyApp.ToolboxModalService = ng.core.Class({
|
||||
constructor: [
|
||||
blocklyApp.BlockConnectionService,
|
||||
@@ -51,45 +60,56 @@ blocklyApp.ToolboxModalService = ng.core.Class({
|
||||
'A pre-show hook must be defined for the toolbox modal before it ' +
|
||||
'can be shown.');
|
||||
};
|
||||
|
||||
// Populate the toolbox categories.
|
||||
this.allToolboxCategories = [];
|
||||
var toolboxXmlElt = document.getElementById('blockly-toolbox-xml');
|
||||
var toolboxCategoryElts = toolboxXmlElt.getElementsByTagName('category');
|
||||
if (toolboxCategoryElts.length) {
|
||||
this.allToolboxCategories = Array.from(toolboxCategoryElts).map(
|
||||
function(categoryElt) {
|
||||
var tmpWorkspace = new Blockly.Workspace();
|
||||
Blockly.Xml.domToWorkspace(categoryElt, tmpWorkspace);
|
||||
return {
|
||||
categoryName: categoryElt.attributes.name.value,
|
||||
blocks: tmpWorkspace.topBlocks_
|
||||
};
|
||||
}
|
||||
);
|
||||
this.computeCategoriesForCreateNewGroupModal_();
|
||||
} else {
|
||||
// A timeout seems to be needed in order for the .children accessor to
|
||||
// work correctly.
|
||||
var that = this;
|
||||
setTimeout(function() {
|
||||
// If there are no top-level categories, we create a single category
|
||||
// containing all the top-level blocks.
|
||||
var tmpWorkspace = new Blockly.Workspace();
|
||||
Array.from(toolboxXmlElt.children).forEach(function(topLevelNode) {
|
||||
Blockly.Xml.domToBlock(tmpWorkspace, topLevelNode);
|
||||
});
|
||||
|
||||
that.allToolboxCategories = [{
|
||||
categoryName: '',
|
||||
blocks: tmpWorkspace.topBlocks_
|
||||
}];
|
||||
|
||||
that.computeCategoriesForCreateNewGroupModal_();
|
||||
});
|
||||
}
|
||||
}
|
||||
],
|
||||
populateToolbox_: function() {
|
||||
// Populate the toolbox categories.
|
||||
this.allToolboxCategories = [];
|
||||
var toolboxXmlElt = document.getElementById('blockly-toolbox-xml');
|
||||
var toolboxCategoryElts = toolboxXmlElt.getElementsByTagName('category');
|
||||
if (toolboxCategoryElts.length) {
|
||||
this.allToolboxCategories = Array.from(toolboxCategoryElts).map(
|
||||
function(categoryElt) {
|
||||
var tmpWorkspace = new Blockly.Workspace();
|
||||
var custom = categoryElt.attributes.custom
|
||||
// TODO (corydiers): Implement custom flyouts once #1153 is solved.
|
||||
if (custom && custom.value == Blockly.VARIABLE_CATEGORY_NAME) {
|
||||
var varBlocks =
|
||||
Blockly.Variables.flyoutCategoryBlocks(blocklyApp.workspace);
|
||||
varBlocks.forEach(function(block) {
|
||||
Blockly.Xml.domToBlock(block, tmpWorkspace);
|
||||
});
|
||||
} else {
|
||||
Blockly.Xml.domToWorkspace(categoryElt, tmpWorkspace);
|
||||
}
|
||||
return {
|
||||
categoryName: categoryElt.attributes.name.value,
|
||||
blocks: tmpWorkspace.topBlocks_
|
||||
};
|
||||
}
|
||||
);
|
||||
this.computeCategoriesForCreateNewGroupModal_();
|
||||
} else {
|
||||
// A timeout seems to be needed in order for the .children accessor to
|
||||
// work correctly.
|
||||
var that = this;
|
||||
setTimeout(function() {
|
||||
// If there are no top-level categories, we create a single category
|
||||
// containing all the top-level blocks.
|
||||
var tmpWorkspace = new Blockly.Workspace();
|
||||
Array.from(toolboxXmlElt.children).forEach(function(topLevelNode) {
|
||||
Blockly.Xml.domToBlock(tmpWorkspace, topLevelNode);
|
||||
});
|
||||
|
||||
that.allToolboxCategories = [{
|
||||
categoryName: '',
|
||||
blocks: tmpWorkspace.topBlocks_
|
||||
}];
|
||||
|
||||
that.computeCategoriesForCreateNewGroupModal_();
|
||||
});
|
||||
}
|
||||
},
|
||||
computeCategoriesForCreateNewGroupModal_: function() {
|
||||
// Precompute toolbox categories for blocks that have no output
|
||||
// connection (and that can therefore be used as the base block of a
|
||||
@@ -156,6 +176,7 @@ blocklyApp.ToolboxModalService = ng.core.Class({
|
||||
var that = this;
|
||||
|
||||
var selectedToolboxCategories = [];
|
||||
this.populateToolbox_();
|
||||
this.allToolboxCategories.forEach(function(toolboxCategory) {
|
||||
var selectedBlocks = toolboxCategory.blocks.filter(function(block) {
|
||||
return that.blockConnectionService.canBeAttachedToMarkedConnection(
|
||||
@@ -193,6 +214,7 @@ blocklyApp.ToolboxModalService = ng.core.Class({
|
||||
},
|
||||
showToolboxModalForCreateNewGroup: function(sourceButtonId) {
|
||||
var that = this;
|
||||
this.populateToolbox_();
|
||||
this.showModal_(this.toolboxCategoriesForNewGroup, function(block) {
|
||||
var blockDescription = that.utilsService.getBlockDescription(block);
|
||||
var xml = Blockly.Xml.blockToDom(block);
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
* @author sll@google.com (Sean Lip)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.TranslatePipe');
|
||||
|
||||
|
||||
blocklyApp.TranslatePipe = ng.core.Pipe({
|
||||
name: 'translate'
|
||||
})
|
||||
|
||||
@@ -25,6 +25,17 @@
|
||||
* @author madeeha@google.com (Madeeha Ghori)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.TreeService');
|
||||
|
||||
goog.require('blocklyApp.UtilsService');
|
||||
|
||||
goog.require('blocklyApp.AudioService');
|
||||
goog.require('blocklyApp.BlockConnectionService');
|
||||
goog.require('blocklyApp.BlockOptionsModalService');
|
||||
goog.require('blocklyApp.NotificationsService');
|
||||
goog.require('blocklyApp.VariableModalService');
|
||||
|
||||
|
||||
blocklyApp.TreeService = ng.core.Class({
|
||||
constructor: [
|
||||
blocklyApp.AudioService,
|
||||
@@ -32,14 +43,16 @@ blocklyApp.TreeService = ng.core.Class({
|
||||
blocklyApp.BlockOptionsModalService,
|
||||
blocklyApp.NotificationsService,
|
||||
blocklyApp.UtilsService,
|
||||
blocklyApp.VariableModalService,
|
||||
function(
|
||||
audioService, blockConnectionService, blockOptionsModalService,
|
||||
notificationsService, utilsService) {
|
||||
notificationsService, utilsService, variableModalService) {
|
||||
this.audioService = audioService;
|
||||
this.blockConnectionService = blockConnectionService;
|
||||
this.blockOptionsModalService = blockOptionsModalService;
|
||||
this.notificationsService = notificationsService;
|
||||
this.utilsService = utilsService;
|
||||
this.variableModalService = variableModalService;
|
||||
|
||||
// The suffix used for all IDs of block root elements.
|
||||
this.BLOCK_ROOT_ID_SUFFIX_ = blocklyApp.BLOCK_ROOT_ID_SUFFIX;
|
||||
@@ -207,11 +220,10 @@ blocklyApp.TreeService = ng.core.Class({
|
||||
var activeDesc = document.getElementById(this.getActiveDescId(treeId));
|
||||
if (activeDesc) {
|
||||
activeDesc.classList.remove('blocklyActiveDescendant');
|
||||
}
|
||||
|
||||
if (this.activeDescendantIds_[treeId]) {
|
||||
delete this.activeDescendantIds_[treeId];
|
||||
} else {
|
||||
throw Error(
|
||||
'The active desc element for the tree with ID ' + treeId +
|
||||
' is invalid.');
|
||||
}
|
||||
},
|
||||
clearAllActiveDescs: function() {
|
||||
@@ -460,14 +472,16 @@ blocklyApp.TreeService = ng.core.Class({
|
||||
onKeypress: function(e, tree) {
|
||||
// TODO(sll): Instead of this, have a common ActiveContextService which
|
||||
// returns true if at least one modal is shown, and false otherwise.
|
||||
if (this.blockOptionsModalService.isModalShown()) {
|
||||
if (this.blockOptionsModalService.isModalShown() ||
|
||||
this.variableModalService.isModalShown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var treeId = tree.id;
|
||||
var activeDesc = document.getElementById(this.getActiveDescId(treeId));
|
||||
if (!activeDesc) {
|
||||
console.error('ERROR: no active descendant for current tree.');
|
||||
// The underlying Blockly instance may have decided blocks needed to
|
||||
// be deleted. This is not necessarily an error, but needs to be repaired.
|
||||
this.initActiveDesc(treeId);
|
||||
activeDesc = document.getElementById(this.getActiveDescId(treeId));
|
||||
}
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
* @author madeeha@google.com (Madeeha Ghori)
|
||||
*/
|
||||
|
||||
var blocklyApp = {};
|
||||
goog.provide('blocklyApp.UtilsService');
|
||||
|
||||
|
||||
blocklyApp.ID_FOR_EMPTY_WORKSPACE_BTN = 'blocklyEmptyWorkspaceBtn';
|
||||
blocklyApp.BLOCK_ROOT_ID_SUFFIX = '-blockRoot';
|
||||
|
||||
|
||||
@@ -23,6 +23,16 @@
|
||||
* @author corydiers@google.com (Cory Diers)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.VariableAddModalComponent');
|
||||
|
||||
goog.require('blocklyApp.AudioService');
|
||||
goog.require('blocklyApp.KeyboardInputService');
|
||||
goog.require('blocklyApp.TranslatePipe');
|
||||
goog.require('blocklyApp.VariableModalService');
|
||||
|
||||
goog.require('Blockly.CommonModal');
|
||||
|
||||
|
||||
blocklyApp.VariableAddModalComponent = ng.core.Component({
|
||||
selector: 'blockly-add-variable-modal',
|
||||
template: `
|
||||
@@ -33,6 +43,8 @@ blocklyApp.VariableAddModalComponent = ng.core.Component({
|
||||
<div id="varModal" class="blocklyModal" role="alertdialog"
|
||||
(click)="$event.stopPropagation()" tabindex="0"
|
||||
aria-labelledby="variableModalHeading">
|
||||
<h3 id="variableModalHeading">Add a variable...</h3>
|
||||
|
||||
<form id="varForm">
|
||||
<p id="inputLabel">New Variable Name:
|
||||
<input id="mainFieldId" type="text" [ngModel]="VALUE"
|
||||
@@ -40,10 +52,10 @@ blocklyApp.VariableAddModalComponent = ng.core.Component({
|
||||
aria-labelledby="inputLabel" />
|
||||
</p>
|
||||
<hr>
|
||||
<button id="submitButton" (click)="submit()">
|
||||
<button type="button" id="submitButton" (click)="submit()">
|
||||
SUBMIT
|
||||
</button>
|
||||
<button id="cancelButton" (click)="dismissModal()">
|
||||
<button type="button" id="cancelButton" (click)="dismissModal()">
|
||||
CANCEL
|
||||
</button>
|
||||
</form>
|
||||
@@ -71,7 +83,7 @@ blocklyApp.VariableAddModalComponent = ng.core.Component({
|
||||
Blockly.CommonModal.setupKeyboardOverrides(that);
|
||||
|
||||
setTimeout(function() {
|
||||
document.getElementById('mainFieldId').focus();
|
||||
document.getElementById('varModal').focus();
|
||||
}, 150);
|
||||
}
|
||||
);
|
||||
@@ -96,10 +108,11 @@ blocklyApp.VariableAddModalComponent = ng.core.Component({
|
||||
// Submits the name change for the variable.
|
||||
submit: function() {
|
||||
this.workspace.createVariable(this.variableName);
|
||||
this.hideModal_();
|
||||
this.dismissModal();
|
||||
},
|
||||
// Dismisses and closes the modal.
|
||||
dismissModal: function() {
|
||||
this.variableModalService.hideModal();
|
||||
this.hideModal_();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
* @author corydiers@google.com (Cory Diers)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.VariableModalService');
|
||||
|
||||
blocklyApp.VariableModalService = ng.core.Class({
|
||||
constructor: [
|
||||
function() {
|
||||
@@ -63,14 +65,25 @@ blocklyApp.VariableModalService = ng.core.Class({
|
||||
},
|
||||
// Show the remove variable modal.
|
||||
showRemoveModal_: function(oldName) {
|
||||
var count = blocklyApp.workspace.getVariableUses(oldName).length;
|
||||
var count = this.getNumVariables(oldName);
|
||||
this.modalIsShown = true;
|
||||
if (count > 1) {
|
||||
this.preRemoveShowHook(oldName, count);
|
||||
this.modalIsShown = true;
|
||||
} else {
|
||||
blocklyApp.workspace.deleteVariableInternal_(oldName);
|
||||
var variable = blocklyApp.workspace.getVariable(oldName);
|
||||
blocklyApp.workspace.deleteVariableInternal_(variable);
|
||||
// Allow the execution loop to finish before "closing" the modal. While
|
||||
// the modal never opens, its being "open" should prevent other keypresses
|
||||
// anyway.
|
||||
var that = this;
|
||||
setTimeout(function() {
|
||||
that.modalIsShown = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
getNumVariables: function(oldName) {
|
||||
return blocklyApp.workspace.getVariableUses(oldName).length;
|
||||
},
|
||||
// Hide the variable modal.
|
||||
hideModal: function() {
|
||||
this.modalIsShown = false;
|
||||
|
||||
@@ -23,6 +23,17 @@
|
||||
* @author corydiers@google.com (Cory Diers)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.VariableRemoveModalComponent');
|
||||
|
||||
goog.require('blocklyApp.AudioService');
|
||||
goog.require('blocklyApp.KeyboardInputService');
|
||||
goog.require('blocklyApp.TranslatePipe');
|
||||
goog.require('blocklyApp.TreeService');
|
||||
goog.require('blocklyApp.VariableModalService');
|
||||
|
||||
goog.require('Blockly.CommonModal');
|
||||
|
||||
|
||||
blocklyApp.VariableRemoveModalComponent = ng.core.Component({
|
||||
selector: 'blockly-remove-variable-modal',
|
||||
template: `
|
||||
@@ -33,15 +44,17 @@ blocklyApp.VariableRemoveModalComponent = ng.core.Component({
|
||||
<div id="varModal" class="blocklyModal" role="alertdialog"
|
||||
(click)="$event.stopPropagation()" tabindex="0"
|
||||
aria-labelledby="variableModalHeading">
|
||||
<h3 id="variableModalHeading">
|
||||
Delete {{getNumVariables()}} uses of the "{{currentVariableName}}"
|
||||
variable?
|
||||
</h3>
|
||||
|
||||
<form id="varForm">
|
||||
<p id="label">Remove {{count}} instances of
|
||||
"{{currentVariableName}}" variable?
|
||||
</p>
|
||||
<hr>
|
||||
<button id="yesButton" (click)="submit()">
|
||||
<button type="button" id="yesButton" (click)="submit()">
|
||||
YES
|
||||
</button>
|
||||
<button id="noButton" (click)="dismissModal()">
|
||||
<button type="button" id="noButton" (click)="dismissModal()">
|
||||
NO
|
||||
</button>
|
||||
</form>
|
||||
@@ -52,9 +65,13 @@ blocklyApp.VariableRemoveModalComponent = ng.core.Component({
|
||||
})
|
||||
.Class({
|
||||
constructor: [
|
||||
blocklyApp.AudioService, blocklyApp.KeyboardInputService, blocklyApp.VariableModalService,
|
||||
function(audioService, keyboardService, variableService) {
|
||||
blocklyApp.AudioService,
|
||||
blocklyApp.KeyboardInputService,
|
||||
blocklyApp.TreeService,
|
||||
blocklyApp.VariableModalService,
|
||||
function(audioService, keyboardService, treeService, variableService) {
|
||||
this.workspace = blocklyApp.workspace;
|
||||
this.treeService = treeService;
|
||||
this.variableModalService = variableService;
|
||||
this.audioService = audioService;
|
||||
this.keyboardInputService = keyboardService
|
||||
@@ -73,7 +90,7 @@ blocklyApp.VariableRemoveModalComponent = ng.core.Component({
|
||||
Blockly.CommonModal.setupKeyboardOverrides(that);
|
||||
|
||||
setTimeout(function() {
|
||||
document.getElementById('label').focus();
|
||||
document.getElementById('varModal').focus();
|
||||
}, 150);
|
||||
}
|
||||
);
|
||||
@@ -91,13 +108,18 @@ blocklyApp.VariableRemoveModalComponent = ng.core.Component({
|
||||
getInteractiveContainer: function() {
|
||||
return document.getElementById("varForm");
|
||||
},
|
||||
getNumVariables: function() {
|
||||
return this.variableModalService.getNumVariables(this.currentVariableName);
|
||||
},
|
||||
// Submits the name change for the variable.
|
||||
submit: function() {
|
||||
blocklyApp.workspace.deleteVariableInternal_(this.currentVariableName);
|
||||
this.hideModal_();
|
||||
var variable = blocklyApp.workspace.getVariable(this.currentVariableName);
|
||||
blocklyApp.workspace.deleteVariableInternal_(variable);
|
||||
this.dismissModal();
|
||||
},
|
||||
// Dismisses and closes the modal.
|
||||
dismissModal: function() {
|
||||
this.variableModalService.hideModal();
|
||||
this.hideModal_();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -23,6 +23,15 @@
|
||||
* @author corydiers@google.com (Cory Diers)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.VariableRenameModalComponent');
|
||||
|
||||
goog.require('Blockly.CommonModal');
|
||||
goog.require('blocklyApp.AudioService');
|
||||
goog.require('blocklyApp.KeyboardInputService');
|
||||
goog.require('blocklyApp.TranslatePipe');
|
||||
goog.require('blocklyApp.VariableModalService');
|
||||
|
||||
|
||||
blocklyApp.VariableRenameModalComponent = ng.core.Component({
|
||||
selector: 'blockly-rename-variable-modal',
|
||||
template: `
|
||||
@@ -33,6 +42,10 @@ blocklyApp.VariableRenameModalComponent = ng.core.Component({
|
||||
<div id="varModal" class="blocklyModal" role="alertdialog"
|
||||
(click)="$event.stopPropagation()" tabindex="0"
|
||||
aria-labelledby="variableModalHeading">
|
||||
<h3 id="variableModalHeading">
|
||||
Rename the "{{currentVariableName}}" variable...
|
||||
</h3>
|
||||
|
||||
<form id="varForm">
|
||||
<p id="inputLabel">New Variable Name:
|
||||
<input id="mainFieldId" type="text" [ngModel]="VALUE"
|
||||
@@ -40,10 +53,10 @@ blocklyApp.VariableRenameModalComponent = ng.core.Component({
|
||||
aria-labelledby="inputLabel" />
|
||||
</p>
|
||||
<hr>
|
||||
<button id="submitButton" (click)="submit()">
|
||||
<button type="button" id="submitButton" (click)="submit()">
|
||||
SUBMIT
|
||||
</button>
|
||||
<button id="cancelButton" (click)="dismissModal()">
|
||||
<button type="button" id="cancelButton" (click)="dismissModal()">
|
||||
CANCEL
|
||||
</button>
|
||||
</form>
|
||||
@@ -73,7 +86,7 @@ blocklyApp.VariableRenameModalComponent = ng.core.Component({
|
||||
Blockly.CommonModal.setupKeyboardOverrides(that);
|
||||
|
||||
setTimeout(function() {
|
||||
document.getElementById('mainFieldId').focus();
|
||||
document.getElementById('varModal').focus();
|
||||
}, 150);
|
||||
}
|
||||
);
|
||||
@@ -98,10 +111,11 @@ blocklyApp.VariableRenameModalComponent = ng.core.Component({
|
||||
// Submits the name change for the variable.
|
||||
submit: function() {
|
||||
this.workspace.renameVariable(this.currentVariableName, this.variableName);
|
||||
this.hideModal_();
|
||||
this.dismissModal();
|
||||
},
|
||||
// Dismisses and closes the modal.
|
||||
dismissModal: function() {
|
||||
this.variableModalService.hideModal();
|
||||
this.hideModal_();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -23,6 +23,17 @@
|
||||
* @author madeeha@google.com (Madeeha Ghori)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.WorkspaceBlockComponent');
|
||||
|
||||
goog.require('blocklyApp.UtilsService');
|
||||
|
||||
goog.require('blocklyApp.AudioService');
|
||||
goog.require('blocklyApp.BlockConnectionService');
|
||||
goog.require('blocklyApp.FieldSegmentComponent');
|
||||
goog.require('blocklyApp.TranslatePipe');
|
||||
goog.require('blocklyApp.TreeService');
|
||||
|
||||
|
||||
blocklyApp.WorkspaceBlockComponent = ng.core.Component({
|
||||
selector: 'blockly-workspace-block',
|
||||
template: `
|
||||
|
||||
@@ -24,6 +24,16 @@
|
||||
* @author madeeha@google.com (Madeeha Ghori)
|
||||
*/
|
||||
|
||||
goog.provide('blocklyApp.WorkspaceComponent');
|
||||
|
||||
goog.require('blocklyApp.NotificationsService');
|
||||
goog.require('blocklyApp.ToolboxModalService');
|
||||
goog.require('blocklyApp.TranslatePipe');
|
||||
goog.require('blocklyApp.TreeService');
|
||||
|
||||
goog.require('blocklyApp.WorkspaceBlockComponent');
|
||||
|
||||
|
||||
blocklyApp.WorkspaceComponent = ng.core.Component({
|
||||
selector: 'blockly-workspace',
|
||||
template: `
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+542
-501
File diff suppressed because it is too large
Load Diff
+31
-11
@@ -13,7 +13,7 @@ window.BLOCKLY_DIR = (function() {
|
||||
if (!isNodeJS) {
|
||||
// Find name of current directory.
|
||||
var scripts = document.getElementsByTagName('script');
|
||||
var re = new RegExp('(.+)[\/]blockly_uncompressed\.js$');
|
||||
var re = new RegExp('(.+)[\/]blockly_(.*)uncompressed\.js$');
|
||||
for (var i = 0, script; script = scripts[i]; i++) {
|
||||
var match = re.exec(script.src);
|
||||
if (match) {
|
||||
@@ -39,7 +39,7 @@ window.BLOCKLY_BOOT = function() {
|
||||
dir = window.BLOCKLY_DIR.match(/[^\/]+$/)[0];
|
||||
}
|
||||
goog.addDependency("../../../" + dir + "/core/warning.js", ['Blockly.Warning'], ['Blockly.Bubble', 'Blockly.Icon']);
|
||||
goog.addDependency("../../../" + dir + "/core/field.js", ['Blockly.Field'], ['goog.asserts', 'goog.dom', 'goog.math.Size', 'goog.style', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/field.js", ['Blockly.Field'], ['Blockly.Gesture', 'goog.asserts', 'goog.dom', 'goog.math.Size', 'goog.style', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/scrollbar.js", ['Blockly.Scrollbar', 'Blockly.ScrollbarPair'], ['goog.dom', 'goog.events']);
|
||||
goog.addDependency("../../../" + dir + "/core/extensions.js", ['Blockly.Extensions'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/comment.js", ['Blockly.Comment'], ['Blockly.Bubble', 'Blockly.Icon', 'goog.userAgent']);
|
||||
@@ -54,42 +54,52 @@ goog.addDependency("../../../" + dir + "/core/block_render_svg.js", ['Blockly.Bl
|
||||
goog.addDependency("../../../" + dir + "/core/utils.js", ['Blockly.utils'], ['Blockly.Touch', 'goog.dom', 'goog.events.BrowserFeature', 'goog.math.Coordinate', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/msg.js", ['Blockly.Msg'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/contextmenu.js", ['Blockly.ContextMenu'], ['goog.dom', 'goog.events', 'goog.style', 'goog.ui.Menu', 'goog.ui.MenuItem']);
|
||||
goog.addDependency("../../../" + dir + "/core/block_dragger.js", ['Blockly.BlockDragger'], ['Blockly.DraggedConnectionManager', 'goog.math.Coordinate', 'goog.asserts']);
|
||||
goog.addDependency("../../../" + dir + "/core/workspace_dragger.js", ['Blockly.WorkspaceDragger'], ['goog.math.Coordinate', 'goog.asserts']);
|
||||
goog.addDependency("../../../" + dir + "/core/icon.js", ['Blockly.Icon'], ['goog.dom', 'goog.math.Coordinate']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_textinput.js", ['Blockly.FieldTextInput'], ['Blockly.Field', 'Blockly.Msg', 'goog.asserts', 'goog.dom', 'goog.dom.TagName', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/toolbox.js", ['Blockly.Toolbox'], ['Blockly.Flyout', 'Blockly.Touch', 'goog.dom', 'goog.dom.TagName', 'goog.events', 'goog.events.BrowserFeature', 'goog.html.SafeHtml', 'goog.html.SafeStyle', 'goog.math.Rect', 'goog.style', 'goog.ui.tree.TreeControl', 'goog.ui.tree.TreeNode']);
|
||||
goog.addDependency("../../../" + dir + "/core/toolbox.js", ['Blockly.Toolbox'], ['Blockly.Flyout', 'Blockly.HorizontalFlyout', 'Blockly.Touch', 'Blockly.VerticalFlyout', 'goog.dom', 'goog.dom.TagName', 'goog.events', 'goog.events.BrowserFeature', 'goog.html.SafeHtml', 'goog.html.SafeStyle', 'goog.math.Rect', 'goog.style', 'goog.ui.tree.TreeControl', 'goog.ui.tree.TreeNode']);
|
||||
goog.addDependency("../../../" + dir + "/core/options.js", ['Blockly.Options'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/block.js", ['Blockly.Block'], ['Blockly.Blocks', 'Blockly.Comment', 'Blockly.Connection', 'Blockly.Extensions', 'Blockly.Input', 'Blockly.Mutator', 'Blockly.Warning', 'Blockly.Workspace', 'Blockly.Xml', 'goog.array', 'goog.asserts', 'goog.math.Coordinate', 'goog.string']);
|
||||
goog.addDependency("../../../" + dir + "/core/block_svg.js", ['Blockly.BlockSvg'], ['Blockly.Block', 'Blockly.ContextMenu', 'Blockly.RenderedConnection', 'Blockly.Touch', 'Blockly.utils', 'goog.Timer', 'goog.asserts', 'goog.dom', 'goog.math.Coordinate', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/workspace_audio.js", ['Blockly.WorkspaceAudio'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/block_svg.js", ['Blockly.BlockSvg'], ['Blockly.Block', 'Blockly.ContextMenu', 'Blockly.Grid', 'Blockly.RenderedConnection', 'Blockly.Touch', 'Blockly.utils', 'goog.Timer', 'goog.asserts', 'goog.dom', 'goog.math.Coordinate', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/flyout_dragger.js", ['Blockly.FlyoutDragger'], ['Blockly.WorkspaceDragger', 'goog.asserts', 'goog.math.Coordinate']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_dropdown.js", ['Blockly.FieldDropdown'], ['Blockly.Field', 'goog.dom', 'goog.events', 'goog.style', 'goog.ui.Menu', 'goog.ui.MenuItem', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/gesture.js", ['Blockly.Gesture'], ['Blockly.BlockDragger', 'Blockly.constants', 'Blockly.FlyoutDragger', 'Blockly.Tooltip', 'Blockly.Touch', 'Blockly.WorkspaceDragger', 'goog.asserts', 'goog.math.Coordinate']);
|
||||
goog.addDependency("../../../" + dir + "/core/variable_map.js", ['Blockly.VariableMap'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/css.js", ['Blockly.Css'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/field_checkbox.js", ['Blockly.FieldCheckbox'], ['Blockly.Field']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_label.js", ['Blockly.FieldLabel'], ['Blockly.Field', 'Blockly.Tooltip', 'goog.dom', 'goog.math.Size']);
|
||||
goog.addDependency("../../../" + dir + "/core/names.js", ['Blockly.Names'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/workspace_drag_surface_svg.js", ['Blockly.WorkspaceDragSurfaceSvg'], ['Blockly.utils', 'goog.asserts', 'goog.math.Coordinate']);
|
||||
goog.addDependency("../../../" + dir + "/core/flyout_base.js", ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.Events', 'Blockly.FlyoutButton', 'Blockly.Gesture', 'Blockly.Touch', 'Blockly.WorkspaceSvg', 'goog.dom', 'goog.events', 'goog.math.Rect', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/mutator.js", ['Blockly.Mutator'], ['Blockly.Bubble', 'Blockly.Icon', 'Blockly.WorkspaceSvg', 'goog.Timer', 'goog.dom']);
|
||||
goog.addDependency("../../../" + dir + "/core/variable_model.js", ['Blockly.VariableModel'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/variable_model.js", ['Blockly.VariableModel'], ['goog.string']);
|
||||
goog.addDependency("../../../" + dir + "/core/constants.js", ['Blockly.constants'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/rendered_connection.js", ['Blockly.RenderedConnection'], ['Blockly.Connection']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_colour.js", ['Blockly.FieldColour'], ['Blockly.Field', 'goog.dom', 'goog.events', 'goog.style', 'goog.ui.ColorPicker']);
|
||||
goog.addDependency("../../../" + dir + "/core/flyout_horizontal.js", ['Blockly.HorizontalFlyout'], ['Blockly.Block', 'Blockly.Events', 'Blockly.FlyoutButton', 'Blockly.Flyout', 'Blockly.WorkspaceSvg', 'goog.dom', 'goog.events', 'goog.math.Rect', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_image.js", ['Blockly.FieldImage'], ['Blockly.Field', 'goog.dom', 'goog.math.Size', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_variable.js", ['Blockly.FieldVariable'], ['Blockly.FieldDropdown', 'Blockly.Msg', 'Blockly.Variables', 'goog.asserts', 'goog.string']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_variable.js", ['Blockly.FieldVariable'], ['Blockly.FieldDropdown', 'Blockly.Msg', 'Blockly.VariableModel', 'Blockly.Variables', 'Blockly.VariableModel', 'goog.asserts', 'goog.string']);
|
||||
goog.addDependency("../../../" + dir + "/core/input.js", ['Blockly.Input'], ['Blockly.Connection', 'Blockly.FieldLabel', 'goog.asserts']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_number.js", ['Blockly.FieldNumber'], ['Blockly.FieldTextInput', 'goog.math']);
|
||||
goog.addDependency("../../../" + dir + "/core/variables.js", ['Blockly.Variables'], ['Blockly.Blocks', 'Blockly.constants', 'Blockly.Workspace', 'goog.string']);
|
||||
goog.addDependency("../../../" + dir + "/core/workspace_svg.js", ['Blockly.WorkspaceSvg'], ['Blockly.ConnectionDB', 'Blockly.constants', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.Touch', 'Blockly.Trashcan', 'Blockly.Workspace', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.Xml', 'Blockly.ZoomControls', 'goog.array', 'goog.dom', 'goog.math.Coordinate', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/variables.js", ['Blockly.Variables'], ['Blockly.Blocks', 'Blockly.constants', 'Blockly.VariableModel', 'Blockly.Workspace', 'goog.string']);
|
||||
goog.addDependency("../../../" + dir + "/core/workspace_svg.js", ['Blockly.WorkspaceSvg'], ['Blockly.ConnectionDB', 'Blockly.constants', 'Blockly.Gesture', 'Blockly.Grid', 'Blockly.Options', 'Blockly.ScrollbarPair', 'Blockly.Touch', 'Blockly.Trashcan', 'Blockly.Workspace', 'Blockly.WorkspaceAudio', 'Blockly.WorkspaceDragSurfaceSvg', 'Blockly.Xml', 'Blockly.ZoomControls', 'goog.array', 'goog.dom', 'goog.math.Coordinate', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/bubble.js", ['Blockly.Bubble'], ['Blockly.Touch', 'Blockly.Workspace', 'goog.dom', 'goog.math', 'goog.math.Coordinate', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/procedures.js", ['Blockly.Procedures'], ['Blockly.Blocks', 'Blockly.constants', 'Blockly.Field', 'Blockly.Names', 'Blockly.Workspace']);
|
||||
goog.addDependency("../../../" + dir + "/core/flyout.js", ['Blockly.Flyout'], ['Blockly.Block', 'Blockly.Comment', 'Blockly.Events', 'Blockly.FlyoutButton', 'Blockly.Touch', 'Blockly.WorkspaceSvg', 'goog.dom', 'goog.events', 'goog.math.Rect', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/xml.js", ['Blockly.Xml'], ['goog.asserts', 'goog.dom']);
|
||||
goog.addDependency("../../../" + dir + "/core/blocks.js", ['Blockly.Blocks'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/dragged_connection_manager.js", ['Blockly.DraggedConnectionManager'], ['Blockly.RenderedConnection', 'goog.math.Coordinate']);
|
||||
goog.addDependency("../../../" + dir + "/core/flyout_vertical.js", ['Blockly.VerticalFlyout'], ['Blockly.Block', 'Blockly.Events', 'Blockly.Flyout', 'Blockly.FlyoutButton', 'Blockly.utils', 'Blockly.WorkspaceSvg', 'goog.dom', 'goog.events', 'goog.math.Rect', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/tooltip.js", ['Blockly.Tooltip'], ['goog.dom', 'goog.dom.TagName']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_angle.js", ['Blockly.FieldAngle'], ['Blockly.FieldTextInput', 'goog.math', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/zoom_controls.js", ['Blockly.ZoomControls'], ['Blockly.Touch', 'goog.dom']);
|
||||
goog.addDependency("../../../" + dir + "/core/workspace.js", ['Blockly.Workspace'], ['goog.array', 'goog.math']);
|
||||
goog.addDependency("../../../" + dir + "/core/workspace.js", ['Blockly.Workspace'], ['Blockly.VariableMap', 'goog.array', 'goog.math']);
|
||||
goog.addDependency("../../../" + dir + "/core/grid.js", ['Blockly.Grid'], ['Blockly.utils', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/field_date.js", ['Blockly.FieldDate'], ['Blockly.Field', 'goog.date', 'goog.dom', 'goog.events', 'goog.i18n.DateTimeSymbols', 'goog.i18n.DateTimeSymbols_he', 'goog.style', 'goog.ui.DatePicker']);
|
||||
goog.addDependency("../../../" + dir + "/core/touch.js", ['Blockly.Touch'], ['goog.events', 'goog.events.BrowserFeature', 'goog.string']);
|
||||
goog.addDependency("../../../" + dir + "/core/generator.js", ['Blockly.Generator'], ['Blockly.Block', 'goog.asserts']);
|
||||
goog.addDependency("../../../" + dir + "/core/inject.js", ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Css', 'Blockly.Options', 'Blockly.WorkspaceSvg', 'Blockly.WorkspaceDragSurfaceSvg', 'goog.dom', 'goog.ui.Component', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/inject.js", ['Blockly.inject'], ['Blockly.BlockDragSurfaceSvg', 'Blockly.Css', 'Blockly.Grid', 'Blockly.Options', 'Blockly.WorkspaceSvg', 'Blockly.WorkspaceDragSurfaceSvg', 'goog.dom', 'goog.ui.Component', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/connection_db.js", ['Blockly.ConnectionDB'], ['Blockly.Connection']);
|
||||
goog.addDependency("../../browser_capabilities.js", [], []);
|
||||
goog.addDependency("../../protractor_spec.js", [], []);
|
||||
@@ -1658,6 +1668,7 @@ goog.addDependency("../../doc/js/article.js", [], []);
|
||||
goog.require('Blockly');
|
||||
goog.require('Blockly.Block');
|
||||
goog.require('Blockly.BlockDragSurfaceSvg');
|
||||
goog.require('Blockly.BlockDragger');
|
||||
goog.require('Blockly.BlockSvg');
|
||||
goog.require('Blockly.BlockSvg.render');
|
||||
goog.require('Blockly.Blocks');
|
||||
@@ -1667,6 +1678,7 @@ goog.require('Blockly.Connection');
|
||||
goog.require('Blockly.ConnectionDB');
|
||||
goog.require('Blockly.ContextMenu');
|
||||
goog.require('Blockly.Css');
|
||||
goog.require('Blockly.DraggedConnectionManager');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Extensions');
|
||||
goog.require('Blockly.Field');
|
||||
@@ -1682,7 +1694,11 @@ goog.require('Blockly.FieldTextInput');
|
||||
goog.require('Blockly.FieldVariable');
|
||||
goog.require('Blockly.Flyout');
|
||||
goog.require('Blockly.FlyoutButton');
|
||||
goog.require('Blockly.FlyoutDragger');
|
||||
goog.require('Blockly.Generator');
|
||||
goog.require('Blockly.Gesture');
|
||||
goog.require('Blockly.Grid');
|
||||
goog.require('Blockly.HorizontalFlyout');
|
||||
goog.require('Blockly.Icon');
|
||||
goog.require('Blockly.Input');
|
||||
goog.require('Blockly.Msg');
|
||||
@@ -1697,12 +1713,16 @@ goog.require('Blockly.Toolbox');
|
||||
goog.require('Blockly.Tooltip');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.Trashcan');
|
||||
goog.require('Blockly.VariableMap');
|
||||
goog.require('Blockly.VariableModel');
|
||||
goog.require('Blockly.Variables');
|
||||
goog.require('Blockly.VerticalFlyout');
|
||||
goog.require('Blockly.Warning');
|
||||
goog.require('Blockly.WidgetDiv');
|
||||
goog.require('Blockly.Workspace');
|
||||
goog.require('Blockly.WorkspaceAudio');
|
||||
goog.require('Blockly.WorkspaceDragSurfaceSvg');
|
||||
goog.require('Blockly.WorkspaceDragger');
|
||||
goog.require('Blockly.WorkspaceSvg');
|
||||
goog.require('Blockly.Xml');
|
||||
goog.require('Blockly.ZoomControls');
|
||||
|
||||
@@ -687,7 +687,7 @@ Blockly.Blocks['procedures_callnoreturn'] = {
|
||||
// Block is deleted or is in a flyout.
|
||||
return;
|
||||
}
|
||||
if (event.type == Blockly.Events.CREATE &&
|
||||
if (event.type == Blockly.Events.BLOCK_CREATE &&
|
||||
event.ids.indexOf(this.id) != -1) {
|
||||
// Look for the case where a procedure call was created (usually through
|
||||
// paste) and there is no matching definition. In this case, create
|
||||
@@ -730,7 +730,7 @@ Blockly.Blocks['procedures_callnoreturn'] = {
|
||||
Blockly.Xml.domToWorkspace(xml, this.workspace);
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
} else if (event.type == Blockly.Events.DELETE) {
|
||||
} else if (event.type == Blockly.Events.BLOCK_DELETE) {
|
||||
// Look for the case where a procedure definition has been deleted,
|
||||
// leaving this block (a procedure call) orphaned. In this case, delete
|
||||
// the orphan.
|
||||
|
||||
+409
-368
@@ -57,377 +57,148 @@ Blockly.defineBlocksWithJsonArray([ // BEGIN JSON EXTRACT
|
||||
"text_quotes",
|
||||
"parent_tooltip_when_inline"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text_join",
|
||||
"message0": "",
|
||||
"output": "String",
|
||||
"colour": "%{BKY_TEXTS_HUE}",
|
||||
"helpUrl": "%{BKY_TEXT_JOIN_HELPURL}",
|
||||
"tooltip": "%{BKY_TEXT_JOIN_TOOLTIP}",
|
||||
"mutator": "text_join_mutator"
|
||||
|
||||
},
|
||||
{
|
||||
"type": "text_create_join_container",
|
||||
"message0": "%{BKY_TEXT_CREATE_JOIN_TITLE_JOIN} %1 %2",
|
||||
"args0": [{
|
||||
"type": "input_dummy"
|
||||
},
|
||||
{
|
||||
"type": "input_statement",
|
||||
"name": "STACK"
|
||||
}],
|
||||
"colour": "%{BKY_TEXTS_HUE}",
|
||||
"tooltip": "%{BKY_TEXT_CREATE_JOIN_TOOLTIP}",
|
||||
"enableContextMenu": false
|
||||
},
|
||||
{
|
||||
"type": "text_create_join_item",
|
||||
"message0": "%{BKY_TEXT_CREATE_JOIN_ITEM_TITLE_ITEM}",
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": "%{BKY_TEXTS_HUE}",
|
||||
"tooltip": "{%BKY_TEXT_CREATE_JOIN_ITEM_TOOLTIP}",
|
||||
"enableContextMenu": false
|
||||
},
|
||||
{
|
||||
"type": "text_append",
|
||||
"message0": "%{BKY_TEXT_APPEND_TITLE}",
|
||||
"args0": [{
|
||||
"type": "field_variable",
|
||||
"name": "VAR",
|
||||
"variable": "%{BKY_TEXT_APPEND_VARIABLE}"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "TEXT"
|
||||
}],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": "%{BKY_TEXTS_HUE}",
|
||||
"extensions": [
|
||||
"text_append_tooltip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text_length",
|
||||
"message0": "%{BKY_TEXT_LENGTH_TITLE}",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "VALUE",
|
||||
"check": ['String', 'Array']
|
||||
}
|
||||
],
|
||||
"output": 'Number',
|
||||
"colour": "%{BKY_TEXTS_HUE}",
|
||||
"tooltip": "%{BKY_TEXT_LENGTH_TOOLTIP}",
|
||||
"helpUrl": "%{BKY_TEXT_LENGTH_HELPURL}"
|
||||
},
|
||||
{
|
||||
"type": "text_isEmpty",
|
||||
"message0": "%{BKY_TEXT_ISEMPTY_TITLE}",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "VALUE",
|
||||
"check": ['String', 'Array']
|
||||
}
|
||||
],
|
||||
"output": 'Boolean',
|
||||
"colour": "%{BKY_TEXTS_HUE}",
|
||||
"tooltip": "%{BKY_TEXT_ISEMPTY_TOOLTIP}",
|
||||
"helpUrl": "%{BKY_TEXT_ISEMPTY_HELPURL}"
|
||||
},
|
||||
{
|
||||
"type": "text_indexOf",
|
||||
"message0": "%{BKY_TEXT_INDEXOF_TITLE}",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "VALUE",
|
||||
"check": "String"
|
||||
},
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "END",
|
||||
"options": [
|
||||
[
|
||||
"%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}",
|
||||
"FIRST"
|
||||
],
|
||||
[
|
||||
"%{BKY_TEXT_INDEXOF_OPERATOR_LAST}",
|
||||
"LAST"
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "FIND",
|
||||
"check": "String"
|
||||
}
|
||||
],
|
||||
"output": "Number",
|
||||
"colour": "%{BKY_TEXTS_HUE}",
|
||||
"helpUrl": "%{BKY_TEXT_INDEXOF_HELPURL}",
|
||||
"inputsInline": true,
|
||||
"extensions": [
|
||||
"text_indexOf_tooltip"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "text_charAt",
|
||||
"message0": "%{BKY_TEXT_CHARAT_TITLE}", // "in text %1 %2"
|
||||
"args0": [
|
||||
{
|
||||
"type":"input_value",
|
||||
"name": "VALUE",
|
||||
"check": "String"
|
||||
},
|
||||
{
|
||||
"type": "input_dummy",
|
||||
"name": "AT"
|
||||
}
|
||||
],
|
||||
"output": "String",
|
||||
"colour": "%{BKY_TEXTS_HUE}",
|
||||
"helpUrl": "%{BKY_TEXT_CHARAT_HELPURL}",
|
||||
"inputsInline": true,
|
||||
"mutator": "text_charAt_mutator"
|
||||
}
|
||||
]); // END JSON EXTRACT (Do not delete this comment.)
|
||||
|
||||
/** Wraps TEXT field with images of double quote characters. */
|
||||
Blockly.Constants.Text.textQuotesExtension = function() {
|
||||
this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);
|
||||
this.quoteField_('TEXT');
|
||||
};
|
||||
|
||||
Blockly.Extensions.register('text_quotes',
|
||||
Blockly.Constants.Text.textQuotesExtension);
|
||||
|
||||
Blockly.Blocks['text_join'] = {
|
||||
/**
|
||||
* Block for creating a string made up of any number of elements of any type.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.setHelpUrl(Blockly.Msg.TEXT_JOIN_HELPURL);
|
||||
this.setColour(Blockly.Blocks.texts.HUE);
|
||||
this.itemCount_ = 2;
|
||||
this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);
|
||||
this.updateShape_();
|
||||
this.setOutput(true, 'String');
|
||||
this.setMutator(new Blockly.Mutator(['text_create_join_item']));
|
||||
this.setTooltip(Blockly.Msg.TEXT_JOIN_TOOLTIP);
|
||||
},
|
||||
/**
|
||||
* Create XML to represent number of text inputs.
|
||||
* @return {!Element} XML storage element.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
mutationToDom: function() {
|
||||
var container = document.createElement('mutation');
|
||||
container.setAttribute('items', this.itemCount_);
|
||||
return container;
|
||||
},
|
||||
/**
|
||||
* Parse XML to restore the text inputs.
|
||||
* @param {!Element} xmlElement XML storage element.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
domToMutation: function(xmlElement) {
|
||||
this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
|
||||
this.updateShape_();
|
||||
},
|
||||
/**
|
||||
* Populate the mutator's dialog with this block's components.
|
||||
* @param {!Blockly.Workspace} workspace Mutator's workspace.
|
||||
* @return {!Blockly.Block} Root block in mutator.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
decompose: function(workspace) {
|
||||
var containerBlock = workspace.newBlock('text_create_join_container');
|
||||
containerBlock.initSvg();
|
||||
var connection = containerBlock.getInput('STACK').connection;
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
var itemBlock = workspace.newBlock('text_create_join_item');
|
||||
itemBlock.initSvg();
|
||||
connection.connect(itemBlock.previousConnection);
|
||||
connection = itemBlock.nextConnection;
|
||||
}
|
||||
return containerBlock;
|
||||
},
|
||||
/**
|
||||
* Reconfigure this block based on the mutator dialog's components.
|
||||
* @param {!Blockly.Block} containerBlock Root block in mutator.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
compose: function(containerBlock) {
|
||||
var itemBlock = containerBlock.getInputTargetBlock('STACK');
|
||||
// Count number of inputs.
|
||||
var connections = [];
|
||||
while (itemBlock) {
|
||||
connections.push(itemBlock.valueConnection_);
|
||||
itemBlock = itemBlock.nextConnection &&
|
||||
itemBlock.nextConnection.targetBlock();
|
||||
}
|
||||
// Disconnect any children that don't belong.
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
var connection = this.getInput('ADD' + i).connection.targetConnection;
|
||||
if (connection && connections.indexOf(connection) == -1) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
this.itemCount_ = connections.length;
|
||||
this.updateShape_();
|
||||
// Reconnect any child blocks.
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Store pointers to any connected child blocks.
|
||||
* @param {!Blockly.Block} containerBlock Root block in mutator.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
saveConnections: function(containerBlock) {
|
||||
var itemBlock = containerBlock.getInputTargetBlock('STACK');
|
||||
var i = 0;
|
||||
while (itemBlock) {
|
||||
var input = this.getInput('ADD' + i);
|
||||
itemBlock.valueConnection_ = input && input.connection.targetConnection;
|
||||
i++;
|
||||
itemBlock = itemBlock.nextConnection &&
|
||||
itemBlock.nextConnection.targetBlock();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Modify this block to have the correct number of inputs.
|
||||
* @private
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
updateShape_: function() {
|
||||
if (this.itemCount_ && this.getInput('EMPTY')) {
|
||||
this.removeInput('EMPTY');
|
||||
} else if (!this.itemCount_ && !this.getInput('EMPTY')) {
|
||||
this.appendDummyInput('EMPTY')
|
||||
.appendField(this.newQuote_(true))
|
||||
.appendField(this.newQuote_(false));
|
||||
}
|
||||
// Add new inputs.
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
if (!this.getInput('ADD' + i)) {
|
||||
var input = this.appendValueInput('ADD' + i);
|
||||
if (i == 0) {
|
||||
input.appendField(Blockly.Msg.TEXT_JOIN_TITLE_CREATEWITH);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove deleted inputs.
|
||||
while (this.getInput('ADD' + i)) {
|
||||
this.removeInput('ADD' + i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['text_create_join_container'] = {
|
||||
/**
|
||||
* Mutator block for container.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.setColour(Blockly.Blocks.texts.HUE);
|
||||
this.appendDummyInput()
|
||||
.appendField(Blockly.Msg.TEXT_CREATE_JOIN_TITLE_JOIN);
|
||||
this.appendStatementInput('STACK');
|
||||
this.setTooltip(Blockly.Msg.TEXT_CREATE_JOIN_TOOLTIP);
|
||||
this.contextMenu = false;
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['text_create_join_item'] = {
|
||||
/**
|
||||
* Mutator block for add items.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.setColour(Blockly.Blocks.texts.HUE);
|
||||
this.appendDummyInput()
|
||||
.appendField(Blockly.Msg.TEXT_CREATE_JOIN_ITEM_TITLE_ITEM);
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
this.setTooltip(Blockly.Msg.TEXT_CREATE_JOIN_ITEM_TOOLTIP);
|
||||
this.contextMenu = false;
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['text_append'] = {
|
||||
/**
|
||||
* Block for appending to a variable in place.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.setHelpUrl(Blockly.Msg.TEXT_APPEND_HELPURL);
|
||||
this.setColour(Blockly.Blocks.texts.HUE);
|
||||
this.appendValueInput('TEXT')
|
||||
.appendField(Blockly.Msg.TEXT_APPEND_TO)
|
||||
.appendField(new Blockly.FieldVariable(
|
||||
Blockly.Msg.TEXT_APPEND_VARIABLE), 'VAR')
|
||||
.appendField(Blockly.Msg.TEXT_APPEND_APPENDTEXT);
|
||||
this.setPreviousStatement(true);
|
||||
this.setNextStatement(true);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
var thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
return Blockly.Msg.TEXT_APPEND_TOOLTIP.replace('%1',
|
||||
thisBlock.getFieldValue('VAR'));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['text_length'] = {
|
||||
/**
|
||||
* Block for string length.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
"message0": Blockly.Msg.TEXT_LENGTH_TITLE,
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "VALUE",
|
||||
"check": ['String', 'Array']
|
||||
}
|
||||
],
|
||||
"output": 'Number',
|
||||
"colour": Blockly.Blocks.texts.HUE,
|
||||
"tooltip": Blockly.Msg.TEXT_LENGTH_TOOLTIP,
|
||||
"helpUrl": Blockly.Msg.TEXT_LENGTH_HELPURL
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['text_isEmpty'] = {
|
||||
/**
|
||||
* Block for is the string null?
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.jsonInit({
|
||||
"message0": Blockly.Msg.TEXT_ISEMPTY_TITLE,
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "VALUE",
|
||||
"check": ['String', 'Array']
|
||||
}
|
||||
],
|
||||
"output": 'Boolean',
|
||||
"colour": Blockly.Blocks.texts.HUE,
|
||||
"tooltip": Blockly.Msg.TEXT_ISEMPTY_TOOLTIP,
|
||||
"helpUrl": Blockly.Msg.TEXT_ISEMPTY_HELPURL
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['text_indexOf'] = {
|
||||
/**
|
||||
* Block for finding a substring in the text.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
var OPERATORS = [
|
||||
[Blockly.Msg.TEXT_INDEXOF_OPERATOR_FIRST, 'FIRST'],
|
||||
[Blockly.Msg.TEXT_INDEXOF_OPERATOR_LAST, 'LAST']
|
||||
];
|
||||
this.setHelpUrl(Blockly.Msg.TEXT_INDEXOF_HELPURL);
|
||||
this.setColour(Blockly.Blocks.texts.HUE);
|
||||
this.setOutput(true, 'Number');
|
||||
this.appendValueInput('VALUE')
|
||||
.setCheck('String')
|
||||
.appendField(Blockly.Msg.TEXT_INDEXOF_INPUT_INTEXT);
|
||||
this.appendValueInput('FIND')
|
||||
.setCheck('String')
|
||||
.appendField(new Blockly.FieldDropdown(OPERATORS), 'END');
|
||||
if (Blockly.Msg.TEXT_INDEXOF_TAIL) {
|
||||
this.appendDummyInput().appendField(Blockly.Msg.TEXT_INDEXOF_TAIL);
|
||||
}
|
||||
this.setInputsInline(true);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
var thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
return Blockly.Msg.TEXT_INDEXOF_TOOLTIP.replace('%1',
|
||||
thisBlock.workspace.options.oneBasedIndex ? '0' : '-1');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['text_charAt'] = {
|
||||
/**
|
||||
* Block for getting a character from the string.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
init: function() {
|
||||
this.WHERE_OPTIONS = [
|
||||
[Blockly.Msg.TEXT_CHARAT_FROM_START, 'FROM_START'],
|
||||
[Blockly.Msg.TEXT_CHARAT_FROM_END, 'FROM_END'],
|
||||
[Blockly.Msg.TEXT_CHARAT_FIRST, 'FIRST'],
|
||||
[Blockly.Msg.TEXT_CHARAT_LAST, 'LAST'],
|
||||
[Blockly.Msg.TEXT_CHARAT_RANDOM, 'RANDOM']
|
||||
];
|
||||
this.setHelpUrl(Blockly.Msg.TEXT_CHARAT_HELPURL);
|
||||
this.setColour(Blockly.Blocks.texts.HUE);
|
||||
this.setOutput(true, 'String');
|
||||
this.appendValueInput('VALUE')
|
||||
.setCheck('String')
|
||||
.appendField(Blockly.Msg.TEXT_CHARAT_INPUT_INTEXT);
|
||||
this.appendDummyInput('AT');
|
||||
this.setInputsInline(true);
|
||||
this.updateAt_(true);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
var thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
var where = thisBlock.getFieldValue('WHERE');
|
||||
var tooltip = Blockly.Msg.TEXT_CHARAT_TOOLTIP;
|
||||
if (where == 'FROM_START' || where == 'FROM_END') {
|
||||
var msg = (where == 'FROM_START') ?
|
||||
Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP :
|
||||
Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP;
|
||||
tooltip += ' ' + msg.replace('%1',
|
||||
thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
|
||||
}
|
||||
return tooltip;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Create XML to represent whether there is an 'AT' input.
|
||||
* @return {!Element} XML storage element.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
mutationToDom: function() {
|
||||
var container = document.createElement('mutation');
|
||||
var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE;
|
||||
container.setAttribute('at', isAt);
|
||||
return container;
|
||||
},
|
||||
/**
|
||||
* Parse XML to restore the 'AT' input.
|
||||
* @param {!Element} xmlElement XML storage element.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
domToMutation: function(xmlElement) {
|
||||
// Note: Until January 2013 this block did not have mutations,
|
||||
// so 'at' defaults to true.
|
||||
var isAt = (xmlElement.getAttribute('at') != 'false');
|
||||
this.updateAt_(isAt);
|
||||
},
|
||||
/**
|
||||
* Create or delete an input for the numeric index.
|
||||
* @param {boolean} isAt True if the input should exist.
|
||||
* @private
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
updateAt_: function(isAt) {
|
||||
// Destroy old 'AT' and 'ORDINAL' inputs.
|
||||
this.removeInput('AT');
|
||||
this.removeInput('ORDINAL', true);
|
||||
// Create either a value 'AT' input or a dummy input.
|
||||
if (isAt) {
|
||||
this.appendValueInput('AT').setCheck('Number');
|
||||
if (Blockly.Msg.ORDINAL_NUMBER_SUFFIX) {
|
||||
this.appendDummyInput('ORDINAL')
|
||||
.appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX);
|
||||
}
|
||||
} else {
|
||||
this.appendDummyInput('AT');
|
||||
}
|
||||
if (Blockly.Msg.TEXT_CHARAT_TAIL) {
|
||||
this.removeInput('TAIL', true);
|
||||
this.appendDummyInput('TAIL')
|
||||
.appendField(Blockly.Msg.TEXT_CHARAT_TAIL);
|
||||
}
|
||||
var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function(value) {
|
||||
var newAt = (value == 'FROM_START') || (value == 'FROM_END');
|
||||
// The 'isAt' variable is available due to this function being a closure.
|
||||
if (newAt != isAt) {
|
||||
var block = this.sourceBlock_;
|
||||
block.updateAt_(newAt);
|
||||
// This menu has been destroyed and replaced. Update the replacement.
|
||||
block.setFieldValue(value, 'WHERE');
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
this.getInput('AT').appendField(menu, 'WHERE');
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['text_getSubstring'] = {
|
||||
/**
|
||||
* Block for getting substring.
|
||||
@@ -849,3 +620,273 @@ Blockly.Constants.Text.QUOTE_IMAGE_MIXIN = {
|
||||
}
|
||||
};
|
||||
|
||||
/** Wraps TEXT field with images of double quote characters. */
|
||||
Blockly.Constants.Text.TEXT_QUOTES_EXTENSION = function() {
|
||||
this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);
|
||||
this.quoteField_('TEXT');
|
||||
};
|
||||
|
||||
/**
|
||||
* Mixin for mutator functions in the 'text_join_mutator' extension.
|
||||
* @mixin
|
||||
* @augments Blockly.Block
|
||||
* @package
|
||||
*/
|
||||
Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN = {
|
||||
/**
|
||||
* Create XML to represent number of text inputs.
|
||||
* @return {!Element} XML storage element.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
mutationToDom: function() {
|
||||
var container = document.createElement('mutation');
|
||||
container.setAttribute('items', this.itemCount_);
|
||||
return container;
|
||||
},
|
||||
/**
|
||||
* Parse XML to restore the text inputs.
|
||||
* @param {!Element} xmlElement XML storage element.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
domToMutation: function(xmlElement) {
|
||||
this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
|
||||
this.updateShape_();
|
||||
},
|
||||
/**
|
||||
* Populate the mutator's dialog with this block's components.
|
||||
* @param {!Blockly.Workspace} workspace Mutator's workspace.
|
||||
* @return {!Blockly.Block} Root block in mutator.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
decompose: function(workspace) {
|
||||
var containerBlock = workspace.newBlock('text_create_join_container');
|
||||
containerBlock.initSvg();
|
||||
var connection = containerBlock.getInput('STACK').connection;
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
var itemBlock = workspace.newBlock('text_create_join_item');
|
||||
itemBlock.initSvg();
|
||||
connection.connect(itemBlock.previousConnection);
|
||||
connection = itemBlock.nextConnection;
|
||||
}
|
||||
return containerBlock;
|
||||
},
|
||||
/**
|
||||
* Reconfigure this block based on the mutator dialog's components.
|
||||
* @param {!Blockly.Block} containerBlock Root block in mutator.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
compose: function(containerBlock) {
|
||||
var itemBlock = containerBlock.getInputTargetBlock('STACK');
|
||||
// Count number of inputs.
|
||||
var connections = [];
|
||||
while (itemBlock) {
|
||||
connections.push(itemBlock.valueConnection_);
|
||||
itemBlock = itemBlock.nextConnection &&
|
||||
itemBlock.nextConnection.targetBlock();
|
||||
}
|
||||
// Disconnect any children that don't belong.
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
var connection = this.getInput('ADD' + i).connection.targetConnection;
|
||||
if (connection && connections.indexOf(connection) == -1) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
this.itemCount_ = connections.length;
|
||||
this.updateShape_();
|
||||
// Reconnect any child blocks.
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
Blockly.Mutator.reconnect(connections[i], this, 'ADD' + i);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Store pointers to any connected child blocks.
|
||||
* @param {!Blockly.Block} containerBlock Root block in mutator.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
saveConnections: function(containerBlock) {
|
||||
var itemBlock = containerBlock.getInputTargetBlock('STACK');
|
||||
var i = 0;
|
||||
while (itemBlock) {
|
||||
var input = this.getInput('ADD' + i);
|
||||
itemBlock.valueConnection_ = input && input.connection.targetConnection;
|
||||
i++;
|
||||
itemBlock = itemBlock.nextConnection &&
|
||||
itemBlock.nextConnection.targetBlock();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Modify this block to have the correct number of inputs.
|
||||
* @private
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
updateShape_: function() {
|
||||
if (this.itemCount_ && this.getInput('EMPTY')) {
|
||||
this.removeInput('EMPTY');
|
||||
} else if (!this.itemCount_ && !this.getInput('EMPTY')) {
|
||||
this.appendDummyInput('EMPTY')
|
||||
.appendField(this.newQuote_(true))
|
||||
.appendField(this.newQuote_(false));
|
||||
}
|
||||
// Add new inputs.
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
if (!this.getInput('ADD' + i)) {
|
||||
var input = this.appendValueInput('ADD' + i);
|
||||
if (i == 0) {
|
||||
input.appendField(Blockly.Msg.TEXT_JOIN_TITLE_CREATEWITH);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove deleted inputs.
|
||||
while (this.getInput('ADD' + i)) {
|
||||
this.removeInput('ADD' + i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Performs final setup of a text_join block.
|
||||
Blockly.Constants.Text.TEXT_JOIN_EXTENSION = function() {
|
||||
// Add the quote mixin for the itemCount_ = 0 case.
|
||||
this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);
|
||||
// initialize the mutator values
|
||||
this.itemCount_ = 2;
|
||||
this.updateShape_();
|
||||
// Configure the mutator ui
|
||||
this.setMutator(new Blockly.Mutator(['text_create_join_item']));
|
||||
};
|
||||
|
||||
Blockly.Constants.Text.TEXT_APPEND_TOOLTIP_EXTENSION = function() {
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
var thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
if (Blockly.Msg.TEXT_APPEND_TOOLTIP) {
|
||||
return Blockly.Msg.TEXT_APPEND_TOOLTIP.replace('%1',
|
||||
thisBlock.getFieldValue('VAR'));
|
||||
}
|
||||
return '';
|
||||
});
|
||||
};
|
||||
|
||||
Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION = function() {
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
var thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
return Blockly.Msg.TEXT_INDEXOF_TOOLTIP.replace('%1',
|
||||
thisBlock.workspace.options.oneBasedIndex ? '0' : '-1');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Mixin for mutator functions in the 'text_charAt_mutator' extension.
|
||||
* @mixin
|
||||
* @augments Blockly.Block
|
||||
* @package
|
||||
*/
|
||||
Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN = {
|
||||
/**
|
||||
* Create XML to represent whether there is an 'AT' input.
|
||||
* @return {!Element} XML storage element.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
mutationToDom: function() {
|
||||
var container = document.createElement('mutation');
|
||||
var isAt = this.getInput('AT').type == Blockly.INPUT_VALUE;
|
||||
container.setAttribute('at', isAt);
|
||||
return container;
|
||||
},
|
||||
/**
|
||||
* Parse XML to restore the 'AT' input.
|
||||
* @param {!Element} xmlElement XML storage element.
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
domToMutation: function(xmlElement) {
|
||||
// Note: Until January 2013 this block did not have mutations,
|
||||
// so 'at' defaults to true.
|
||||
var isAt = (xmlElement.getAttribute('at') != 'false');
|
||||
this.updateAt_(isAt);
|
||||
},
|
||||
/**
|
||||
* Create or delete an input for the numeric index.
|
||||
* @param {boolean} isAt True if the input should exist.
|
||||
* @private
|
||||
* @this Blockly.Block
|
||||
*/
|
||||
updateAt_: function(isAt) {
|
||||
// Destroy old 'AT' and 'ORDINAL' inputs.
|
||||
this.removeInput('AT');
|
||||
this.removeInput('ORDINAL', true);
|
||||
// Create either a value 'AT' input or a dummy input.
|
||||
if (isAt) {
|
||||
this.appendValueInput('AT').setCheck('Number');
|
||||
if (Blockly.Msg.ORDINAL_NUMBER_SUFFIX) {
|
||||
this.appendDummyInput('ORDINAL')
|
||||
.appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX);
|
||||
}
|
||||
} else {
|
||||
this.appendDummyInput('AT');
|
||||
}
|
||||
if (Blockly.Msg.TEXT_CHARAT_TAIL) {
|
||||
this.removeInput('TAIL', true);
|
||||
this.appendDummyInput('TAIL')
|
||||
.appendField(Blockly.Msg.TEXT_CHARAT_TAIL);
|
||||
}
|
||||
var menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, function(value) {
|
||||
var newAt = (value == 'FROM_START') || (value == 'FROM_END');
|
||||
// The 'isAt' variable is available due to this function being a closure.
|
||||
if (newAt != isAt) {
|
||||
var block = this.sourceBlock_;
|
||||
block.updateAt_(newAt);
|
||||
// This menu has been destroyed and replaced. Update the replacement.
|
||||
block.setFieldValue(value, 'WHERE');
|
||||
return null;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
this.getInput('AT').appendField(menu, 'WHERE');
|
||||
}
|
||||
};
|
||||
|
||||
// Does the initial mutator update of text_charAt and adds the tooltip
|
||||
Blockly.Constants.Text.TEXT_CHARAT_EXTENSION = function() {
|
||||
this.WHERE_OPTIONS = [
|
||||
[Blockly.Msg.TEXT_CHARAT_FROM_START, 'FROM_START'],
|
||||
[Blockly.Msg.TEXT_CHARAT_FROM_END, 'FROM_END'],
|
||||
[Blockly.Msg.TEXT_CHARAT_FIRST, 'FIRST'],
|
||||
[Blockly.Msg.TEXT_CHARAT_LAST, 'LAST'],
|
||||
[Blockly.Msg.TEXT_CHARAT_RANDOM, 'RANDOM']
|
||||
];
|
||||
this.updateAt_(true);
|
||||
// Assign 'this' to a variable for use in the tooltip closure below.
|
||||
var thisBlock = this;
|
||||
this.setTooltip(function() {
|
||||
var where = thisBlock.getFieldValue('WHERE');
|
||||
var tooltip = Blockly.Msg.TEXT_CHARAT_TOOLTIP;
|
||||
if (where == 'FROM_START' || where == 'FROM_END') {
|
||||
var msg = (where == 'FROM_START') ?
|
||||
Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP :
|
||||
Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP;
|
||||
if (msg) {
|
||||
tooltip += ' ' + msg.replace('%1',
|
||||
thisBlock.workspace.options.oneBasedIndex ? '#1' : '#0');
|
||||
}
|
||||
}
|
||||
return tooltip;
|
||||
});
|
||||
};
|
||||
|
||||
Blockly.Extensions.register('text_indexOf_tooltip',
|
||||
Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION);
|
||||
|
||||
Blockly.Extensions.register('text_quotes',
|
||||
Blockly.Constants.Text.TEXT_QUOTES_EXTENSION);
|
||||
|
||||
Blockly.Extensions.register('text_append_tooltip',
|
||||
Blockly.Constants.Text.TEXT_APPEND_TOOLTIP_EXTENSION);
|
||||
|
||||
Blockly.Extensions.registerMutator('text_join_mutator',
|
||||
Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN,
|
||||
Blockly.Constants.Text.TEXT_JOIN_EXTENSION);
|
||||
|
||||
Blockly.Extensions.registerMutator('text_charAt_mutator',
|
||||
Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN,
|
||||
Blockly.Constants.Text.TEXT_CHARAT_EXTENSION);
|
||||
+25
-25
@@ -68,7 +68,7 @@ mutationToDom:function(a){var b=document.createElement("mutation");a&&b.setAttri
|
||||
this.arguments_.push(c.getAttribute("name"));this.updateParams_();Blockly.Procedures.mutateCallers(this);this.setStatements_("false"!==a.getAttribute("statements"))},decompose:function(a){var b=a.newBlock("procedures_mutatorcontainer");b.initSvg();this.getInput("RETURN")?b.setFieldValue(this.hasStatements_?"TRUE":"FALSE","STATEMENTS"):b.getInput("STATEMENT_INPUT").setVisible(!1);for(var c=b.getInput("STACK").connection,d=0;d<this.arguments_.length;d++){var e=a.newBlock("procedures_mutatorarg");e.initSvg();
|
||||
e.setFieldValue(this.arguments_[d],"NAME");e.oldLocation=d;c.connect(e.previousConnection);c=e.nextConnection}Blockly.Procedures.mutateCallers(this);return b},compose:function(a){this.arguments_=[];this.paramIds_=[];for(var b=a.getInputTargetBlock("STACK");b;)this.arguments_.push(b.getFieldValue("NAME")),this.paramIds_.push(b.id),b=b.nextConnection&&b.nextConnection.targetBlock();this.updateParams_();Blockly.Procedures.mutateCallers(this);a=a.getFieldValue("STATEMENTS");if(null!==a&&(a="TRUE"==a,
|
||||
this.hasStatements_!=a))if(a)this.setStatements_(!0),Blockly.Mutator.reconnect(this.statementConnection_,this,"STACK"),this.statementConnection_=null;else{a=this.getInput("STACK").connection;if(this.statementConnection_=a.targetConnection)a=a.targetBlock(),a.unplug(),a.bumpNeighbours_();this.setStatements_(!1)}},getProcedureDef:function(){return[this.getFieldValue("NAME"),this.arguments_,!1]},getVars:function(){return this.arguments_},renameVar:function(a,b){for(var c=!1,d=0;d<this.arguments_.length;d++)Blockly.Names.equals(a,
|
||||
this.arguments_[d])&&(this.arguments_[d]=b,c=!0);if(c&&(this.updateParams_(),this.mutator.isVisible()))for(var c=this.mutator.workspace_.getAllBlocks(),d=0,e;e=c[d];d++)"procedures_mutatorarg"==e.type&&Blockly.Names.equals(a,e.getFieldValue("NAME"))&&e.setFieldValue(b,"NAME")},customContextMenu:function(a){var b={enabled:!0};var c=this.getFieldValue("NAME");b.text=Blockly.Msg.PROCEDURES_CREATE_DO.replace("%1",c);var d=goog.dom.createDom("mutation");d.setAttribute("name",c);for(var e=0;e<this.arguments_.length;e++)c=
|
||||
this.arguments_[d])&&(this.arguments_[d]=b,c=!0);if(c&&(this.updateParams_(),this.mutator.isVisible())){c=this.mutator.workspace_.getAllBlocks();d=0;for(var e;e=c[d];d++)"procedures_mutatorarg"==e.type&&Blockly.Names.equals(a,e.getFieldValue("NAME"))&&e.setFieldValue(b,"NAME")}},customContextMenu:function(a){var b={enabled:!0};var c=this.getFieldValue("NAME");b.text=Blockly.Msg.PROCEDURES_CREATE_DO.replace("%1",c);var d=goog.dom.createDom("mutation");d.setAttribute("name",c);for(var e=0;e<this.arguments_.length;e++)c=
|
||||
goog.dom.createDom("arg"),c.setAttribute("name",this.arguments_[e]),d.appendChild(c);d=goog.dom.createDom("block",null,d);d.setAttribute("type",this.callType_);b.callback=Blockly.ContextMenu.callbackFactory(this,d);a.push(b);if(!this.isCollapsed())for(e=0;e<this.arguments_.length;e++)b={enabled:!0},c=this.arguments_[e],b.text=Blockly.Msg.VARIABLES_SET_CREATE_GET.replace("%1",c),d=goog.dom.createDom("field",null,c),d.setAttribute("name","VAR"),d=goog.dom.createDom("block",null,d),d.setAttribute("type",
|
||||
"variables_get"),b.callback=Blockly.ContextMenu.callbackFactory(this,d),a.push(b)},callType_:"procedures_callnoreturn"};
|
||||
Blockly.Blocks.procedures_defreturn={init:function(){var a=new Blockly.FieldTextInput("",Blockly.Procedures.rename);a.setSpellcheck(!1);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_DEFRETURN_TITLE).appendField(a,"NAME").appendField("","PARAMS");this.appendValueInput("RETURN").setAlign(Blockly.ALIGN_RIGHT).appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);this.setMutator(new Blockly.Mutator(["procedures_mutatorarg"]));(this.workspace.options.comments||this.workspace.options.parentWorkspace&&
|
||||
@@ -78,36 +78,25 @@ callType_:"procedures_callreturn"};Blockly.Blocks.procedures_mutatorcontainer={i
|
||||
Blockly.Blocks.procedures_mutatorarg={init:function(){var a=new Blockly.FieldTextInput("x",this.validator_);this.appendDummyInput().appendField(Blockly.Msg.PROCEDURES_MUTATORARG_TITLE).appendField(a,"NAME");this.setPreviousStatement(!0);this.setNextStatement(!0);this.setColour(Blockly.Blocks.procedures.HUE);this.setTooltip(Blockly.Msg.PROCEDURES_MUTATORARG_TOOLTIP);this.contextMenu=!1;a.onFinishEditing_=this.createNewVar_;a.onFinishEditing_("x")},validator_:function(a){return(a=a.replace(/[\s\xa0]+/g,
|
||||
" ").replace(/^ | $/g,""))||null},createNewVar_:function(a){var b=this.sourceBlock_;b&&b.workspace&&b.workspace.options&&b.workspace.options.parentWorkspace&&b.workspace.options.parentWorkspace.createVariable(a)}};
|
||||
Blockly.Blocks.procedures_callnoreturn={init:function(){this.appendDummyInput("TOPROW").appendField(this.id,"NAME");this.setPreviousStatement(!0);this.setNextStatement(!0);this.setColour(Blockly.Blocks.procedures.HUE);this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLNORETURN_HELPURL);this.arguments_=[];this.quarkConnections_={};this.quarkIds_=null},getProcedureCall:function(){return this.getFieldValue("NAME")},renameProcedure:function(a,b){Blockly.Names.equals(a,this.getProcedureCall())&&(this.setFieldValue(b,
|
||||
"NAME"),this.setTooltip((this.outputConnection?Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP:Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP).replace("%1",b)))},setProcedureParameters_:function(a,b){var c,d=Blockly.Procedures.getDefinition(this.getProcedureCall(),this.workspace);var e=d&&d.mutator&&d.mutator.isVisible();e||(this.quarkConnections_={},this.quarkIds_=null);if(b)if(goog.array.equals(this.arguments_,a))this.quarkIds_=b;else{if(b.length!=a.length)throw"Error: paramNames and paramIds must be the same length.";
|
||||
"NAME"),this.setTooltip((this.outputConnection?Blockly.Msg.PROCEDURES_CALLRETURN_TOOLTIP:Blockly.Msg.PROCEDURES_CALLNORETURN_TOOLTIP).replace("%1",b)))},setProcedureParameters_:function(a,b){var c,d=Blockly.Procedures.getDefinition(this.getProcedureCall(),this.workspace),e=d&&d.mutator&&d.mutator.isVisible();e||(this.quarkConnections_={},this.quarkIds_=null);if(b)if(goog.array.equals(this.arguments_,a))this.quarkIds_=b;else{if(b.length!=a.length)throw"Error: paramNames and paramIds must be the same length.";
|
||||
this.setCollapsed(!1);this.quarkIds_||(this.quarkConnections_={},a.join("\n")==this.arguments_.join("\n")?this.quarkIds_=b:this.quarkIds_=[]);d=this.rendered;this.rendered=!1;for(var f=0;f<this.arguments_.length;f++)if(c=this.getInput("ARG"+f))c=c.connection.targetConnection,this.quarkConnections_[this.quarkIds_[f]]=c,e&&c&&-1==b.indexOf(this.quarkIds_[f])&&(c.disconnect(),c.getSourceBlock().bumpNeighbours_());this.arguments_=[].concat(a);this.updateShape_();if(this.quarkIds_=b)for(f=0;f<this.arguments_.length;f++)e=
|
||||
this.quarkIds_[f],e in this.quarkConnections_&&(c=this.quarkConnections_[e],Blockly.Mutator.reconnect(c,this,"ARG"+f)||delete this.quarkConnections_[e]);(this.rendered=d)&&this.render()}},updateShape_:function(){for(var a=0;a<this.arguments_.length;a++){var b=this.getField("ARGNAME"+a);if(b){Blockly.Events.disable();try{b.setValue(this.arguments_[a])}finally{Blockly.Events.enable()}}else b=new Blockly.FieldLabel(this.arguments_[a]),this.appendValueInput("ARG"+a).setAlign(Blockly.ALIGN_RIGHT).appendField(b,
|
||||
"ARGNAME"+a).init()}for(;this.getInput("ARG"+a);)this.removeInput("ARG"+a),a++;if(a=this.getInput("TOPROW"))this.arguments_.length?this.getField("WITH")||(a.appendField(Blockly.Msg.PROCEDURES_CALL_BEFORE_PARAMS,"WITH"),a.init()):this.getField("WITH")&&a.removeField("WITH")},mutationToDom:function(){var a=document.createElement("mutation");a.setAttribute("name",this.getProcedureCall());for(var b=0;b<this.arguments_.length;b++){var c=document.createElement("arg");c.setAttribute("name",this.arguments_[b]);
|
||||
a.appendChild(c)}return a},domToMutation:function(a){var b=a.getAttribute("name");this.renameProcedure(this.getProcedureCall(),b);for(var b=[],c=[],d=0,e;e=a.childNodes[d];d++)"arg"==e.nodeName.toLowerCase()&&(b.push(e.getAttribute("name")),c.push(e.getAttribute("paramId")));this.setProcedureParameters_(b,c)},renameVar:function(a,b){for(var c=0;c<this.arguments_.length;c++)Blockly.Names.equals(a,this.arguments_[c])&&(this.arguments_[c]=b,this.getField("ARGNAME"+c).setValue(b))},onchange:function(a){if(this.workspace&&
|
||||
!this.workspace.isFlyout)if(a.type==Blockly.Events.CREATE&&-1!=a.ids.indexOf(this.id)){var b=this.getProcedureCall();b=Blockly.Procedures.getDefinition(b,this.workspace);!b||b.type==this.defType_&&JSON.stringify(b.arguments_)==JSON.stringify(this.arguments_)||(b=null);if(!b){Blockly.Events.setGroup(a.group);a=goog.dom.createDom("xml");b=goog.dom.createDom("block");b.setAttribute("type",this.defType_);var c=this.getRelativeToSurfaceXY(),d=c.y+2*Blockly.SNAP_RADIUS;b.setAttribute("x",c.x+Blockly.SNAP_RADIUS*
|
||||
(this.RTL?-1:1));b.setAttribute("y",d);c=this.mutationToDom();b.appendChild(c);c=goog.dom.createDom("field");c.setAttribute("name","NAME");c.appendChild(document.createTextNode(this.getProcedureCall()));b.appendChild(c);a.appendChild(b);Blockly.Xml.domToWorkspace(a,this.workspace);Blockly.Events.setGroup(!1)}}else a.type==Blockly.Events.DELETE&&(b=this.getProcedureCall(),b=Blockly.Procedures.getDefinition(b,this.workspace),b||(Blockly.Events.setGroup(a.group),this.dispose(!0,!1),Blockly.Events.setGroup(!1)))},
|
||||
a.appendChild(c)}return a},domToMutation:function(a){var b=a.getAttribute("name");this.renameProcedure(this.getProcedureCall(),b);b=[];for(var c=[],d=0,e;e=a.childNodes[d];d++)"arg"==e.nodeName.toLowerCase()&&(b.push(e.getAttribute("name")),c.push(e.getAttribute("paramId")));this.setProcedureParameters_(b,c)},renameVar:function(a,b){for(var c=0;c<this.arguments_.length;c++)Blockly.Names.equals(a,this.arguments_[c])&&(this.arguments_[c]=b,this.getField("ARGNAME"+c).setValue(b))},onchange:function(a){if(this.workspace&&
|
||||
!this.workspace.isFlyout)if(a.type==Blockly.Events.BLOCK_CREATE&&-1!=a.ids.indexOf(this.id)){var b=this.getProcedureCall();b=Blockly.Procedures.getDefinition(b,this.workspace);!b||b.type==this.defType_&&JSON.stringify(b.arguments_)==JSON.stringify(this.arguments_)||(b=null);if(!b){Blockly.Events.setGroup(a.group);a=goog.dom.createDom("xml");b=goog.dom.createDom("block");b.setAttribute("type",this.defType_);var c=this.getRelativeToSurfaceXY(),d=c.y+2*Blockly.SNAP_RADIUS;b.setAttribute("x",c.x+Blockly.SNAP_RADIUS*
|
||||
(this.RTL?-1:1));b.setAttribute("y",d);c=this.mutationToDom();b.appendChild(c);c=goog.dom.createDom("field");c.setAttribute("name","NAME");c.appendChild(document.createTextNode(this.getProcedureCall()));b.appendChild(c);a.appendChild(b);Blockly.Xml.domToWorkspace(a,this.workspace);Blockly.Events.setGroup(!1)}}else a.type==Blockly.Events.BLOCK_DELETE&&(b=this.getProcedureCall(),b=Blockly.Procedures.getDefinition(b,this.workspace),b||(Blockly.Events.setGroup(a.group),this.dispose(!0,!1),Blockly.Events.setGroup(!1)))},
|
||||
customContextMenu:function(a){var b={enabled:!0};b.text=Blockly.Msg.PROCEDURES_HIGHLIGHT_DEF;var c=this.getProcedureCall(),d=this.workspace;b.callback=function(){var a=Blockly.Procedures.getDefinition(c,d);a&&a.select()};a.push(b)},defType_:"procedures_defnoreturn"};
|
||||
Blockly.Blocks.procedures_callreturn={init:function(){this.appendDummyInput("TOPROW").appendField("","NAME");this.setOutput(!0);this.setColour(Blockly.Blocks.procedures.HUE);this.setHelpUrl(Blockly.Msg.PROCEDURES_CALLRETURN_HELPURL);this.arguments_=[];this.quarkConnections_={};this.quarkIds_=null},getProcedureCall:Blockly.Blocks.procedures_callnoreturn.getProcedureCall,renameProcedure:Blockly.Blocks.procedures_callnoreturn.renameProcedure,setProcedureParameters_:Blockly.Blocks.procedures_callnoreturn.setProcedureParameters_,
|
||||
updateShape_:Blockly.Blocks.procedures_callnoreturn.updateShape_,mutationToDom:Blockly.Blocks.procedures_callnoreturn.mutationToDom,domToMutation:Blockly.Blocks.procedures_callnoreturn.domToMutation,renameVar:Blockly.Blocks.procedures_callnoreturn.renameVar,onchange:Blockly.Blocks.procedures_callnoreturn.onchange,customContextMenu:Blockly.Blocks.procedures_callnoreturn.customContextMenu,defType_:"procedures_defreturn"};
|
||||
Blockly.Blocks.procedures_ifreturn={init:function(){this.appendValueInput("CONDITION").setCheck("Boolean").appendField(Blockly.Msg.CONTROLS_IF_MSG_IF);this.appendValueInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN);this.setInputsInline(!0);this.setPreviousStatement(!0);this.setNextStatement(!0);this.setColour(Blockly.Blocks.procedures.HUE);this.setTooltip(Blockly.Msg.PROCEDURES_IFRETURN_TOOLTIP);this.setHelpUrl(Blockly.Msg.PROCEDURES_IFRETURN_HELPURL);this.hasReturnValue_=!0},
|
||||
mutationToDom:function(){var a=document.createElement("mutation");a.setAttribute("value",Number(this.hasReturnValue_));return a},domToMutation:function(a){this.hasReturnValue_=1==a.getAttribute("value");this.hasReturnValue_||(this.removeInput("VALUE"),this.appendDummyInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN))},onchange:function(){if(this.workspace.isDragging&&!this.workspace.isDragging()){var a=!1,b=this;do{if(-1!=this.FUNCTION_TYPES.indexOf(b.type)){a=!0;break}b=b.getSurroundParent()}while(b);
|
||||
a?("procedures_defnoreturn"==b.type&&this.hasReturnValue_?(this.removeInput("VALUE"),this.appendDummyInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN),this.hasReturnValue_=!1):"procedures_defreturn"!=b.type||this.hasReturnValue_||(this.removeInput("VALUE"),this.appendValueInput("VALUE").appendField(Blockly.Msg.PROCEDURES_DEFRETURN_RETURN),this.hasReturnValue_=!0),this.setWarningText(null),this.isInFlyout||this.setDisabled(!1)):(this.setWarningText(Blockly.Msg.PROCEDURES_IFRETURN_WARNING),
|
||||
this.isInFlyout||this.getInheritedDisabled()||this.setDisabled(!0))}},FUNCTION_TYPES:["procedures_defnoreturn","procedures_defreturn"]};Blockly.Blocks.texts={};Blockly.Constants.Text={};Blockly.Constants.Text.HUE=160;Blockly.Blocks.texts.HUE=Blockly.Constants.Text.HUE;Blockly.defineBlocksWithJsonArray([{type:"text",message0:"%1",args0:[{type:"field_input",name:"TEXT",text:""}],output:"String",colour:"%{BKY_TEXTS_HUE}",helpUrl:"%{BKY_TEXT_TEXT_HELPURL}",tooltip:"%{BKY_TEXT_TEXT_TOOLTIP}",extensions:["text_quotes","parent_tooltip_when_inline"]}]);
|
||||
Blockly.Constants.Text.textQuotesExtension=function(){this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);this.quoteField_("TEXT")};Blockly.Extensions.register("text_quotes",Blockly.Constants.Text.textQuotesExtension);
|
||||
Blockly.Blocks.text_join={init:function(){this.setHelpUrl(Blockly.Msg.TEXT_JOIN_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);this.itemCount_=2;this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);this.updateShape_();this.setOutput(!0,"String");this.setMutator(new Blockly.Mutator(["text_create_join_item"]));this.setTooltip(Blockly.Msg.TEXT_JOIN_TOOLTIP)},mutationToDom:function(){var a=document.createElement("mutation");a.setAttribute("items",this.itemCount_);return a},domToMutation:function(a){this.itemCount_=
|
||||
parseInt(a.getAttribute("items"),10);this.updateShape_()},decompose:function(a){var b=a.newBlock("text_create_join_container");b.initSvg();for(var c=b.getInput("STACK").connection,d=0;d<this.itemCount_;d++){var e=a.newBlock("text_create_join_item");e.initSvg();c.connect(e.previousConnection);c=e.nextConnection}return b},compose:function(a){var b=a.getInputTargetBlock("STACK");for(a=[];b;)a.push(b.valueConnection_),b=b.nextConnection&&b.nextConnection.targetBlock();for(b=0;b<this.itemCount_;b++){var c=
|
||||
this.getInput("ADD"+b).connection.targetConnection;c&&-1==a.indexOf(c)&&c.disconnect()}this.itemCount_=a.length;this.updateShape_();for(b=0;b<this.itemCount_;b++)Blockly.Mutator.reconnect(a[b],this,"ADD"+b)},saveConnections:function(a){a=a.getInputTargetBlock("STACK");for(var b=0;a;){var c=this.getInput("ADD"+b);a.valueConnection_=c&&c.connection.targetConnection;b++;a=a.nextConnection&&a.nextConnection.targetBlock()}},updateShape_:function(){this.itemCount_&&this.getInput("EMPTY")?this.removeInput("EMPTY"):
|
||||
this.itemCount_||this.getInput("EMPTY")||this.appendDummyInput("EMPTY").appendField(this.newQuote_(!0)).appendField(this.newQuote_(!1));for(var a=0;a<this.itemCount_;a++)if(!this.getInput("ADD"+a)){var b=this.appendValueInput("ADD"+a);0==a&&b.appendField(Blockly.Msg.TEXT_JOIN_TITLE_CREATEWITH)}for(;this.getInput("ADD"+a);)this.removeInput("ADD"+a),a++}};
|
||||
Blockly.Blocks.text_create_join_container={init:function(){this.setColour(Blockly.Blocks.texts.HUE);this.appendDummyInput().appendField(Blockly.Msg.TEXT_CREATE_JOIN_TITLE_JOIN);this.appendStatementInput("STACK");this.setTooltip(Blockly.Msg.TEXT_CREATE_JOIN_TOOLTIP);this.contextMenu=!1}};
|
||||
Blockly.Blocks.text_create_join_item={init:function(){this.setColour(Blockly.Blocks.texts.HUE);this.appendDummyInput().appendField(Blockly.Msg.TEXT_CREATE_JOIN_ITEM_TITLE_ITEM);this.setPreviousStatement(!0);this.setNextStatement(!0);this.setTooltip(Blockly.Msg.TEXT_CREATE_JOIN_ITEM_TOOLTIP);this.contextMenu=!1}};
|
||||
Blockly.Blocks.text_append={init:function(){this.setHelpUrl(Blockly.Msg.TEXT_APPEND_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);this.appendValueInput("TEXT").appendField(Blockly.Msg.TEXT_APPEND_TO).appendField(new Blockly.FieldVariable(Blockly.Msg.TEXT_APPEND_VARIABLE),"VAR").appendField(Blockly.Msg.TEXT_APPEND_APPENDTEXT);this.setPreviousStatement(!0);this.setNextStatement(!0);var a=this;this.setTooltip(function(){return Blockly.Msg.TEXT_APPEND_TOOLTIP.replace("%1",a.getFieldValue("VAR"))})}};
|
||||
Blockly.Blocks.text_length={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_LENGTH_TITLE,args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Number",colour:Blockly.Blocks.texts.HUE,tooltip:Blockly.Msg.TEXT_LENGTH_TOOLTIP,helpUrl:Blockly.Msg.TEXT_LENGTH_HELPURL})}};
|
||||
Blockly.Blocks.text_isEmpty={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_ISEMPTY_TITLE,args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Boolean",colour:Blockly.Blocks.texts.HUE,tooltip:Blockly.Msg.TEXT_ISEMPTY_TOOLTIP,helpUrl:Blockly.Msg.TEXT_ISEMPTY_HELPURL})}};
|
||||
Blockly.Blocks.text_indexOf={init:function(){var a=[[Blockly.Msg.TEXT_INDEXOF_OPERATOR_FIRST,"FIRST"],[Blockly.Msg.TEXT_INDEXOF_OPERATOR_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.TEXT_INDEXOF_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);this.setOutput(!0,"Number");this.appendValueInput("VALUE").setCheck("String").appendField(Blockly.Msg.TEXT_INDEXOF_INPUT_INTEXT);this.appendValueInput("FIND").setCheck("String").appendField(new Blockly.FieldDropdown(a),"END");Blockly.Msg.TEXT_INDEXOF_TAIL&&this.appendDummyInput().appendField(Blockly.Msg.TEXT_INDEXOF_TAIL);
|
||||
this.setInputsInline(!0);var b=this;this.setTooltip(function(){return Blockly.Msg.TEXT_INDEXOF_TOOLTIP.replace("%1",b.workspace.options.oneBasedIndex?"0":"-1")})}};
|
||||
Blockly.Blocks.text_charAt={init:function(){this.WHERE_OPTIONS=[[Blockly.Msg.TEXT_CHARAT_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_CHARAT_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_CHARAT_FIRST,"FIRST"],[Blockly.Msg.TEXT_CHARAT_LAST,"LAST"],[Blockly.Msg.TEXT_CHARAT_RANDOM,"RANDOM"]];this.setHelpUrl(Blockly.Msg.TEXT_CHARAT_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);this.setOutput(!0,"String");this.appendValueInput("VALUE").setCheck("String").appendField(Blockly.Msg.TEXT_CHARAT_INPUT_INTEXT);this.appendDummyInput("AT");
|
||||
this.setInputsInline(!0);this.updateAt_(!0);var a=this;this.setTooltip(function(){var b=a.getFieldValue("WHERE"),c=Blockly.Msg.TEXT_CHARAT_TOOLTIP;if("FROM_START"==b||"FROM_END"==b)c+=" "+("FROM_START"==b?Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP:Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP).replace("%1",a.workspace.options.oneBasedIndex?"#1":"#0");return c})},mutationToDom:function(){var a=document.createElement("mutation"),b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},
|
||||
domToMutation:function(a){a="false"!=a.getAttribute("at");this.updateAt_(a)},updateAt_:function(a){this.removeInput("AT");this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):this.appendDummyInput("AT");Blockly.Msg.TEXT_CHARAT_TAIL&&(this.removeInput("TAIL",!0),this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_CHARAT_TAIL));var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,
|
||||
function(b){var c="FROM_START"==b||"FROM_END"==b;if(c!=a){var e=this.sourceBlock_;e.updateAt_(c);e.setFieldValue(b,"WHERE");return null}});this.getInput("AT").appendField(b,"WHERE")}};
|
||||
this.isInFlyout||this.getInheritedDisabled()||this.setDisabled(!0))}},FUNCTION_TYPES:["procedures_defnoreturn","procedures_defreturn"]};Blockly.Blocks.texts={};Blockly.Constants.Text={};Blockly.Constants.Text.HUE=160;Blockly.Blocks.texts.HUE=Blockly.Constants.Text.HUE;
|
||||
Blockly.defineBlocksWithJsonArray([{type:"text",message0:"%1",args0:[{type:"field_input",name:"TEXT",text:""}],output:"String",colour:"%{BKY_TEXTS_HUE}",helpUrl:"%{BKY_TEXT_TEXT_HELPURL}",tooltip:"%{BKY_TEXT_TEXT_TOOLTIP}",extensions:["text_quotes","parent_tooltip_when_inline"]},{type:"text_join",message0:"",output:"String",colour:"%{BKY_TEXTS_HUE}",helpUrl:"%{BKY_TEXT_JOIN_HELPURL}",tooltip:"%{BKY_TEXT_JOIN_TOOLTIP}",mutator:"text_join_mutator"},{type:"text_create_join_container",message0:"%{BKY_TEXT_CREATE_JOIN_TITLE_JOIN} %1 %2",
|
||||
args0:[{type:"input_dummy"},{type:"input_statement",name:"STACK"}],colour:"%{BKY_TEXTS_HUE}",tooltip:"%{BKY_TEXT_CREATE_JOIN_TOOLTIP}",enableContextMenu:!1},{type:"text_create_join_item",message0:"%{BKY_TEXT_CREATE_JOIN_ITEM_TITLE_ITEM}",previousStatement:null,nextStatement:null,colour:"%{BKY_TEXTS_HUE}",tooltip:"{%BKY_TEXT_CREATE_JOIN_ITEM_TOOLTIP}",enableContextMenu:!1},{type:"text_append",message0:"%{BKY_TEXT_APPEND_TITLE}",args0:[{type:"field_variable",name:"VAR",variable:"%{BKY_TEXT_APPEND_VARIABLE}"},
|
||||
{type:"input_value",name:"TEXT"}],previousStatement:null,nextStatement:null,colour:"%{BKY_TEXTS_HUE}",extensions:["text_append_tooltip"]},{type:"text_length",message0:"%{BKY_TEXT_LENGTH_TITLE}",args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],output:"Number",colour:"%{BKY_TEXTS_HUE}",tooltip:"%{BKY_TEXT_LENGTH_TOOLTIP}",helpUrl:"%{BKY_TEXT_LENGTH_HELPURL}"},{type:"text_isEmpty",message0:"%{BKY_TEXT_ISEMPTY_TITLE}",args0:[{type:"input_value",name:"VALUE",check:["String","Array"]}],
|
||||
output:"Boolean",colour:"%{BKY_TEXTS_HUE}",tooltip:"%{BKY_TEXT_ISEMPTY_TOOLTIP}",helpUrl:"%{BKY_TEXT_ISEMPTY_HELPURL}"},{type:"text_indexOf",message0:"%{BKY_TEXT_INDEXOF_TITLE}",args0:[{type:"input_value",name:"VALUE",check:"String"},{type:"field_dropdown",name:"END",options:[["%{BKY_TEXT_INDEXOF_OPERATOR_FIRST}","FIRST"],["%{BKY_TEXT_INDEXOF_OPERATOR_LAST}","LAST"]]},{type:"input_value",name:"FIND",check:"String"}],output:"Number",colour:"%{BKY_TEXTS_HUE}",helpUrl:"%{BKY_TEXT_INDEXOF_HELPURL}",inputsInline:!0,
|
||||
extensions:["text_indexOf_tooltip"]},{type:"text_charAt",message0:"%{BKY_TEXT_CHARAT_TITLE}",args0:[{type:"input_value",name:"VALUE",check:"String"},{type:"input_dummy",name:"AT"}],output:"String",colour:"%{BKY_TEXTS_HUE}",helpUrl:"%{BKY_TEXT_CHARAT_HELPURL}",inputsInline:!0,mutator:"text_charAt_mutator"}]);
|
||||
Blockly.Blocks.text_getSubstring={init:function(){this.WHERE_OPTIONS_1=[[Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_GET_SUBSTRING_START_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_GET_SUBSTRING_START_FIRST,"FIRST"]];this.WHERE_OPTIONS_2=[[Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_GET_SUBSTRING_END_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_GET_SUBSTRING_END_LAST,"LAST"]];this.setHelpUrl(Blockly.Msg.TEXT_GET_SUBSTRING_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);
|
||||
this.appendValueInput("STRING").setCheck("String").appendField(Blockly.Msg.TEXT_GET_SUBSTRING_INPUT_IN_TEXT);this.appendDummyInput("AT1");this.appendDummyInput("AT2");Blockly.Msg.TEXT_GET_SUBSTRING_TAIL&&this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_GET_SUBSTRING_TAIL);this.setInputsInline(!0);this.setOutput(!0,"String");this.updateAt_(1,!0);this.updateAt_(2,!0);this.setTooltip(Blockly.Msg.TEXT_GET_SUBSTRING_TOOLTIP)},mutationToDom:function(){var a=document.createElement("mutation"),
|
||||
b=this.getInput("AT1").type==Blockly.INPUT_VALUE;a.setAttribute("at1",b);b=this.getInput("AT2").type==Blockly.INPUT_VALUE;a.setAttribute("at2",b);return a},domToMutation:function(a){var b="true"==a.getAttribute("at1");a="true"==a.getAttribute("at2");this.updateAt_(1,b);this.updateAt_(2,a)},updateAt_:function(a,b){this.removeInput("AT"+a);this.removeInput("ORDINAL"+a,!0);b?(this.appendValueInput("AT"+a).setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL"+a).appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):
|
||||
@@ -115,7 +104,7 @@ this.appendDummyInput("AT"+a);2==a&&Blockly.Msg.TEXT_GET_SUBSTRING_TAIL&&(this.r
|
||||
Blockly.Blocks.text_changeCase={init:function(){var a=[[Blockly.Msg.TEXT_CHANGECASE_OPERATOR_UPPERCASE,"UPPERCASE"],[Blockly.Msg.TEXT_CHANGECASE_OPERATOR_LOWERCASE,"LOWERCASE"],[Blockly.Msg.TEXT_CHANGECASE_OPERATOR_TITLECASE,"TITLECASE"]];this.setHelpUrl(Blockly.Msg.TEXT_CHANGECASE_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);this.appendValueInput("TEXT").setCheck("String").appendField(new Blockly.FieldDropdown(a),"CASE");this.setOutput(!0,"String");this.setTooltip(Blockly.Msg.TEXT_CHANGECASE_TOOLTIP)}};
|
||||
Blockly.Blocks.text_trim={init:function(){var a=[[Blockly.Msg.TEXT_TRIM_OPERATOR_BOTH,"BOTH"],[Blockly.Msg.TEXT_TRIM_OPERATOR_LEFT,"LEFT"],[Blockly.Msg.TEXT_TRIM_OPERATOR_RIGHT,"RIGHT"]];this.setHelpUrl(Blockly.Msg.TEXT_TRIM_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);this.appendValueInput("TEXT").setCheck("String").appendField(new Blockly.FieldDropdown(a),"MODE");this.setOutput(!0,"String");this.setTooltip(Blockly.Msg.TEXT_TRIM_TOOLTIP)}};
|
||||
Blockly.Blocks.text_print={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_PRINT_TITLE,args0:[{type:"input_value",name:"TEXT"}],previousStatement:null,nextStatement:null,colour:Blockly.Blocks.texts.HUE,tooltip:Blockly.Msg.TEXT_PRINT_TOOLTIP,helpUrl:Blockly.Msg.TEXT_PRINT_HELPURL})}};
|
||||
Blockly.Blocks.text_prompt_ext={init:function(){var a=[[Blockly.Msg.TEXT_PROMPT_TYPE_TEXT,"TEXT"],[Blockly.Msg.TEXT_PROMPT_TYPE_NUMBER,"NUMBER"]];this.setHelpUrl(Blockly.Msg.TEXT_PROMPT_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);var b=this,a=new Blockly.FieldDropdown(a,function(a){b.updateType_(a)});this.appendValueInput("TEXT").appendField(a,"TYPE");this.setOutput(!0,"String");this.setTooltip(function(){return"TEXT"==b.getFieldValue("TYPE")?Blockly.Msg.TEXT_PROMPT_TOOLTIP_TEXT:Blockly.Msg.TEXT_PROMPT_TOOLTIP_NUMBER})},
|
||||
Blockly.Blocks.text_prompt_ext={init:function(){var a=[[Blockly.Msg.TEXT_PROMPT_TYPE_TEXT,"TEXT"],[Blockly.Msg.TEXT_PROMPT_TYPE_NUMBER,"NUMBER"]];this.setHelpUrl(Blockly.Msg.TEXT_PROMPT_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);var b=this;a=new Blockly.FieldDropdown(a,function(a){b.updateType_(a)});this.appendValueInput("TEXT").appendField(a,"TYPE");this.setOutput(!0,"String");this.setTooltip(function(){return"TEXT"==b.getFieldValue("TYPE")?Blockly.Msg.TEXT_PROMPT_TOOLTIP_TEXT:Blockly.Msg.TEXT_PROMPT_TOOLTIP_NUMBER})},
|
||||
updateType_:function(a){this.outputConnection.setCheck("NUMBER"==a?"Number":"String")},mutationToDom:function(){var a=document.createElement("mutation");a.setAttribute("type",this.getFieldValue("TYPE"));return a},domToMutation:function(a){this.updateType_(a.getAttribute("type"))}};
|
||||
Blockly.Blocks.text_prompt={init:function(){this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);var a=[[Blockly.Msg.TEXT_PROMPT_TYPE_TEXT,"TEXT"],[Blockly.Msg.TEXT_PROMPT_TYPE_NUMBER,"NUMBER"]],b=this;this.setHelpUrl(Blockly.Msg.TEXT_PROMPT_HELPURL);this.setColour(Blockly.Blocks.texts.HUE);a=new Blockly.FieldDropdown(a,function(a){b.updateType_(a)});this.appendDummyInput().appendField(a,"TYPE").appendField(this.newQuote_(!0)).appendField(new Blockly.FieldTextInput(""),"TEXT").appendField(this.newQuote_(!1));
|
||||
this.setOutput(!0,"String");this.setTooltip(function(){return"TEXT"==b.getFieldValue("TYPE")?Blockly.Msg.TEXT_PROMPT_TOOLTIP_TEXT:Blockly.Msg.TEXT_PROMPT_TOOLTIP_NUMBER})},updateType_:Blockly.Blocks.text_prompt_ext.updateType_,mutationToDom:Blockly.Blocks.text_prompt_ext.mutationToDom,domToMutation:Blockly.Blocks.text_prompt_ext.domToMutation};
|
||||
@@ -123,7 +112,18 @@ Blockly.Blocks.text_count={init:function(){this.jsonInit({message0:Blockly.Msg.T
|
||||
Blockly.Blocks.text_replace={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_REPLACE_MESSAGE0,args0:[{type:"input_value",name:"FROM",check:"String"},{type:"input_value",name:"TO",check:"String"},{type:"input_value",name:"TEXT",check:"String"}],output:"String",inputsInline:!0,colour:Blockly.Blocks.texts.HUE,tooltip:Blockly.Msg.TEXT_REPLACE_TOOLTIP,helpUrl:Blockly.Msg.TEXT_REPLACE_HELPURL})}};
|
||||
Blockly.Blocks.text_reverse={init:function(){this.jsonInit({message0:Blockly.Msg.TEXT_REVERSE_MESSAGE0,args0:[{type:"input_value",name:"TEXT",check:"String"}],output:"String",inputsInline:!0,colour:Blockly.Blocks.texts.HUE,tooltip:Blockly.Msg.TEXT_REVERSE_TOOLTIP,helpUrl:Blockly.Msg.TEXT_REVERSE_HELPURL})}};
|
||||
Blockly.Constants.Text.QUOTE_IMAGE_MIXIN={QUOTE_IMAGE_LEFT_DATAURI:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAAn0lEQVQI1z3OMa5BURSF4f/cQhAKjUQhuQmFNwGJEUi0RKN5rU7FHKhpjEH3TEMtkdBSCY1EIv8r7nFX9e29V7EBAOvu7RPjwmWGH/VuF8CyN9/OAdvqIXYLvtRaNjx9mMTDyo+NjAN1HNcl9ZQ5oQMM3dgDUqDo1l8DzvwmtZN7mnD+PkmLa+4mhrxVA9fRowBWmVBhFy5gYEjKMfz9AylsaRRgGzvZAAAAAElFTkSuQmCC",QUOTE_IMAGE_RIGHT_DATAURI:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAKCAQAAAAqJXdxAAAAqUlEQVQI1z3KvUpCcRiA8ef9E4JNHhI0aFEacm1o0BsI0Slx8wa8gLauoDnoBhq7DcfWhggONDmJJgqCPA7neJ7p934EOOKOnM8Q7PDElo/4x4lFb2DmuUjcUzS3URnGib9qaPNbuXvBO3sGPHJDRG6fGVdMSeWDP2q99FQdFrz26Gu5Tq7dFMzUvbXy8KXeAj57cOklgA+u1B5AoslLtGIHQMaCVnwDnADZIFIrXsoXrgAAAABJRU5ErkJggg==",
|
||||
QUOTE_IMAGE_WIDTH:12,QUOTE_IMAGE_HEIGHT:12,quoteField_:function(a){for(var b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)if(a==e.name){c.insertFieldAt(d,this.newQuote_(!0));c.insertFieldAt(d+2,this.newQuote_(!1));return}console.warn('field named "'+a+'" not found in '+this.toDevString())},newQuote_:function(a){a=this.RTL?!a:a;return new Blockly.FieldImage(a?this.QUOTE_IMAGE_LEFT_DATAURI:this.QUOTE_IMAGE_RIGHT_DATAURI,this.QUOTE_IMAGE_WIDTH,this.QUOTE_IMAGE_HEIGHT,a?"\u201c":"\u201d")}};Blockly.Blocks.loops={};Blockly.Constants.Loops={};Blockly.Constants.Loops.HUE=120;Blockly.Blocks.loops.HUE=Blockly.Constants.Loops.HUE;
|
||||
QUOTE_IMAGE_WIDTH:12,QUOTE_IMAGE_HEIGHT:12,quoteField_:function(a){for(var b=0,c;c=this.inputList[b];b++)for(var d=0,e;e=c.fieldRow[d];d++)if(a==e.name){c.insertFieldAt(d,this.newQuote_(!0));c.insertFieldAt(d+2,this.newQuote_(!1));return}console.warn('field named "'+a+'" not found in '+this.toDevString())},newQuote_:function(a){a=this.RTL?!a:a;return new Blockly.FieldImage(a?this.QUOTE_IMAGE_LEFT_DATAURI:this.QUOTE_IMAGE_RIGHT_DATAURI,this.QUOTE_IMAGE_WIDTH,this.QUOTE_IMAGE_HEIGHT,a?"\u201c":"\u201d")}};
|
||||
Blockly.Constants.Text.TEXT_QUOTES_EXTENSION=function(){this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);this.quoteField_("TEXT")};
|
||||
Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN={mutationToDom:function(){var a=document.createElement("mutation");a.setAttribute("items",this.itemCount_);return a},domToMutation:function(a){this.itemCount_=parseInt(a.getAttribute("items"),10);this.updateShape_()},decompose:function(a){var b=a.newBlock("text_create_join_container");b.initSvg();for(var c=b.getInput("STACK").connection,d=0;d<this.itemCount_;d++){var e=a.newBlock("text_create_join_item");e.initSvg();c.connect(e.previousConnection);c=
|
||||
e.nextConnection}return b},compose:function(a){var b=a.getInputTargetBlock("STACK");for(a=[];b;)a.push(b.valueConnection_),b=b.nextConnection&&b.nextConnection.targetBlock();for(b=0;b<this.itemCount_;b++){var c=this.getInput("ADD"+b).connection.targetConnection;c&&-1==a.indexOf(c)&&c.disconnect()}this.itemCount_=a.length;this.updateShape_();for(b=0;b<this.itemCount_;b++)Blockly.Mutator.reconnect(a[b],this,"ADD"+b)},saveConnections:function(a){a=a.getInputTargetBlock("STACK");for(var b=0;a;){var c=
|
||||
this.getInput("ADD"+b);a.valueConnection_=c&&c.connection.targetConnection;b++;a=a.nextConnection&&a.nextConnection.targetBlock()}},updateShape_:function(){this.itemCount_&&this.getInput("EMPTY")?this.removeInput("EMPTY"):this.itemCount_||this.getInput("EMPTY")||this.appendDummyInput("EMPTY").appendField(this.newQuote_(!0)).appendField(this.newQuote_(!1));for(var a=0;a<this.itemCount_;a++)if(!this.getInput("ADD"+a)){var b=this.appendValueInput("ADD"+a);0==a&&b.appendField(Blockly.Msg.TEXT_JOIN_TITLE_CREATEWITH)}for(;this.getInput("ADD"+
|
||||
a);)this.removeInput("ADD"+a),a++}};Blockly.Constants.Text.TEXT_JOIN_EXTENSION=function(){this.mixin(Blockly.Constants.Text.QUOTE_IMAGE_MIXIN);this.itemCount_=2;this.updateShape_();this.setMutator(new Blockly.Mutator(["text_create_join_item"]))};Blockly.Constants.Text.TEXT_APPEND_TOOLTIP_EXTENSION=function(){var a=this;this.setTooltip(function(){return Blockly.Msg.TEXT_APPEND_TOOLTIP?Blockly.Msg.TEXT_APPEND_TOOLTIP.replace("%1",a.getFieldValue("VAR")):""})};
|
||||
Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION=function(){var a=this;this.setTooltip(function(){return Blockly.Msg.TEXT_INDEXOF_TOOLTIP.replace("%1",a.workspace.options.oneBasedIndex?"0":"-1")})};
|
||||
Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN={mutationToDom:function(){var a=document.createElement("mutation"),b=this.getInput("AT").type==Blockly.INPUT_VALUE;a.setAttribute("at",b);return a},domToMutation:function(a){a="false"!=a.getAttribute("at");this.updateAt_(a)},updateAt_:function(a){this.removeInput("AT");this.removeInput("ORDINAL",!0);a?(this.appendValueInput("AT").setCheck("Number"),Blockly.Msg.ORDINAL_NUMBER_SUFFIX&&this.appendDummyInput("ORDINAL").appendField(Blockly.Msg.ORDINAL_NUMBER_SUFFIX)):
|
||||
this.appendDummyInput("AT");Blockly.Msg.TEXT_CHARAT_TAIL&&(this.removeInput("TAIL",!0),this.appendDummyInput("TAIL").appendField(Blockly.Msg.TEXT_CHARAT_TAIL));var b=new Blockly.FieldDropdown(this.WHERE_OPTIONS,function(b){var c="FROM_START"==b||"FROM_END"==b;if(c!=a){var e=this.sourceBlock_;e.updateAt_(c);e.setFieldValue(b,"WHERE");return null}});this.getInput("AT").appendField(b,"WHERE")}};
|
||||
Blockly.Constants.Text.TEXT_CHARAT_EXTENSION=function(){this.WHERE_OPTIONS=[[Blockly.Msg.TEXT_CHARAT_FROM_START,"FROM_START"],[Blockly.Msg.TEXT_CHARAT_FROM_END,"FROM_END"],[Blockly.Msg.TEXT_CHARAT_FIRST,"FIRST"],[Blockly.Msg.TEXT_CHARAT_LAST,"LAST"],[Blockly.Msg.TEXT_CHARAT_RANDOM,"RANDOM"]];this.updateAt_(!0);var a=this;this.setTooltip(function(){var b=a.getFieldValue("WHERE"),c=Blockly.Msg.TEXT_CHARAT_TOOLTIP;("FROM_START"==b||"FROM_END"==b)&&(b="FROM_START"==b?Blockly.Msg.LISTS_INDEX_FROM_START_TOOLTIP:
|
||||
Blockly.Msg.LISTS_INDEX_FROM_END_TOOLTIP)&&(c+=" "+b.replace("%1",a.workspace.options.oneBasedIndex?"#1":"#0"));return c})};Blockly.Extensions.register("text_indexOf_tooltip",Blockly.Constants.Text.TEXT_INDEXOF_TOOLTIP_EXTENSION);Blockly.Extensions.register("text_quotes",Blockly.Constants.Text.TEXT_QUOTES_EXTENSION);Blockly.Extensions.register("text_append_tooltip",Blockly.Constants.Text.TEXT_APPEND_TOOLTIP_EXTENSION);
|
||||
Blockly.Extensions.registerMutator("text_join_mutator",Blockly.Constants.Text.TEXT_JOIN_MUTATOR_MIXIN,Blockly.Constants.Text.TEXT_JOIN_EXTENSION);Blockly.Extensions.registerMutator("text_charAt_mutator",Blockly.Constants.Text.TEXT_CHARAT_MUTATOR_MIXIN,Blockly.Constants.Text.TEXT_CHARAT_EXTENSION);Blockly.Blocks.loops={};Blockly.Constants.Loops={};Blockly.Constants.Loops.HUE=120;Blockly.Blocks.loops.HUE=Blockly.Constants.Loops.HUE;
|
||||
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,colour:"%{BKY_LOOPS_HUE}",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,colour:"%{BKY_LOOPS_HUE}",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,colour:"%{BKY_LOOPS_HUE}",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",
|
||||
@@ -150,7 +150,7 @@ break;case "controls_if_else":this.elseCount_++;d=b.statementConnection_;break;d
|
||||
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"Unknown block type.";}a=a.nextConnection&&a.nextConnection.targetBlock()}},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)}};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.fixLogicCompareRtlOpLabels=function(){var a={LT:"\u200f<\u200f",LTE:"\u200f\u2264\u200f",GT:"\u200f>\u200f",GTE:"\u200f\u2265\u200f"},b=this.getField("OP");if(b)for(var b=b.getOptions(),c=0;c<b.length;++c){var d=b[c],e=a[d[1]];goog.isString(d[0])&&e&&(d[0]=e)}};
|
||||
Blockly.Constants.Logic.fixLogicCompareRtlOpLabels=function(){var a={LT:"\u200f<\u200f",LTE:"\u200f\u2264\u200f",GT:"\u200f>\u200f",GTE:"\u200f\u2265\u200f"},b=this.getField("OP");if(b){b=b.getOptions();for(var c=0;c<b.length;++c){var d=b[c],e=a[d[1]];goog.isString(d[0])&&e&&(d[0]=e)}}};
|
||||
Blockly.Constants.Logic.LOGIC_COMPARE_ONCHANGE_MIXIN={prevBlocks_:[null,null],onchange:function(a){var b=this.getInputTargetBlock("A"),c=this.getInputTargetBlock("B");if(b&&c&&!b.outputConnection.checkType_(c.outputConnection)){Blockly.Events.setGroup(a.group);for(a=0;a<this.prevBlocks_.length;a++){var d=this.prevBlocks_[a];if(d===b||d===c)d.unplug(),d.bumpNeighbours_()}Blockly.Events.setGroup(!1)}this.prevBlocks_[0]=b;this.prevBlocks_[1]=c}};
|
||||
Blockly.Constants.Logic.LOGIC_COMPARE_EXTENSION=function(){this.RTL&&Blockly.Constants.Logic.fixLogicCompareRtlOpLabels.apply(this);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_=
|
||||
|
||||
@@ -16,7 +16,16 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This script generates two versions of Blockly's core files:
|
||||
# Usage: build.py <0 or more of accessible, core, generators, langfiles>
|
||||
# build.py with no parameters builds all files.
|
||||
# core builds blockly_compressed, blockly_uncompressed, and blocks_compressed.
|
||||
# accessible builds blockly_accessible_compressed,
|
||||
# blockly_accessible_uncompressed, and blocks_compressed.
|
||||
# generators builds every <language>_compressed.js.
|
||||
# langfiles builds every msg/js/<LANG>.js file.
|
||||
|
||||
# This script generates four versions of Blockly's core files. The first pair
|
||||
# are:
|
||||
# blockly_compressed.js
|
||||
# blockly_uncompressed.js
|
||||
# The compressed file is a concatenation of all of Blockly's core files which
|
||||
@@ -28,6 +37,12 @@
|
||||
# been renamed. The uncompressed file also allows for a faster developement
|
||||
# cycle since there is no need to rebuild or recompile, just reload.
|
||||
#
|
||||
# The second pair are:
|
||||
# blockly_accessible_compressed.js
|
||||
# blockly_accessible_uncompressed.js
|
||||
# These files are analogous to blockly_compressed and blockly_uncompressed,
|
||||
# but also include the visually-impaired module for Blockly.
|
||||
#
|
||||
# This script also generates:
|
||||
# blocks_compressed.js: The compressed Blockly language blocks.
|
||||
# javascript_compressed.js: The compressed Javascript generator.
|
||||
@@ -41,6 +56,14 @@ if sys.version_info[0] != 2:
|
||||
raise Exception("Blockly build only compatible with Python 2.x.\n"
|
||||
"You are using: " + sys.version)
|
||||
|
||||
for arg in sys.argv[1:len(sys.argv)]:
|
||||
if (arg != 'core' and
|
||||
arg != 'accessible' and
|
||||
arg != 'generators' and
|
||||
arg != 'langfiles'):
|
||||
raise Exception("Invalid argument: \"" + arg + "\". Usage: build.py <0 or more of accessible," +
|
||||
" core, generators, langfiles>")
|
||||
|
||||
import errno, glob, httplib, json, os, re, subprocess, threading, urllib
|
||||
|
||||
|
||||
@@ -71,13 +94,13 @@ class Gen_uncompressed(threading.Thread):
|
||||
"""Generate a JavaScript file that loads Blockly's raw files.
|
||||
Runs in a separate thread.
|
||||
"""
|
||||
def __init__(self, search_paths):
|
||||
def __init__(self, search_paths, target_filename):
|
||||
threading.Thread.__init__(self)
|
||||
self.search_paths = search_paths
|
||||
self.target_filename = target_filename
|
||||
|
||||
def run(self):
|
||||
target_filename = 'blockly_uncompressed.js'
|
||||
f = open(target_filename, 'w')
|
||||
f = open(self.target_filename, 'w')
|
||||
f.write(HEADER)
|
||||
f.write("""
|
||||
var isNodeJS = !!(typeof module !== 'undefined' && module.exports &&
|
||||
@@ -92,7 +115,7 @@ window.BLOCKLY_DIR = (function() {
|
||||
if (!isNodeJS) {
|
||||
// Find name of current directory.
|
||||
var scripts = document.getElementsByTagName('script');
|
||||
var re = new RegExp('(.+)[\/]blockly_uncompressed\.js$');
|
||||
var re = new RegExp('(.+)[\/]blockly_(.*)uncompressed\.js$');
|
||||
for (var i = 0, script; script = scripts[i]; i++) {
|
||||
var match = re.exec(script.src);
|
||||
if (match) {
|
||||
@@ -159,7 +182,7 @@ if (isNodeJS) {
|
||||
}
|
||||
""")
|
||||
f.close()
|
||||
print("SUCCESS: " + target_filename)
|
||||
print("SUCCESS: " + self.target_filename)
|
||||
|
||||
|
||||
class Gen_compressed(threading.Thread):
|
||||
@@ -168,18 +191,27 @@ class Gen_compressed(threading.Thread):
|
||||
Uses the Closure Compiler's online API.
|
||||
Runs in a separate thread.
|
||||
"""
|
||||
def __init__(self, search_paths):
|
||||
def __init__(self, search_paths, bundles):
|
||||
threading.Thread.__init__(self)
|
||||
self.search_paths = search_paths
|
||||
self.bundles = bundles
|
||||
|
||||
def run(self):
|
||||
self.gen_core()
|
||||
self.gen_blocks()
|
||||
self.gen_generator("javascript")
|
||||
self.gen_generator("python")
|
||||
self.gen_generator("php")
|
||||
self.gen_generator("dart")
|
||||
self.gen_generator("lua")
|
||||
if ('core' in self.bundles):
|
||||
self.gen_core()
|
||||
|
||||
if ('accessible' in self.bundles):
|
||||
self.gen_accessible()
|
||||
|
||||
if ('core' in self.bundles or 'accessible' in self.bundles):
|
||||
self.gen_blocks()
|
||||
|
||||
if ('generators' in self.bundles):
|
||||
self.gen_generator("javascript")
|
||||
self.gen_generator("python")
|
||||
self.gen_generator("php")
|
||||
self.gen_generator("dart")
|
||||
self.gen_generator("lua")
|
||||
|
||||
def gen_core(self):
|
||||
target_filename = "blockly_compressed.js"
|
||||
@@ -207,6 +239,33 @@ class Gen_compressed(threading.Thread):
|
||||
|
||||
self.do_compile(params, target_filename, filenames, "")
|
||||
|
||||
def gen_accessible(self):
|
||||
target_filename = "blockly_accessible_compressed.js"
|
||||
# Define the parameters for the POST request.
|
||||
params = [
|
||||
("compilation_level", "SIMPLE_OPTIMIZATIONS"),
|
||||
("use_closure_library", "true"),
|
||||
("language_out", "ES5"),
|
||||
("output_format", "json"),
|
||||
("output_info", "compiled_code"),
|
||||
("output_info", "warnings"),
|
||||
("output_info", "errors"),
|
||||
("output_info", "statistics"),
|
||||
]
|
||||
|
||||
# Read in all the source files.
|
||||
filenames = calcdeps.CalculateDependencies(self.search_paths,
|
||||
[os.path.join("accessible", "app.component.js")])
|
||||
for filename in filenames:
|
||||
# Filter out the Closure files (the compiler will add them).
|
||||
if filename.startswith(os.pardir + os.sep): # '../'
|
||||
continue
|
||||
f = open(filename)
|
||||
params.append(("js_code", "".join(f.readlines())))
|
||||
f.close()
|
||||
|
||||
self.do_compile(params, target_filename, filenames, "")
|
||||
|
||||
def gen_blocks(self):
|
||||
target_filename = "blocks_compressed.js"
|
||||
# Define the parameters for the POST request.
|
||||
@@ -362,8 +421,9 @@ class Gen_langfiles(threading.Thread):
|
||||
Runs in a separate thread.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, force_gen):
|
||||
threading.Thread.__init__(self)
|
||||
self.force_gen = force_gen
|
||||
|
||||
def _rebuild(self, srcs, dests):
|
||||
# Determine whether any of the files in srcs is newer than any in dests.
|
||||
@@ -385,9 +445,10 @@ class Gen_langfiles(threading.Thread):
|
||||
|
||||
def run(self):
|
||||
# The files msg/json/{en,qqq,synonyms}.json depend on msg/messages.js.
|
||||
if self._rebuild([os.path.join("msg", "messages.js")],
|
||||
[os.path.join("msg", "json", f) for f in
|
||||
["en.json", "qqq.json", "synonyms.json"]]):
|
||||
if (self.force_gen or
|
||||
self._rebuild([os.path.join("msg", "messages.js")],
|
||||
[os.path.join("msg", "json", f) for f in
|
||||
["en.json", "qqq.json", "synonyms.json"]])):
|
||||
try:
|
||||
subprocess.check_call([
|
||||
"python",
|
||||
@@ -456,14 +517,29 @@ if __name__ == "__main__":
|
||||
developers.google.com/blockly/guides/modify/web/closure""")
|
||||
sys.exit(1)
|
||||
|
||||
search_paths = calcdeps.ExpandDirectories(
|
||||
core_search_paths = calcdeps.ExpandDirectories(
|
||||
["core", os.path.join(os.path.pardir, "closure-library")])
|
||||
full_search_paths = calcdeps.ExpandDirectories(
|
||||
["accessible", "core", os.path.join(os.path.pardir, "closure-library")])
|
||||
|
||||
# Run both tasks in parallel threads.
|
||||
if (len(sys.argv) == 1):
|
||||
args = ['core', 'accessible', 'generators', 'defaultlangfiles']
|
||||
else:
|
||||
args = sys.argv
|
||||
|
||||
# Uncompressed and compressed are run in parallel threads.
|
||||
# Uncompressed is limited by processor speed.
|
||||
# Compressed is limited by network and server speed.
|
||||
Gen_uncompressed(search_paths).start()
|
||||
Gen_compressed(search_paths).start()
|
||||
if ('core' in args):
|
||||
Gen_uncompressed(core_search_paths, 'blockly_uncompressed.js').start()
|
||||
|
||||
# This is run locally in a separate thread.
|
||||
Gen_langfiles().start()
|
||||
if ('accessible' in args):
|
||||
Gen_uncompressed(full_search_paths, 'blockly_accessible_uncompressed.js').start()
|
||||
|
||||
# Compressed is limited by network and server speed.
|
||||
Gen_compressed(full_search_paths, args).start()
|
||||
|
||||
# This is run locally in a separate thread
|
||||
# defaultlangfiles checks for changes in the msg files, while manually asking
|
||||
# to build langfiles will force the messages to be rebuilt.
|
||||
if ('langfiles' in args or 'defaultlangfiles' in args):
|
||||
Gen_langfiles('langfiles' in args).start()
|
||||
|
||||
+20
-14
@@ -156,7 +156,7 @@ Blockly.Block = function(workspace, prototypeName, opt_id) {
|
||||
/** @type {boolean|undefined} */
|
||||
this.inputsInlineDefault = this.inputsInline;
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.fire(new Blockly.Events.Create(this));
|
||||
Blockly.Events.fire(new Blockly.Events.BlockCreate(this));
|
||||
}
|
||||
// Bind an onchange function, if it exists.
|
||||
if (goog.isFunction(this.onchange)) {
|
||||
@@ -209,7 +209,7 @@ Blockly.Block.prototype.dispose = function(healStack) {
|
||||
}
|
||||
this.unplug(healStack);
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.fire(new Blockly.Events.Delete(this));
|
||||
Blockly.Events.fire(new Blockly.Events.BlockDelete(this));
|
||||
}
|
||||
Blockly.Events.disable();
|
||||
|
||||
@@ -311,7 +311,7 @@ Blockly.Block.prototype.getConnections_ = function() {
|
||||
/**
|
||||
* Walks down a stack of blocks and finds the last next connection on the stack.
|
||||
* @return {Blockly.Connection} The last next connection on the stack, or null.
|
||||
* @private
|
||||
* @package
|
||||
*/
|
||||
Blockly.Block.prototype.lastConnectionInStack_ = function() {
|
||||
var nextConnection = this.nextConnection;
|
||||
@@ -793,7 +793,7 @@ Blockly.Block.prototype.setOutput = function(newBoolean, opt_check) {
|
||||
*/
|
||||
Blockly.Block.prototype.setInputsInline = function(newBoolean) {
|
||||
if (this.inputsInline != newBoolean) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this, 'inline', null, this.inputsInline, newBoolean));
|
||||
this.inputsInline = newBoolean;
|
||||
}
|
||||
@@ -832,7 +832,7 @@ Blockly.Block.prototype.getInputsInline = function() {
|
||||
*/
|
||||
Blockly.Block.prototype.setDisabled = function(disabled) {
|
||||
if (this.disabled != disabled) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this, 'disabled', null, this.disabled, disabled));
|
||||
this.disabled = disabled;
|
||||
}
|
||||
@@ -869,7 +869,7 @@ Blockly.Block.prototype.isCollapsed = function() {
|
||||
*/
|
||||
Blockly.Block.prototype.setCollapsed = function(collapsed) {
|
||||
if (this.collapsed_ != collapsed) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this, 'collapsed', null, this.collapsed_, collapsed));
|
||||
this.collapsed_ = collapsed;
|
||||
}
|
||||
@@ -1067,10 +1067,14 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) {
|
||||
for (var i = 0; i < tokens.length; i++) {
|
||||
var token = tokens[i];
|
||||
if (typeof token == 'number') {
|
||||
goog.asserts.assert(token > 0 && token <= args.length,
|
||||
'Message index %%s out of range.', token);
|
||||
goog.asserts.assert(!indexDup[token],
|
||||
'Message index %%s duplicated.', token);
|
||||
if (token <= 0 || token > args.length) {
|
||||
throw new Error('Block \"' + this.type + '\": ' +
|
||||
'Message index %' + token + ' out of range.');
|
||||
}
|
||||
if (indexDup[token]) {
|
||||
throw new Error('Block \"' + this.type + '\": ' +
|
||||
'Message index %' + token + ' duplicated.');
|
||||
}
|
||||
indexDup[token] = true;
|
||||
indexCount++;
|
||||
elements.push(args[token - 1]);
|
||||
@@ -1081,8 +1085,10 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) {
|
||||
}
|
||||
}
|
||||
}
|
||||
goog.asserts.assert(indexCount == args.length,
|
||||
'block "%s": Message does not reference all %s arg(s).', this.type, args.length);
|
||||
if(indexCount != args.length) {
|
||||
throw new Error('Block \"' + this.type + '\": ' +
|
||||
'Message does not reference all ' + args.length + ' arg(s).');
|
||||
}
|
||||
// Add last dummy input if needed.
|
||||
if (elements.length && (typeof elements[elements.length - 1] == 'string' ||
|
||||
goog.string.startsWith(elements[elements.length - 1]['type'],
|
||||
@@ -1389,7 +1395,7 @@ Blockly.Block.prototype.getCommentText = function() {
|
||||
*/
|
||||
Blockly.Block.prototype.setCommentText = function(text) {
|
||||
if (this.comment != text) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this, 'comment', null, this.comment, text || ''));
|
||||
this.comment = text;
|
||||
}
|
||||
@@ -1427,7 +1433,7 @@ Blockly.Block.prototype.getRelativeToSurfaceXY = function() {
|
||||
*/
|
||||
Blockly.Block.prototype.moveBy = function(dx, dy) {
|
||||
goog.asserts.assert(!this.parentBlock_, 'Block has parent.');
|
||||
var event = new Blockly.Events.Move(this);
|
||||
var event = new Blockly.Events.BlockMove(this);
|
||||
this.xy_.translate(dx, dy);
|
||||
event.recordNew();
|
||||
Blockly.Events.fire(event);
|
||||
|
||||
+42
-13
@@ -80,6 +80,15 @@ Blockly.BlockDragSurfaceSvg.prototype.container_ = null;
|
||||
*/
|
||||
Blockly.BlockDragSurfaceSvg.prototype.scale_ = 1;
|
||||
|
||||
/**
|
||||
* Cached value for the translation of the drag surface.
|
||||
* This translation is in pixel units, because the scale is applied to the
|
||||
* drag group rather than the top-level SVG.
|
||||
* @type {goog.math.Coordinate}
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockDragSurfaceSvg.prototype.surfaceXY_ = null;
|
||||
|
||||
/**
|
||||
* Create the drag surface and inject it into the container.
|
||||
*/
|
||||
@@ -109,6 +118,7 @@ Blockly.BlockDragSurfaceSvg.prototype.setBlocksAndShow = function(blocks) {
|
||||
// appendChild removes the blocks from the previous parent
|
||||
this.dragGroup_.appendChild(blocks);
|
||||
this.SVG_.style.display = 'block';
|
||||
this.surfaceXY_ = new goog.math.Coordinate(0, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -128,6 +138,23 @@ Blockly.BlockDragSurfaceSvg.prototype.translateAndScaleGroup = function(x, y, sc
|
||||
' scale(' + scale + ')');
|
||||
};
|
||||
|
||||
/**
|
||||
* Translate the drag surface's SVG based on its internal state.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockDragSurfaceSvg.prototype.translateSurfaceInternal_ = function() {
|
||||
var x = this.surfaceXY_.x;
|
||||
var y = this.surfaceXY_.y;
|
||||
// This is a work-around to prevent a the blocks from rendering
|
||||
// fuzzy while they are being dragged on the drag surface.
|
||||
x = x.toFixed(0);
|
||||
y = y.toFixed(0);
|
||||
this.SVG_.style.display = 'block';
|
||||
|
||||
Blockly.utils.setCssTransform(this.SVG_,
|
||||
'translate3d(' + x + 'px, ' + y + 'px, 0px)');
|
||||
};
|
||||
|
||||
/**
|
||||
* Translate the entire drag surface during a drag.
|
||||
* We translate the drag surface instead of the blocks inside the surface
|
||||
@@ -137,15 +164,8 @@ Blockly.BlockDragSurfaceSvg.prototype.translateAndScaleGroup = function(x, y, sc
|
||||
* @param {number} y Y translation for the entire surface.
|
||||
*/
|
||||
Blockly.BlockDragSurfaceSvg.prototype.translateSurface = function(x, y) {
|
||||
x *= this.scale_;
|
||||
y *= this.scale_;
|
||||
// This is a work-around to prevent a the blocks from rendering
|
||||
// fuzzy while they are being dragged on the drag surface.
|
||||
x = x.toFixed(0);
|
||||
y = y.toFixed(0);
|
||||
this.SVG_.style.display = 'block';
|
||||
Blockly.utils.setCssTransform(this.SVG_,
|
||||
'translate3d(' + x + 'px, ' + y + 'px, 0px)');
|
||||
this.surfaceXY_ = new goog.math.Coordinate(x * this.scale_, y * this.scale_);
|
||||
this.translateSurfaceInternal_();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -180,12 +200,21 @@ Blockly.BlockDragSurfaceSvg.prototype.getCurrentBlock = function() {
|
||||
/**
|
||||
* Clear the group and hide the surface; move the blocks off onto the provided
|
||||
* element.
|
||||
* @param {!Element} newSurface Surface the dragging blocks should be moved to.
|
||||
* If the block is being deleted it doesn't need to go back to the original
|
||||
* surface, since it would be removed immediately during dispose.
|
||||
* @param {Element} opt_newSurface Surface the dragging blocks should be moved
|
||||
* to, or null if the blocks should be removed from this surface without
|
||||
* being moved to a different surface.
|
||||
*/
|
||||
Blockly.BlockDragSurfaceSvg.prototype.clearAndHide = function(newSurface) {
|
||||
// appendChild removes the node from this.dragGroup_
|
||||
newSurface.appendChild(this.getCurrentBlock());
|
||||
Blockly.BlockDragSurfaceSvg.prototype.clearAndHide = function(opt_newSurface) {
|
||||
if (opt_newSurface) {
|
||||
// appendChild removes the node from this.dragGroup_
|
||||
opt_newSurface.appendChild(this.getCurrentBlock());
|
||||
} else {
|
||||
this.dragGroup_.removeChild(this.getCurrentBlock());
|
||||
}
|
||||
this.SVG_.style.display = 'none';
|
||||
goog.asserts.assert(this.dragGroup_.childNodes.length == 0,
|
||||
'Drag group was not cleared.');
|
||||
this.surfaceXY_ = null;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Methods for dragging a block visually.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.BlockDragger');
|
||||
|
||||
goog.require('Blockly.DraggedConnectionManager');
|
||||
|
||||
goog.require('goog.math.Coordinate');
|
||||
goog.require('goog.asserts');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a block dragger. It moves blocks around the workspace when they
|
||||
* are being dragged by a mouse or touch.
|
||||
* @param {!Blockly.Block} block The block to drag.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The workspace to drag on.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.BlockDragger = function(block, workspace) {
|
||||
/**
|
||||
* The top block in the stack that is being dragged.
|
||||
* @type {!Blockly.BlockSvg}
|
||||
* @private
|
||||
*/
|
||||
this.draggingBlock_ = block;
|
||||
|
||||
/**
|
||||
* The workspace on which the block is being dragged.
|
||||
* @type {!Blockly.WorkspaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.workspace_ = workspace;
|
||||
|
||||
/**
|
||||
* Object that keeps track of connections on dragged blocks.
|
||||
* @type {!Blockly.DraggedConnectionManager}
|
||||
* @private
|
||||
*/
|
||||
this.draggedConnectionManager_ = new Blockly.DraggedConnectionManager(
|
||||
this.draggingBlock_);
|
||||
|
||||
/**
|
||||
* Which delete area the mouse pointer is over, if any.
|
||||
* One of {@link Blockly.DELETE_AREA_TRASH},
|
||||
* {@link Blockly.DELETE_AREA_TOOLBOX}, or {@link Blockly.DELETE_AREA_NONE}.
|
||||
* @type {?number}
|
||||
* @private
|
||||
*/
|
||||
this.deleteArea_ = null;
|
||||
|
||||
/**
|
||||
* Whether the block would be deleted if dropped immediately.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.wouldDeleteBlock_ = false;
|
||||
|
||||
/**
|
||||
* The location of the top left corner of the dragging block at the beginning
|
||||
* of the drag in workspace coordinates.
|
||||
* @type {!goog.math.Coordinate}
|
||||
* @private
|
||||
*/
|
||||
this.startXY_ = this.draggingBlock_.getRelativeToSurfaceXY();
|
||||
|
||||
/**
|
||||
* A list of all of the icons (comment, warning, and mutator) that are
|
||||
* on this block and its descendants. Moving an icon moves the bubble that
|
||||
* extends from it if that bubble is open.
|
||||
* @type {Array.<!Object>}
|
||||
* @private
|
||||
*/
|
||||
this.dragIconData_ = Blockly.BlockDragger.initIconData_(block);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sever all links from this object.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockDragger.prototype.dispose = function() {
|
||||
this.draggingBlock_ = null;
|
||||
this.workspace_ = null;
|
||||
this.startWorkspace_ = null;
|
||||
this.dragIconData_.length = 0;
|
||||
|
||||
if (this.draggedConnectionManager_) {
|
||||
this.draggedConnectionManager_.dispose();
|
||||
this.draggedConnectionManager_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a list of all of the icons (comment, warning, and mutator) that are
|
||||
* on this block and its descendants. Moving an icon moves the bubble that
|
||||
* extends from it if that bubble is open.
|
||||
* @param {!Blockly.BlockSvg} block The root block that is being dragged.
|
||||
* @return {!Array.<!Object>} The list of all icons and their locations.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockDragger.initIconData_ = function(block) {
|
||||
// Build a list of icons that need to be moved and where they started.
|
||||
var dragIconData = [];
|
||||
var descendants = block.getDescendants();
|
||||
for (var i = 0, descendant; descendant = descendants[i]; i++) {
|
||||
var icons = descendant.getIcons();
|
||||
for (var j = 0; j < icons.length; j++) {
|
||||
var data = {
|
||||
// goog.math.Coordinate with x and y properties (workspace coordinates).
|
||||
location: icons[j].getIconLocation(),
|
||||
// Blockly.Icon
|
||||
icon: icons[j]
|
||||
};
|
||||
dragIconData.push(data);
|
||||
}
|
||||
}
|
||||
return dragIconData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start dragging a block. This includes moving it to the drag surface.
|
||||
* @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at mouse down, in pixel units.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockDragger.prototype.startBlockDrag = function(currentDragDeltaXY) {
|
||||
if (!Blockly.Events.getGroup()) {
|
||||
Blockly.Events.setGroup(true);
|
||||
}
|
||||
|
||||
this.workspace_.setResizesEnabled(false);
|
||||
Blockly.BlockSvg.disconnectUiStop_();
|
||||
|
||||
if (this.draggingBlock_.getParent()) {
|
||||
this.draggingBlock_.unplug();
|
||||
var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
var newLoc = goog.math.Coordinate.sum(this.startXY_, delta);
|
||||
|
||||
this.draggingBlock_.translate(newLoc.x, newLoc.y);
|
||||
this.draggingBlock_.disconnectUiEffect();
|
||||
}
|
||||
this.draggingBlock_.setDragging(true);
|
||||
// For future consideration: we may be able to put moveToDragSurface inside
|
||||
// the block dragger, which would also let the block not track the block drag
|
||||
// surface.
|
||||
this.draggingBlock_.moveToDragSurface_();
|
||||
|
||||
if (this.workspace_.toolbox_) {
|
||||
this.workspace_.toolbox_.addDeleteStyle();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a step of block dragging, based on the given event. Update the
|
||||
* display accordingly.
|
||||
* @param {!Event} e The most recent move event.
|
||||
* @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel units.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockDragger.prototype.dragBlock = function(e, currentDragDeltaXY) {
|
||||
var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
var newLoc = goog.math.Coordinate.sum(this.startXY_, delta);
|
||||
|
||||
this.draggingBlock_.moveDuringDrag(newLoc);
|
||||
this.dragIcons_(delta);
|
||||
|
||||
this.deleteArea_ = this.workspace_.isDeleteArea(e);
|
||||
this.draggedConnectionManager_.update(delta, this.deleteArea_);
|
||||
|
||||
this.updateCursorDuringBlockDrag_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Finish a block drag and put the block back on the workspace.
|
||||
* @param {!Event} e The mouseup/touchend event.
|
||||
* @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel units.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockDragger.prototype.endBlockDrag = function(e, currentDragDeltaXY) {
|
||||
// Make sure internal state is fresh.
|
||||
this.dragBlock(e, currentDragDeltaXY);
|
||||
this.dragIconData_ = [];
|
||||
|
||||
Blockly.BlockSvg.disconnectUiStop_();
|
||||
|
||||
var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY);
|
||||
var newLoc = goog.math.Coordinate.sum(this.startXY_, delta);
|
||||
this.draggingBlock_.moveOffDragSurface_(newLoc);
|
||||
|
||||
var deleted = this.maybeDeleteBlock_();
|
||||
if (!deleted) {
|
||||
// These are expensive and don't need to be done if we're deleting.
|
||||
this.draggingBlock_.moveConnections_(delta.x, delta.y);
|
||||
this.draggingBlock_.setDragging(false);
|
||||
this.draggedConnectionManager_.applyConnections();
|
||||
this.draggingBlock_.render();
|
||||
this.fireMoveEvent_();
|
||||
this.draggingBlock_.scheduleSnapAndBump();
|
||||
}
|
||||
this.workspace_.setResizesEnabled(true);
|
||||
|
||||
if (this.workspace_.toolbox_) {
|
||||
this.workspace_.toolbox_.removeDeleteStyle();
|
||||
}
|
||||
Blockly.Events.setGroup(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fire a move event at the end of a block drag.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockDragger.prototype.fireMoveEvent_ = function() {
|
||||
var event = new Blockly.Events.BlockMove(this.draggingBlock_);
|
||||
event.oldCoordinate = this.startXY_;
|
||||
event.recordNew();
|
||||
Blockly.Events.fire(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Shut the trash can and, if necessary, delete the dragging block.
|
||||
* Should be called at the end of a block drag.
|
||||
* @return {boolean} whether the block was deleted.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockDragger.prototype.maybeDeleteBlock_ = function() {
|
||||
var trashcan = this.workspace_.trashcan;
|
||||
|
||||
if (this.wouldDeleteBlock_) {
|
||||
if (trashcan) {
|
||||
goog.Timer.callOnce(trashcan.close, 100, trashcan);
|
||||
}
|
||||
// Fire a move event, so we know where to go back to for an undo.
|
||||
this.fireMoveEvent_();
|
||||
this.draggingBlock_.dispose(false, true);
|
||||
} else if (trashcan) {
|
||||
// Make sure the trash can is closed.
|
||||
trashcan.close();
|
||||
}
|
||||
return this.wouldDeleteBlock_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the cursor (and possibly the trash can lid) to reflect whether the
|
||||
* dragging block would be deleted if released immediately.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockDragger.prototype.updateCursorDuringBlockDrag_ = function() {
|
||||
this.wouldDeleteBlock_ = this.draggedConnectionManager_.wouldDeleteBlock();
|
||||
var trashcan = this.workspace_.trashcan;
|
||||
if (this.wouldDeleteBlock_) {
|
||||
this.draggingBlock_.setDeleteStyle(true);
|
||||
if (this.deleteArea_ == Blockly.DELETE_AREA_TRASH && trashcan) {
|
||||
trashcan.setOpen_(true);
|
||||
}
|
||||
} else {
|
||||
this.draggingBlock_.setDeleteStyle(false);
|
||||
if (trashcan) {
|
||||
trashcan.setOpen_(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a coordinate object from pixels to workspace units, including a
|
||||
* correction for mutator workspaces.
|
||||
* This function does not consider differing origins. It simply scales the
|
||||
* input's x and y values.
|
||||
* @param {!goog.math.Coordinate} pixelCoord A coordinate with x and y values
|
||||
* in css pixel units.
|
||||
* @return {!goog.math.Coordinate} The input coordinate divided by the workspace
|
||||
* scale.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) {
|
||||
var result = new goog.math.Coordinate(pixelCoord.x / this.workspace_.scale,
|
||||
pixelCoord.y / this.workspace_.scale);
|
||||
if (this.workspace_.isMutator) {
|
||||
// If we're in a mutator, its scale is always 1, purely because of some
|
||||
// oddities in our rendering optimizations. The actual scale is the same as
|
||||
// the scale on the parent workspace.
|
||||
// Fix that for dragging.
|
||||
var mainScale = this.workspace_.options.parentWorkspace.scale;
|
||||
result = result.scale(1 / mainScale);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Move all of the icons connected to this drag.
|
||||
* @param {!goog.math.Coordinate} dxy How far to move the icons from their
|
||||
* original positions, in workspace units.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockDragger.prototype.dragIcons_ = function(dxy) {
|
||||
// Moving icons moves their associated bubbles.
|
||||
for (var i = 0; i < this.dragIconData_.length; i++) {
|
||||
var data = this.dragIconData_[i];
|
||||
data.icon.setIconLocation(goog.math.Coordinate.sum(data.location, dxy));
|
||||
}
|
||||
};
|
||||
@@ -255,6 +255,28 @@ Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_LTR =
|
||||
Blockly.BlockSvg.DISTANCE_45_OUTSIDE) + ',' +
|
||||
(Blockly.BlockSvg.DISTANCE_45_OUTSIDE + 0.5);
|
||||
|
||||
/**
|
||||
* Returns a bounding box describing the dimensions of this block
|
||||
* and any blocks stacked below it.
|
||||
* @return {!{height: number, width: number}} Object with height and width
|
||||
* properties in workspace units.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.getHeightWidth = function() {
|
||||
var height = this.height;
|
||||
var width = this.width;
|
||||
// Recursively add size of subsequent blocks.
|
||||
var nextBlock = this.getNextBlock();
|
||||
if (nextBlock) {
|
||||
var nextHeightWidth = nextBlock.getHeightWidth();
|
||||
height += nextHeightWidth.height - 4; // Height of tab.
|
||||
width = Math.max(width, nextHeightWidth.width);
|
||||
} else if (!this.nextConnection && !this.outputConnection) {
|
||||
// Add a bit of margin under blocks with no bottom tab.
|
||||
height += 2;
|
||||
}
|
||||
return {height: height, width: width};
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the block.
|
||||
* Lays out and reflows a block based on its contents and settings.
|
||||
|
||||
+87
-388
@@ -28,6 +28,7 @@ goog.provide('Blockly.BlockSvg');
|
||||
|
||||
goog.require('Blockly.Block');
|
||||
goog.require('Blockly.ContextMenu');
|
||||
goog.require('Blockly.Grid');
|
||||
goog.require('Blockly.RenderedConnection');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.utils');
|
||||
@@ -56,6 +57,7 @@ Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
|
||||
* @private
|
||||
*/
|
||||
this.svgGroup_ = Blockly.utils.createSvgElement('g', {}, null);
|
||||
this.svgGroup_.translate_ = '';
|
||||
|
||||
/**
|
||||
* @type {SVGElement}
|
||||
@@ -140,9 +142,6 @@ Blockly.BlockSvg.prototype.initSvg = function() {
|
||||
if (!this.workspace.options.readOnly && !this.eventsInit_) {
|
||||
Blockly.bindEventWithChecks_(this.getSvgRoot(), 'mousedown', this,
|
||||
this.onMouseDown_);
|
||||
var thisBlock = this;
|
||||
Blockly.bindEvent_(this.getSvgRoot(), 'touchstart', null,
|
||||
function(e) {Blockly.longStart_(e, thisBlock);});
|
||||
}
|
||||
this.eventsInit_ = true;
|
||||
|
||||
@@ -231,69 +230,6 @@ Blockly.BlockSvg.prototype.getIcons = function() {
|
||||
return icons;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper function called when a mouseUp occurs during a drag operation.
|
||||
* @type {Array.<!Array>}
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockSvg.onMouseUpWrapper_ = null;
|
||||
|
||||
/**
|
||||
* Wrapper function called when a mouseMove occurs during a drag operation.
|
||||
* @type {Array.<!Array>}
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockSvg.onMouseMoveWrapper_ = null;
|
||||
|
||||
/**
|
||||
* Stop binding to the global mouseup and mousemove events.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockSvg.terminateDrag = function() {
|
||||
Blockly.BlockSvg.disconnectUiStop_();
|
||||
if (Blockly.BlockSvg.onMouseUpWrapper_) {
|
||||
Blockly.unbindEvent_(Blockly.BlockSvg.onMouseUpWrapper_);
|
||||
Blockly.BlockSvg.onMouseUpWrapper_ = null;
|
||||
}
|
||||
if (Blockly.BlockSvg.onMouseMoveWrapper_) {
|
||||
Blockly.unbindEvent_(Blockly.BlockSvg.onMouseMoveWrapper_);
|
||||
Blockly.BlockSvg.onMouseMoveWrapper_ = null;
|
||||
}
|
||||
var selected = Blockly.selected;
|
||||
if (Blockly.dragMode_ == Blockly.DRAG_FREE) {
|
||||
// Terminate a drag operation.
|
||||
if (selected) {
|
||||
// Update the connection locations.
|
||||
var xy = selected.getRelativeToSurfaceXY();
|
||||
var dxy = goog.math.Coordinate.difference(xy, selected.dragStartXY_);
|
||||
var event = new Blockly.Events.Move(selected);
|
||||
event.oldCoordinate = selected.dragStartXY_;
|
||||
event.recordNew();
|
||||
Blockly.Events.fire(event);
|
||||
|
||||
selected.moveConnections_(dxy.x, dxy.y);
|
||||
delete selected.draggedBubbles_;
|
||||
selected.setDragging_(false);
|
||||
selected.moveOffDragSurface_();
|
||||
selected.render();
|
||||
selected.workspace.setResizesEnabled(true);
|
||||
// Ensure that any snap and bump are part of this move's event group.
|
||||
var group = Blockly.Events.getGroup();
|
||||
setTimeout(function() {
|
||||
Blockly.Events.setGroup(group);
|
||||
selected.snapToGrid();
|
||||
Blockly.Events.setGroup(false);
|
||||
}, Blockly.BUMP_DELAY / 2);
|
||||
setTimeout(function() {
|
||||
Blockly.Events.setGroup(group);
|
||||
selected.bumpNeighbours_();
|
||||
Blockly.Events.setGroup(false);
|
||||
}, Blockly.BUMP_DELAY);
|
||||
}
|
||||
}
|
||||
Blockly.dragMode_ = Blockly.DRAG_NONE;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set parent of this block to be a new block or null.
|
||||
* @param {Blockly.BlockSvg} newParent New parent block.
|
||||
@@ -326,6 +262,8 @@ Blockly.BlockSvg.prototype.setParent = function(newParent) {
|
||||
/**
|
||||
* Return the coordinates of the top-left corner of this block relative to the
|
||||
* drawing surface's origin (0,0), in workspace units.
|
||||
* If the block is on the workspace, (0, 0) is the origin of the workspace
|
||||
* coordinate system.
|
||||
* This does not change with workspace scale.
|
||||
* @return {!goog.math.Coordinate} Object with .x and .y properties in
|
||||
* workspace coordinates.
|
||||
@@ -366,7 +304,7 @@ Blockly.BlockSvg.prototype.getRelativeToSurfaceXY = function() {
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.moveBy = function(dx, dy) {
|
||||
goog.asserts.assert(!this.parentBlock_, 'Block has parent.');
|
||||
var event = new Blockly.Events.Move(this);
|
||||
var event = new Blockly.Events.BlockMove(this);
|
||||
var xy = this.getRelativeToSurfaceXY();
|
||||
this.translate(xy.x + dx, xy.y + dy);
|
||||
this.moveConnections_(dx, dy);
|
||||
@@ -399,6 +337,7 @@ Blockly.BlockSvg.prototype.moveToDragSurface_ = function() {
|
||||
// The translation for drag surface blocks,
|
||||
// is equal to the current relative-to-surface position,
|
||||
// to keep the position in sync as it move on/off the surface.
|
||||
// This is in workspace coordinates.
|
||||
var xy = this.getRelativeToSurfaceXY();
|
||||
this.clearTransformAttributes_();
|
||||
this.workspace.blockDragSurface_.translateSurface(xy.x, xy.y);
|
||||
@@ -410,19 +349,37 @@ Blockly.BlockSvg.prototype.moveToDragSurface_ = function() {
|
||||
* Move this block back to the workspace block canvas.
|
||||
* Generally should be called at the same time as setDragging_(false).
|
||||
* Does nothing if useDragSurface_ is false.
|
||||
* @param {!goog.math.Coordinate} newXY The position the block should take on
|
||||
* on the workspace canvas, in workspace coordinates.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.moveOffDragSurface_ = function() {
|
||||
Blockly.BlockSvg.prototype.moveOffDragSurface_ = function(newXY) {
|
||||
if (!this.useDragSurface_) {
|
||||
return;
|
||||
}
|
||||
// Translate to current position, turning off 3d.
|
||||
var xy = this.getRelativeToSurfaceXY();
|
||||
this.clearTransformAttributes_();
|
||||
this.translate(xy.x, xy.y);
|
||||
this.translate(newXY.x, newXY.y);
|
||||
this.workspace.blockDragSurface_.clearAndHide(this.workspace.getCanvas());
|
||||
};
|
||||
|
||||
/**
|
||||
* Move this block during a drag, taking into account whether we are using a
|
||||
* drag surface to translate blocks.
|
||||
* This block must be a top-level block.
|
||||
* @param {!goog.math.Coordinate} newLoc The location to translate to, in
|
||||
* workspace coordinates.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.moveDuringDrag = function(newLoc) {
|
||||
if (this.useDragSurface_) {
|
||||
this.workspace.blockDragSurface_.translateSurface(newLoc.x, newLoc.y);
|
||||
} else {
|
||||
this.svgGroup_.translate_ = 'translate(' + newLoc.x + ',' + newLoc.y + ')';
|
||||
this.svgGroup_.setAttribute('transform',
|
||||
this.svgGroup_.translate_ + this.svgGroup_.skew_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the block of transform="..." attributes.
|
||||
* Used when the block is switching from 3d to 2d transform or vice versa.
|
||||
@@ -439,7 +396,7 @@ Blockly.BlockSvg.prototype.snapToGrid = function() {
|
||||
if (!this.workspace) {
|
||||
return; // Deleted block.
|
||||
}
|
||||
if (Blockly.dragMode_ != Blockly.DRAG_NONE) {
|
||||
if (this.workspace.isDragging()) {
|
||||
return; // Don't bump blocks during a drag.
|
||||
}
|
||||
if (this.getParent()) {
|
||||
@@ -448,11 +405,11 @@ Blockly.BlockSvg.prototype.snapToGrid = function() {
|
||||
if (this.isInFlyout) {
|
||||
return; // Don't move blocks around in a flyout.
|
||||
}
|
||||
if (!this.workspace.options.gridOptions ||
|
||||
!this.workspace.options.gridOptions['snap']) {
|
||||
var grid = this.workspace.getGrid();
|
||||
if (!grid || !grid.shouldSnap()) {
|
||||
return; // Config says no snapping.
|
||||
}
|
||||
var spacing = this.workspace.options.gridOptions['spacing'];
|
||||
var spacing = grid.getSpacing();
|
||||
var half = spacing / 2;
|
||||
var xy = this.getRelativeToSurfaceXY();
|
||||
var dx = Math.round((xy.x - half) / spacing) * spacing + half - xy.x;
|
||||
@@ -464,31 +421,10 @@ Blockly.BlockSvg.prototype.snapToGrid = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a bounding box describing the dimensions of this block
|
||||
* and any blocks stacked below it.
|
||||
* @return {!{height: number, width: number}} Object with height and width
|
||||
* properties.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.getHeightWidth = function() {
|
||||
var height = this.height;
|
||||
var width = this.width;
|
||||
// Recursively add size of subsequent blocks.
|
||||
var nextBlock = this.getNextBlock();
|
||||
if (nextBlock) {
|
||||
var nextHeightWidth = nextBlock.getHeightWidth();
|
||||
height += nextHeightWidth.height - 4; // Height of tab.
|
||||
width = Math.max(width, nextHeightWidth.width);
|
||||
} else if (!this.nextConnection && !this.outputConnection) {
|
||||
// Add a bit of margin under blocks with no bottom tab.
|
||||
height += 2;
|
||||
}
|
||||
return {height: height, width: width};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the coordinates of a bounding box describing the dimensions of this
|
||||
* block and any blocks stacked below it.
|
||||
* Coordinate system: workspace coordinates.
|
||||
* @return {!{topLeft: goog.math.Coordinate, bottomRight: goog.math.Coordinate}}
|
||||
* Object with top left and bottom right coordinates of the bounding box.
|
||||
*/
|
||||
@@ -609,132 +545,9 @@ Blockly.BlockSvg.prototype.tab = function(start, forward) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.onMouseDown_ = function(e) {
|
||||
if (this.workspace.options.readOnly) {
|
||||
return;
|
||||
}
|
||||
if (this.isInFlyout) {
|
||||
// longStart's simulation of right-clicks for longpresses on touch devices
|
||||
// calls the onMouseDown_ function defined on the prototype of the object
|
||||
// the was longpressed (in this case, a Blockly.BlockSvg). In this case
|
||||
// that behaviour is wrong, because Blockly.Flyout.prototype.blockMouseDown
|
||||
// should be called for a mousedown on a block in the flyout, which blocks
|
||||
// execution of the block's onMouseDown_ function.
|
||||
if (e.type == 'touchstart' && Blockly.utils.isRightButton(e)) {
|
||||
Blockly.Flyout.blockRightClick_(e, this);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (this.isInMutator) {
|
||||
// Mutator's coordinate system could be out of date because the bubble was
|
||||
// dragged, the block was moved, the parent workspace zoomed, etc.
|
||||
this.workspace.resize();
|
||||
}
|
||||
|
||||
this.workspace.updateScreenCalculationsIfScrolled();
|
||||
this.workspace.markFocused();
|
||||
Blockly.terminateDrag_();
|
||||
this.select();
|
||||
Blockly.hideChaff();
|
||||
if (Blockly.utils.isRightButton(e)) {
|
||||
// Right-click.
|
||||
this.showContextMenu_(e);
|
||||
// Click, not drag, so stop waiting for other touches from this identifier.
|
||||
Blockly.Touch.clearTouchIdentifier();
|
||||
} else if (!this.isMovable()) {
|
||||
// Allow immovable blocks to be selected and context menued, but not
|
||||
// dragged. Let this event bubble up to document, so the workspace may be
|
||||
// dragged instead.
|
||||
return;
|
||||
} else {
|
||||
if (!Blockly.Events.getGroup()) {
|
||||
Blockly.Events.setGroup(true);
|
||||
}
|
||||
// Left-click (or middle click)
|
||||
this.dragStartXY_ = this.getRelativeToSurfaceXY();
|
||||
this.workspace.startDrag(e, this.dragStartXY_);
|
||||
|
||||
Blockly.dragMode_ = Blockly.DRAG_STICKY;
|
||||
Blockly.BlockSvg.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document,
|
||||
'mouseup', this, this.onMouseUp_);
|
||||
Blockly.BlockSvg.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(
|
||||
document, 'mousemove', this, this.onMouseMove_);
|
||||
// Build a list of bubbles that need to be moved and where they started.
|
||||
this.draggedBubbles_ = [];
|
||||
var descendants = this.getDescendants();
|
||||
for (var i = 0, descendant; descendant = descendants[i]; i++) {
|
||||
var icons = descendant.getIcons();
|
||||
for (var j = 0; j < icons.length; j++) {
|
||||
var data = icons[j].getIconLocation();
|
||||
data.bubble = icons[j];
|
||||
this.draggedBubbles_.push(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
// This event has been handled. No need to bubble up to the document.
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a mouse-up anywhere in the SVG pane. Is only registered when a
|
||||
* block is clicked. We can't use mouseUp on the block since a fast-moving
|
||||
* cursor can briefly escape the block before it catches up.
|
||||
* @param {!Event} e Mouse up event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.onMouseUp_ = function(e) {
|
||||
Blockly.Touch.clearTouchIdentifier();
|
||||
if (Blockly.dragMode_ != Blockly.DRAG_FREE &&
|
||||
!Blockly.WidgetDiv.isVisible()) {
|
||||
// Move the block in front of the others. Do this at the end of a click
|
||||
// instead of rearranging the dom on mousedown. This helps with
|
||||
// performance and makes it easier to use psuedo element :active
|
||||
// to set the cursor.
|
||||
this.bringToFront_();
|
||||
Blockly.Events.fire(
|
||||
new Blockly.Events.Ui(this, 'click', undefined, undefined));
|
||||
}
|
||||
Blockly.terminateDrag_();
|
||||
|
||||
var deleteArea = this.workspace.isDeleteArea(e);
|
||||
|
||||
// Connect to a nearby block, but not if it's over the toolbox.
|
||||
if (Blockly.selected && Blockly.highlightedConnection_ &&
|
||||
deleteArea != Blockly.DELETE_AREA_TOOLBOX) {
|
||||
// Connect two blocks together.
|
||||
Blockly.localConnection_.connect(Blockly.highlightedConnection_);
|
||||
if (this.rendered) {
|
||||
// Trigger a connection animation.
|
||||
// Determine which connection is inferior (lower in the source stack).
|
||||
var inferiorConnection = Blockly.localConnection_.isSuperior() ?
|
||||
Blockly.highlightedConnection_ : Blockly.localConnection_;
|
||||
inferiorConnection.getSourceBlock().connectionUiEffect();
|
||||
}
|
||||
if (this.workspace.trashcan) {
|
||||
// Don't throw an object in the trash can if it just got connected.
|
||||
this.workspace.trashcan.close();
|
||||
}
|
||||
} else if (deleteArea && !this.getParent() && Blockly.selected.isDeletable()) {
|
||||
// We didn't connect the block, and it was over the trash can or the
|
||||
// toolbox. Delete it.
|
||||
var trashcan = this.workspace.trashcan;
|
||||
if (trashcan) {
|
||||
goog.Timer.callOnce(trashcan.close, 100, trashcan);
|
||||
}
|
||||
if (this.workspace.toolbox_) {
|
||||
this.workspace.toolbox_.removeDeleteStyle();
|
||||
}
|
||||
|
||||
Blockly.selected.dispose(false, true);
|
||||
}
|
||||
if (Blockly.highlightedConnection_) {
|
||||
Blockly.highlightedConnection_.unhighlight();
|
||||
Blockly.highlightedConnection_ = null;
|
||||
}
|
||||
if (!Blockly.WidgetDiv.isVisible()) {
|
||||
Blockly.Events.setGroup(false);
|
||||
var gesture = this.workspace.getGesture(e);
|
||||
if (gesture) {
|
||||
gesture.handleBlockStart(e, this);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -918,9 +731,9 @@ Blockly.BlockSvg.prototype.moveConnections_ = function(dx, dy) {
|
||||
/**
|
||||
* Recursively adds or removes the dragging class to this node and its children.
|
||||
* @param {boolean} adding True if adding, false if removing.
|
||||
* @private
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.setDragging_ = function(adding) {
|
||||
Blockly.BlockSvg.prototype.setDragging = function(adding) {
|
||||
if (adding) {
|
||||
var group = this.getSvgRoot();
|
||||
group.translate_ = '';
|
||||
@@ -936,161 +749,7 @@ Blockly.BlockSvg.prototype.setDragging_ = function(adding) {
|
||||
}
|
||||
// Recurse through all blocks attached under this one.
|
||||
for (var i = 0; i < this.childBlocks_.length; i++) {
|
||||
this.childBlocks_[i].setDragging_(adding);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Drag this block to follow the mouse.
|
||||
* @param {!Event} e Mouse move event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.onMouseMove_ = function(e) {
|
||||
if (e.type == 'mousemove' && e.clientX <= 1 && e.clientY == 0 &&
|
||||
e.button == 0) {
|
||||
/* HACK:
|
||||
Safari Mobile 6.0 and Chrome for Android 18.0 fire rogue mousemove
|
||||
events on certain touch actions. Ignore events with these signatures.
|
||||
This may result in a one-pixel blind spot in other browsers,
|
||||
but this shouldn't be noticeable. */
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
var oldXY = this.getRelativeToSurfaceXY();
|
||||
var newXY = this.workspace.moveDrag(e);
|
||||
|
||||
if (Blockly.dragMode_ == Blockly.DRAG_STICKY) {
|
||||
// Still dragging within the sticky DRAG_RADIUS.
|
||||
var dr = goog.math.Coordinate.distance(oldXY, newXY) * this.workspace.scale;
|
||||
if (dr > Blockly.DRAG_RADIUS) {
|
||||
// Switch to unrestricted dragging.
|
||||
Blockly.dragMode_ = Blockly.DRAG_FREE;
|
||||
Blockly.longStop_();
|
||||
this.workspace.setResizesEnabled(false);
|
||||
|
||||
var disconnectEffect = !!this.parentBlock_;
|
||||
// If in a stack, either split the stack, or pull out single block.
|
||||
var healStack = !Blockly.DRAG_STACK;
|
||||
if (e.altKey || e.ctrlKey || e.metaKey) {
|
||||
healStack = !healStack;
|
||||
}
|
||||
// Push this block to the very top of the stack.
|
||||
this.unplug(healStack);
|
||||
if (disconnectEffect) {
|
||||
var group = this.getSvgRoot();
|
||||
group.translate_ = 'translate(' + newXY.x + ',' + newXY.y + ')';
|
||||
this.disconnectUiEffect();
|
||||
}
|
||||
this.setDragging_(true);
|
||||
this.moveToDragSurface_();
|
||||
}
|
||||
}
|
||||
if (Blockly.dragMode_ == Blockly.DRAG_FREE) {
|
||||
// Unrestricted dragging.
|
||||
var dxy = goog.math.Coordinate.difference(oldXY, this.dragStartXY_);
|
||||
var group = this.getSvgRoot();
|
||||
if (this.useDragSurface_) {
|
||||
this.workspace.blockDragSurface_.translateSurface(newXY.x, newXY.y);
|
||||
} else {
|
||||
group.translate_ = 'translate(' + newXY.x + ',' + newXY.y + ')';
|
||||
group.setAttribute('transform', group.translate_ + group.skew_);
|
||||
}
|
||||
// Drag all the nested bubbles.
|
||||
for (var i = 0; i < this.draggedBubbles_.length; i++) {
|
||||
var commentData = this.draggedBubbles_[i];
|
||||
commentData.bubble.setIconLocation(
|
||||
goog.math.Coordinate.sum(commentData, dxy));
|
||||
}
|
||||
|
||||
// Check to see if any of this block's connections are within range of
|
||||
// another block's connection.
|
||||
var myConnections = this.getConnections_(false);
|
||||
// Also check the last connection on this stack
|
||||
var lastOnStack = this.lastConnectionInStack_();
|
||||
if (lastOnStack && lastOnStack != this.nextConnection) {
|
||||
myConnections.push(lastOnStack);
|
||||
}
|
||||
var closestConnection = null;
|
||||
var localConnection = null;
|
||||
var radiusConnection = Blockly.SNAP_RADIUS;
|
||||
for (var i = 0; i < myConnections.length; i++) {
|
||||
var myConnection = myConnections[i];
|
||||
var neighbour = myConnection.closest(radiusConnection, dxy);
|
||||
if (neighbour.connection) {
|
||||
closestConnection = neighbour.connection;
|
||||
localConnection = myConnection;
|
||||
radiusConnection = neighbour.radius;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove connection highlighting if needed.
|
||||
if (Blockly.highlightedConnection_ &&
|
||||
Blockly.highlightedConnection_ != closestConnection) {
|
||||
Blockly.highlightedConnection_.unhighlight();
|
||||
Blockly.highlightedConnection_ = null;
|
||||
Blockly.localConnection_ = null;
|
||||
}
|
||||
|
||||
var wouldDeleteBlock = this.updateCursor_(e, closestConnection);
|
||||
|
||||
// Add connection highlighting if needed.
|
||||
if (!wouldDeleteBlock && closestConnection &&
|
||||
closestConnection != Blockly.highlightedConnection_) {
|
||||
closestConnection.highlight();
|
||||
Blockly.highlightedConnection_ = closestConnection;
|
||||
Blockly.localConnection_ = localConnection;
|
||||
}
|
||||
}
|
||||
// This event has been handled. No need to bubble up to the document.
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide visual indication of whether the block will be deleted if
|
||||
* dropped here.
|
||||
* Prefer connecting over dropping into the trash can, but prefer dragging to
|
||||
* the toolbox over connecting to other blocks.
|
||||
* @param {!Event} e Mouse move event.
|
||||
* @param {Blockly.Connection} closestConnection The connection this block would
|
||||
* potentially connect to if dropped here, or null.
|
||||
* @return {boolean} True if the block would be deleted if dropped here,
|
||||
* otherwise false.
|
||||
* @private
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.updateCursor_ = function(e, closestConnection) {
|
||||
var deleteArea = this.workspace.isDeleteArea(e);
|
||||
var wouldConnect = Blockly.selected && closestConnection &&
|
||||
deleteArea != Blockly.DELETE_AREA_TOOLBOX;
|
||||
var wouldDelete = deleteArea && !this.getParent() &&
|
||||
Blockly.selected.isDeletable();
|
||||
var showDeleteCursor = wouldDelete && !wouldConnect;
|
||||
|
||||
if (showDeleteCursor) {
|
||||
Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_),
|
||||
'blocklyDraggingDelete');
|
||||
|
||||
if (this.workspace.toolbox_) {
|
||||
// Change the cursor to a hand with an 'x'
|
||||
this.workspace.toolbox_.addDeleteStyle();
|
||||
}
|
||||
|
||||
if (deleteArea == Blockly.DELETE_AREA_TRASH && this.workspace.trashcan) {
|
||||
this.workspace.trashcan.setOpen_(true);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (this.workspace.trashcan) {
|
||||
this.workspace.trashcan.setOpen_(false);
|
||||
}
|
||||
Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_),
|
||||
'blocklyDraggingDelete');
|
||||
if (this.workspace.toolbox_) {
|
||||
// Change the cursor on the toolbox
|
||||
this.workspace.toolbox_.removeDeleteStyle();
|
||||
}
|
||||
return false;
|
||||
this.childBlocks_[i].setDragging(adding);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1165,7 +824,7 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
|
||||
// If this block is being dragged, unlink the mouse events.
|
||||
if (Blockly.selected == this) {
|
||||
this.unselect();
|
||||
Blockly.terminateDrag_();
|
||||
this.workspace.cancelCurrentGesture();
|
||||
}
|
||||
// If this block has a context menu open, close it.
|
||||
if (Blockly.ContextMenu.currentBlock == this) {
|
||||
@@ -1204,7 +863,7 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) {
|
||||
* Play some UI effects (sound, animation) when disposing of a block.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.disposeUiEffect = function() {
|
||||
this.workspace.playAudio('delete');
|
||||
this.workspace.getAudioManager().play('delete');
|
||||
|
||||
var xy = this.workspace.getSvgXY(/** @type {!Element} */ (this.svgGroup_));
|
||||
// Deeply clone the current block.
|
||||
@@ -1253,7 +912,7 @@ Blockly.BlockSvg.disposeUiStep_ = function(clone, rtl, start, workspaceScale) {
|
||||
* Play some UI effects (sound, ripple) after a connection has been established.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.connectionUiEffect = function() {
|
||||
this.workspace.playAudio('click');
|
||||
this.workspace.getAudioManager().play('click');
|
||||
if (this.workspace.scale < 1) {
|
||||
return; // Too small to care about visual effects.
|
||||
}
|
||||
@@ -1301,7 +960,7 @@ Blockly.BlockSvg.connectionUiStep_ = function(ripple, start, workspaceScale) {
|
||||
* Play some UI effects (sound, animation) when disconnecting a block.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.disconnectUiEffect = function() {
|
||||
this.workspace.playAudio('disconnect');
|
||||
this.workspace.getAudioManager().play('disconnect');
|
||||
if (this.workspace.scale < 1) {
|
||||
return; // Too small to care about visual effects.
|
||||
}
|
||||
@@ -1493,7 +1152,7 @@ Blockly.BlockSvg.prototype.setWarningText = function(text, opt_id) {
|
||||
clearTimeout(this.setWarningText.pid_[id]);
|
||||
delete this.setWarningText.pid_[id];
|
||||
}
|
||||
if (Blockly.dragMode_ == Blockly.DRAG_FREE) {
|
||||
if (this.workspace.isDragging()) {
|
||||
// Don't change the warning text during a drag.
|
||||
// Wait until the drag finishes.
|
||||
var thisBlock = this;
|
||||
@@ -1614,6 +1273,22 @@ Blockly.BlockSvg.prototype.removeSelect = function() {
|
||||
'blocklySelected');
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the cursor over this block by adding or removing a class.
|
||||
* @param {boolean} enable True if the delete cursor should be shown, false
|
||||
* otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.setDeleteStyle = function(enable) {
|
||||
if (enable) {
|
||||
Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_),
|
||||
'blocklyDraggingDelete');
|
||||
} else {
|
||||
Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_),
|
||||
'blocklyDraggingDelete');
|
||||
}
|
||||
};
|
||||
|
||||
// Overrides of functions on Blockly.Block that take into account whether the
|
||||
// block has been rendered.
|
||||
|
||||
@@ -1629,14 +1304,14 @@ Blockly.BlockSvg.prototype.setColour = function(colour) {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Move this block to the front of the visible workspace.
|
||||
* <g> tags do not respect z-index so svg renders them in the
|
||||
* order that they are in the dom. By placing this block first within the
|
||||
* block group's <g>, it will render on top of any other blocks.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.bringToFront_ = function() {
|
||||
Blockly.BlockSvg.prototype.bringToFront = function() {
|
||||
var block = this;
|
||||
do {
|
||||
var root = block.getSvgRoot();
|
||||
@@ -1644,6 +1319,7 @@ Blockly.BlockSvg.prototype.bringToFront_ = function() {
|
||||
block = block.getParent();
|
||||
} while (block);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set whether this block can chain onto the bottom of another block.
|
||||
* @param {boolean} newBoolean True if there can be a previous statement.
|
||||
@@ -1767,7 +1443,7 @@ Blockly.BlockSvg.prototype.appendInput_ = function(type, name) {
|
||||
* Otherwise, for a non-rendered block return an empty list, and for a
|
||||
* collapsed block don't return inputs connections.
|
||||
* @return {!Array.<!Blockly.Connection>} Array of connections.
|
||||
* @private
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.getConnections_ = function(all) {
|
||||
var myConnections = [];
|
||||
@@ -1847,3 +1523,26 @@ Blockly.BlockSvg.prototype.bumpNeighbours_ = function() {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Schedule snapping to grid and bumping neighbours to occur after a brief
|
||||
* delay.
|
||||
* @package
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.scheduleSnapAndBump = function() {
|
||||
var block = this;
|
||||
// Ensure that any snap and bump are part of this move's event group.
|
||||
var group = Blockly.Events.getGroup();
|
||||
|
||||
setTimeout(function() {
|
||||
Blockly.Events.setGroup(group);
|
||||
block.snapToGrid();
|
||||
Blockly.Events.setGroup(false);
|
||||
}, Blockly.BUMP_DELAY / 2);
|
||||
|
||||
setTimeout(function() {
|
||||
Blockly.Events.setGroup(group);
|
||||
block.bumpNeighbours_();
|
||||
Blockly.Events.setGroup(false);
|
||||
}, Blockly.BUMP_DELAY);
|
||||
};
|
||||
|
||||
+11
-41
@@ -73,20 +73,6 @@ Blockly.mainWorkspace = null;
|
||||
*/
|
||||
Blockly.selected = null;
|
||||
|
||||
/**
|
||||
* Currently highlighted connection (during a drag).
|
||||
* @type {Blockly.Connection}
|
||||
* @private
|
||||
*/
|
||||
Blockly.highlightedConnection_ = null;
|
||||
|
||||
/**
|
||||
* Connection on dragged block that matches the highlighted connection.
|
||||
* @type {Blockly.Connection}
|
||||
* @private
|
||||
*/
|
||||
Blockly.localConnection_ = null;
|
||||
|
||||
/**
|
||||
* All of the connections on blocks that are currently being dragged.
|
||||
* @type {!Array.<!Blockly.Connection>}
|
||||
@@ -108,15 +94,6 @@ Blockly.clipboardXml_ = null;
|
||||
*/
|
||||
Blockly.clipboardSource_ = null;
|
||||
|
||||
/**
|
||||
* Is the mouse dragging a block?
|
||||
* DRAG_NONE - No drag operation.
|
||||
* DRAG_STICKY - Still inside the sticky DRAG_RADIUS.
|
||||
* DRAG_FREE - Freely draggable.
|
||||
* @private
|
||||
*/
|
||||
Blockly.dragMode_ = Blockly.DRAG_NONE;
|
||||
|
||||
/**
|
||||
* Cached value for whether 3D is supported.
|
||||
* @type {!boolean}
|
||||
@@ -206,10 +183,18 @@ Blockly.onKeyDown_ = function(e) {
|
||||
// Do this first to prevent an error in the delete code from resulting in
|
||||
// data loss.
|
||||
e.preventDefault();
|
||||
// Don't delete while dragging. Jeez.
|
||||
if (Blockly.mainWorkspace.isDragging()) {
|
||||
return;
|
||||
}
|
||||
if (Blockly.selected && Blockly.selected.isDeletable()) {
|
||||
deleteBlock = true;
|
||||
}
|
||||
} else if (e.altKey || e.ctrlKey || e.metaKey) {
|
||||
// Don't use meta keys during drags.
|
||||
if (Blockly.mainWorkspace.isDragging()) {
|
||||
return;
|
||||
}
|
||||
if (Blockly.selected &&
|
||||
Blockly.selected.isDeletable() && Blockly.selected.isMovable()) {
|
||||
if (e.keyCode == 67) {
|
||||
@@ -239,25 +224,11 @@ Blockly.onKeyDown_ = function(e) {
|
||||
// Common code for delete and cut.
|
||||
Blockly.Events.setGroup(true);
|
||||
Blockly.hideChaff();
|
||||
var heal = Blockly.dragMode_ != Blockly.DRAG_FREE;
|
||||
Blockly.selected.dispose(heal, true);
|
||||
if (Blockly.highlightedConnection_) {
|
||||
Blockly.highlightedConnection_.unhighlight();
|
||||
Blockly.highlightedConnection_ = null;
|
||||
}
|
||||
Blockly.selected.dispose(/* heal */ true, true);
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop binding to the global mouseup and mousemove events.
|
||||
* @private
|
||||
*/
|
||||
Blockly.terminateDrag_ = function() {
|
||||
Blockly.BlockSvg.terminateDrag();
|
||||
Blockly.Flyout.terminateDrag_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Copy a block onto the local clipboard.
|
||||
* @param {!Blockly.Block} block Block to be copied.
|
||||
@@ -265,9 +236,8 @@ Blockly.terminateDrag_ = function() {
|
||||
*/
|
||||
Blockly.copy_ = function(block) {
|
||||
var xmlBlock = Blockly.Xml.blockToDom(block);
|
||||
if (Blockly.dragMode_ != Blockly.DRAG_FREE) {
|
||||
Blockly.Xml.deleteNext(xmlBlock);
|
||||
}
|
||||
// Copy only the selected block and internal blocks.
|
||||
Blockly.Xml.deleteNext(xmlBlock);
|
||||
// Encode start position in XML.
|
||||
var xy = block.getRelativeToSurfaceXY();
|
||||
xmlBlock.setAttribute('x', block.RTL ? -xy.x : xy.x);
|
||||
|
||||
+2
-2
@@ -119,7 +119,7 @@ Blockly.Comment.prototype.createEditor_ = function() {
|
||||
});
|
||||
Blockly.bindEventWithChecks_(textarea, 'change', this, function(e) {
|
||||
if (this.text_ != textarea.value) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this.block_, 'comment', null, this.text_, textarea.value));
|
||||
this.text_ = textarea.value;
|
||||
}
|
||||
@@ -257,7 +257,7 @@ Blockly.Comment.prototype.getText = function() {
|
||||
*/
|
||||
Blockly.Comment.prototype.setText = function(text) {
|
||||
if (this.text_ != text) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this.block_, 'comment', null, this.text_, text));
|
||||
this.text_ = text;
|
||||
}
|
||||
|
||||
+2
-8
@@ -219,7 +219,7 @@ Blockly.Connection.prototype.connect_ = function(childConnection) {
|
||||
|
||||
var event;
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
event = new Blockly.Events.Move(childBlock);
|
||||
event = new Blockly.Events.BlockMove(childBlock);
|
||||
}
|
||||
// Establish the connections.
|
||||
Blockly.Connection.connectReciprocally_(parentConnection, childConnection);
|
||||
@@ -241,12 +241,6 @@ Blockly.Connection.prototype.dispose = function() {
|
||||
if (this.inDB_) {
|
||||
this.db_.removeConnection_(this);
|
||||
}
|
||||
if (Blockly.highlightedConnection_ == this) {
|
||||
Blockly.highlightedConnection_ = null;
|
||||
}
|
||||
if (Blockly.localConnection_ == this) {
|
||||
Blockly.localConnection_ = null;
|
||||
}
|
||||
this.db_ = null;
|
||||
this.dbOpposite_ = null;
|
||||
};
|
||||
@@ -508,7 +502,7 @@ Blockly.Connection.prototype.disconnectInternal_ = function(parentBlock,
|
||||
childBlock) {
|
||||
var event;
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
event = new Blockly.Events.Move(childBlock);
|
||||
event = new Blockly.Events.BlockMove(childBlock);
|
||||
}
|
||||
var otherConnection = this.targetConnection;
|
||||
otherConnection.targetConnection = null;
|
||||
|
||||
@@ -32,6 +32,13 @@ goog.provide('Blockly.constants');
|
||||
*/
|
||||
Blockly.DRAG_RADIUS = 5;
|
||||
|
||||
/**
|
||||
* Number of pixels the mouse must move before a drag/scroll starts from the
|
||||
* flyout. Because the drag-intention is determined when this is reached, it is
|
||||
* larger than Blockly.DRAG_RADIUS so that the drag-direction is clearer.
|
||||
*/
|
||||
Blockly.FLYOUT_DRAG_RADIUS = 10;
|
||||
|
||||
/**
|
||||
* Maximum misalignment between connections for them to snap together.
|
||||
*/
|
||||
@@ -207,6 +214,12 @@ Blockly.TOOLBOX_AT_LEFT = 2;
|
||||
*/
|
||||
Blockly.TOOLBOX_AT_RIGHT = 3;
|
||||
|
||||
/**
|
||||
* ENUM representing that an event is not in any delete areas.
|
||||
* Null for backwards compatibility reasons.
|
||||
* @const
|
||||
*/
|
||||
Blockly.DELETE_AREA_NONE = null;
|
||||
|
||||
/**
|
||||
* ENUM representing that an event is in the delete area of the trash can.
|
||||
@@ -236,3 +249,19 @@ Blockly.VARIABLE_CATEGORY_NAME = 'VARIABLE';
|
||||
* @const {string}
|
||||
*/
|
||||
Blockly.PROCEDURE_CATEGORY_NAME = 'PROCEDURE';
|
||||
|
||||
/**
|
||||
* String for use in the dropdown created in field_variable.
|
||||
* This string indicates that this option in the dropdown is 'Rename
|
||||
* variable...' and if selected, should trigger the prompt to rename a variable.
|
||||
* @const {string}
|
||||
*/
|
||||
Blockly.RENAME_VARIABLE_ID = 'RENAME_VARIABLE_ID';
|
||||
|
||||
/**
|
||||
* String for use in the dropdown created in field_variable.
|
||||
* This string indicates that this option in the dropdown is 'Delete the "%1"
|
||||
* variable' and if selected, should trigger the prompt to delete a variable.
|
||||
* @const {string}
|
||||
*/
|
||||
Blockly.DELETE_VARIABLE_ID = 'DELETE_VARIABLE_ID';
|
||||
|
||||
+45
-11
@@ -43,6 +43,12 @@ goog.require('goog.ui.MenuItem');
|
||||
*/
|
||||
Blockly.ContextMenu.currentBlock = null;
|
||||
|
||||
/**
|
||||
* @type {Array.<!Array>} Opaque data that can be passed to unbindEvent_.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ContextMenu.eventWrapper_ = null;
|
||||
|
||||
/**
|
||||
* Construct the menu based on the list of options and show the menu.
|
||||
* @param {!Event} e Mouse event.
|
||||
@@ -55,12 +61,33 @@ Blockly.ContextMenu.show = function(e, options, rtl) {
|
||||
Blockly.ContextMenu.hide();
|
||||
return;
|
||||
}
|
||||
var menu = Blockly.ContextMenu.populate_(options, rtl);
|
||||
|
||||
goog.events.listen(menu, goog.ui.Component.EventType.ACTION,
|
||||
Blockly.ContextMenu.hide);
|
||||
|
||||
Blockly.ContextMenu.position_(menu, e, rtl);
|
||||
// 1ms delay is required for focusing on context menus because some other
|
||||
// mouse event is still waiting in the queue and clears focus.
|
||||
setTimeout(function() {menu.getElement().focus();}, 1);
|
||||
Blockly.ContextMenu.currentBlock = null; // May be set by Blockly.Block.
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the context menu object and populate it with the given options.
|
||||
* @param {!Array.<!Object>} options Array of menu options.
|
||||
* @param {boolean} rtl True if RTL, false if LTR.
|
||||
* @return {!goog.ui.Menu} The menu that will be shown on right click.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ContextMenu.populate_ = function(options, rtl) {
|
||||
/* Here's what one option object looks like:
|
||||
{text: 'Make It So',
|
||||
enabled: true,
|
||||
callback: Blockly.MakeItSo}
|
||||
*/
|
||||
var menu = new goog.ui.Menu();
|
||||
menu.setAllowAutoFocus(true);
|
||||
menu.setRightToLeft(rtl);
|
||||
for (var i = 0, option; option = options[i]; i++) {
|
||||
var menuItem = new goog.ui.MenuItem(option.text);
|
||||
@@ -70,15 +97,25 @@ Blockly.ContextMenu.show = function(e, options, rtl) {
|
||||
if (option.enabled) {
|
||||
goog.events.listen(menuItem, goog.ui.Component.EventType.ACTION,
|
||||
option.callback);
|
||||
menuItem.handleContextMenu = function(e) {
|
||||
menuItem.handleContextMenu = function(/* e */) {
|
||||
// Right-clicking on menu option should count as a click.
|
||||
goog.events.dispatchEvent(this, goog.ui.Component.EventType.ACTION);
|
||||
};
|
||||
}
|
||||
}
|
||||
goog.events.listen(menu, goog.ui.Component.EventType.ACTION,
|
||||
Blockly.ContextMenu.hide);
|
||||
// Record windowSize and scrollOffset before adding menu.
|
||||
return menu;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the menu to the page and position it correctly.
|
||||
* @param {!goog.ui.Menu} menu The menu to add and position.
|
||||
* @param {!Event} e Mouse event for the right click that is making the context
|
||||
* menu appear.
|
||||
* @param {boolean} rtl True if RTL, false if LTR.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ContextMenu.position_ = function(menu, e, rtl) {
|
||||
// Record windowSize and scrollOffset before adding menu.
|
||||
var windowSize = goog.dom.getViewportSize();
|
||||
var scrollOffset = goog.style.getViewportPageOffset(document);
|
||||
var div = Blockly.WidgetDiv.DIV;
|
||||
@@ -109,12 +146,6 @@ Blockly.ContextMenu.show = function(e, options, rtl) {
|
||||
}
|
||||
}
|
||||
Blockly.WidgetDiv.position(x, y, windowSize, scrollOffset, rtl);
|
||||
|
||||
menu.setAllowAutoFocus(true);
|
||||
// 1ms delay is required for focusing on context menus because some other
|
||||
// mouse event is still waiting in the queue and clears focus.
|
||||
setTimeout(function() {menuDom.focus();}, 1);
|
||||
Blockly.ContextMenu.currentBlock = null; // May be set by Blockly.Block.
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -123,6 +154,9 @@ Blockly.ContextMenu.show = function(e, options, rtl) {
|
||||
Blockly.ContextMenu.hide = function() {
|
||||
Blockly.WidgetDiv.hideIfOwner(Blockly.ContextMenu);
|
||||
Blockly.ContextMenu.currentBlock = null;
|
||||
if (Blockly.ContextMenu.eventWrapper_) {
|
||||
Blockly.unbindEvent_(Blockly.ContextMenu.eventWrapper_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -150,7 +184,7 @@ Blockly.ContextMenu.callbackFactory = function(block, xml) {
|
||||
Blockly.Events.enable();
|
||||
}
|
||||
if (Blockly.Events.isEnabled() && !newBlock.isShadow()) {
|
||||
Blockly.Events.fire(new Blockly.Events.Create(newBlock));
|
||||
Blockly.Events.fire(new Blockly.Events.BlockCreate(newBlock));
|
||||
}
|
||||
newBlock.select();
|
||||
};
|
||||
|
||||
@@ -130,6 +130,7 @@ Blockly.Css.CONTENT = [
|
||||
'height: 100%;',
|
||||
'position: relative;',
|
||||
'overflow: hidden;', /* So blocks in drag surface disappear at edges */
|
||||
'touch-action: none',
|
||||
'}',
|
||||
|
||||
'.blocklyNonSelectable {',
|
||||
@@ -409,6 +410,10 @@ Blockly.Css.CONTENT = [
|
||||
'fill-opacity: .8;',
|
||||
'}',
|
||||
|
||||
'.blocklyTransparentBackground {',
|
||||
'opacity: 0;',
|
||||
'}',
|
||||
|
||||
'.blocklyMainWorkspaceScrollbar {',
|
||||
'z-index: 20;',
|
||||
'}',
|
||||
@@ -601,6 +606,10 @@ Blockly.Css.CONTENT = [
|
||||
'vertical-align: middle;',
|
||||
'}',
|
||||
|
||||
'.blocklyToolboxDelete .blocklyTreeLabel {',
|
||||
'cursor: url("<<<PATH>>>/handdelete.cur"), auto;',
|
||||
'}',
|
||||
|
||||
'.blocklyTreeSelected .blocklyTreeLabel {',
|
||||
'color: #fff;',
|
||||
'}',
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Class that controls updates to connections during drags.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.DraggedConnectionManager');
|
||||
|
||||
goog.require('Blockly.RenderedConnection');
|
||||
|
||||
goog.require('goog.math.Coordinate');
|
||||
|
||||
|
||||
/**
|
||||
* Class that controls updates to connections during drags. It is primarily
|
||||
* responsible for finding the closest eligible connection and highlighting or
|
||||
* unhiglighting it as needed during a drag.
|
||||
* @param {!Blockly.BlockSvg} block The top block in the stack being dragged.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.DraggedConnectionManager = function(block) {
|
||||
Blockly.selected = block;
|
||||
|
||||
/**
|
||||
* The top block in the stack being dragged.
|
||||
* Does not change during a drag.
|
||||
* @type {!Blockly.Block}
|
||||
* @private
|
||||
*/
|
||||
this.topBlock_ = block;
|
||||
|
||||
/**
|
||||
* The workspace on which these connections are being dragged.
|
||||
* Does not change during a drag.
|
||||
* @type {!Blockly.WorkspaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.workspace_ = block.workspace;
|
||||
|
||||
/**
|
||||
* The connections on the dragging blocks that are available to connect to
|
||||
* other blocks. This includes all open connections on the top block, as well
|
||||
* as the last connection on the block stack.
|
||||
* Does not change during a drag.
|
||||
* @type {!Array.<!Blockly.RenderedConnection>}
|
||||
* @private
|
||||
*/
|
||||
this.availableConnections_ = this.initAvailableConnections_();
|
||||
|
||||
/**
|
||||
* The connection that this block would connect to if released immediately.
|
||||
* Updated on every mouse move.
|
||||
* @type {Blockly.RenderedConnection}
|
||||
* @private
|
||||
*/
|
||||
this.closestConnection_ = null;
|
||||
|
||||
/**
|
||||
* The connection that would connect to this.closestConnection_ if this block
|
||||
* were released immediately.
|
||||
* Updated on every mouse move.
|
||||
* @type {Blockly.RenderedConnection}
|
||||
* @private
|
||||
*/
|
||||
this.localConnection_ = null;
|
||||
|
||||
/**
|
||||
* The distance between this.closestConnection_ and this.localConnection_,
|
||||
* in workspace units.
|
||||
* Updated on every mouse move.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.radiusConnection_ = 0;
|
||||
|
||||
/**
|
||||
* Whether the block would be deleted if it were dropped immediately.
|
||||
* Updated on every mouse move.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.wouldDeleteBlock_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sever all links from this object.
|
||||
* @package
|
||||
*/
|
||||
Blockly.DraggedConnectionManager.prototype.dispose = function() {
|
||||
this.topBlock_ = null;
|
||||
this.workspace_ = null;
|
||||
this.availableConnections_.length = 0;
|
||||
this.closestConnection_ = null;
|
||||
this.localConnection_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return whether the block would be deleted if dropped immediately, based on
|
||||
* information from the most recent move event.
|
||||
* @return {boolean} true if the block would be deleted if dropped immediately.
|
||||
* @package
|
||||
*/
|
||||
Blockly.DraggedConnectionManager.prototype.wouldDeleteBlock = function() {
|
||||
return this.wouldDeleteBlock_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect to the closest connection and render the results.
|
||||
* This should be called at the end of a drag.
|
||||
* @package
|
||||
*/
|
||||
Blockly.DraggedConnectionManager.prototype.applyConnections = function() {
|
||||
if (this.closestConnection_) {
|
||||
// Connect two blocks together.
|
||||
this.localConnection_.connect(this.closestConnection_);
|
||||
if (this.topBlock_.rendered) {
|
||||
// Trigger a connection animation.
|
||||
// Determine which connection is inferior (lower in the source stack).
|
||||
var inferiorConnection = this.localConnection_.isSuperior() ?
|
||||
this.closestConnection_ : this.localConnection_;
|
||||
inferiorConnection.getSourceBlock().connectionUiEffect();
|
||||
}
|
||||
this.removeHighlighting_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update highlighted connections based on the most recent move location.
|
||||
* @param {!goog.math.Coordinate} dxy Position relative to drag start,
|
||||
* in workspace units.
|
||||
* @param {?number} deleteArea One of {@link Blockly.DELETE_AREA_TRASH},
|
||||
* {@link Blockly.DELETE_AREA_TOOLBOX}, or {@link Blockly.DELETE_AREA_NONE}.
|
||||
* @package
|
||||
*/
|
||||
Blockly.DraggedConnectionManager.prototype.update = function(dxy, deleteArea) {
|
||||
var oldClosestConnection = this.closestConnection_;
|
||||
var closestConnectionChanged = this.updateClosest_(dxy);
|
||||
|
||||
if (closestConnectionChanged && oldClosestConnection) {
|
||||
oldClosestConnection.unhighlight();
|
||||
}
|
||||
|
||||
// Prefer connecting over dropping into the trash can, but prefer dragging to
|
||||
// the toolbox over connecting to other blocks.
|
||||
var wouldConnect = !!this.closestConnection_ &&
|
||||
deleteArea != Blockly.DELETE_AREA_TOOLBOX;
|
||||
var wouldDelete = !!deleteArea && !this.topBlock_.getParent() &&
|
||||
this.topBlock_.isDeletable();
|
||||
this.wouldDeleteBlock_ = wouldDelete && !wouldConnect;
|
||||
|
||||
if (!this.wouldDeleteBlock_ && closestConnectionChanged &&
|
||||
this.closestConnection_) {
|
||||
this.addHighlighting_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove highlighting from the currently highlighted connection, if it exists.
|
||||
* @private
|
||||
*/
|
||||
Blockly.DraggedConnectionManager.prototype.removeHighlighting_ = function() {
|
||||
if (this.closestConnection_) {
|
||||
this.closestConnection_.unhighlight();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add highlighting to the closest connection, if it exists.
|
||||
* @private
|
||||
*/
|
||||
Blockly.DraggedConnectionManager.prototype.addHighlighting_ = function() {
|
||||
if (this.closestConnection_) {
|
||||
this.closestConnection_.highlight();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Populate the list of available connections on this block stack. This should
|
||||
* only be called once, at the beginning of a drag.
|
||||
* @return {!Array.<!Blockly.RenderedConnection>} a list of available
|
||||
* connections.
|
||||
* @private
|
||||
*/
|
||||
Blockly.DraggedConnectionManager.prototype.initAvailableConnections_ = function() {
|
||||
var available = this.topBlock_.getConnections_(false);
|
||||
// Also check the last connection on this stack
|
||||
var lastOnStack = this.topBlock_.lastConnectionInStack_();
|
||||
if (lastOnStack && lastOnStack != this.topBlock_.nextConnection) {
|
||||
available.push(lastOnStack);
|
||||
}
|
||||
return available;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the new closest connection, and update internal state in response.
|
||||
* @param {!goog.math.Coordinate} dxy Position relative to the drag start,
|
||||
* in workspace units.
|
||||
* @return {boolean} Whether the closest connection has changed.
|
||||
* @private
|
||||
*/
|
||||
Blockly.DraggedConnectionManager.prototype.updateClosest_ = function(dxy) {
|
||||
var oldClosestConnection = this.closestConnection_;
|
||||
|
||||
this.closestConnection_ = null;
|
||||
this.localConnection_ = null;
|
||||
this.radiusConnection_ = Blockly.SNAP_RADIUS;
|
||||
for (var i = 0; i < this.availableConnections_.length; i++) {
|
||||
var myConnection = this.availableConnections_[i];
|
||||
var neighbour = myConnection.closest(this.radiusConnection_, dxy);
|
||||
if (neighbour.connection) {
|
||||
this.closestConnection_ = neighbour.connection;
|
||||
this.localConnection_ = myConnection;
|
||||
this.radiusConnection_ = neighbour.radius;
|
||||
}
|
||||
}
|
||||
return oldClosestConnection != this.closestConnection_;
|
||||
};
|
||||
+297
-14
@@ -55,29 +55,71 @@ Blockly.Events.recordUndo = true;
|
||||
Blockly.Events.disabled_ = 0;
|
||||
|
||||
/**
|
||||
* Name of event that creates a block.
|
||||
* Name of event that creates a block. Will be deprecated for BLOCK_CREATE.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.CREATE = 'create';
|
||||
|
||||
/**
|
||||
* Name of event that deletes a block.
|
||||
* Name of event that creates a block.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.BLOCK_CREATE = Blockly.Events.CREATE;
|
||||
|
||||
/**
|
||||
* Name of event that deletes a block. Will be deprecated for BLOCK_DELETE.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.DELETE = 'delete';
|
||||
|
||||
/**
|
||||
* Name of event that changes a block.
|
||||
* Name of event that deletes a block.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.BLOCK_DELETE = Blockly.Events.DELETE;
|
||||
|
||||
/**
|
||||
* Name of event that changes a block. Will be deprecated for BLOCK_CHANGE.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.CHANGE = 'change';
|
||||
|
||||
/**
|
||||
* Name of event that moves a block.
|
||||
* Name of event that changes a block.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.BLOCK_CHANGE = Blockly.Events.CHANGE;
|
||||
|
||||
/**
|
||||
* Name of event that moves a block. Will be deprecated for BLOCK_MOVE.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.MOVE = 'move';
|
||||
|
||||
/**
|
||||
* Name of event that moves a block.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.BLOCK_MOVE = Blockly.Events.MOVE;
|
||||
|
||||
/**
|
||||
* Name of event that creates a variable.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.VAR_CREATE = 'var_create';
|
||||
|
||||
/**
|
||||
* Name of event that deletes a variable.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.VAR_DELETE = 'var_delete';
|
||||
|
||||
/**
|
||||
* Name of event that renames a variable.
|
||||
* @const
|
||||
*/
|
||||
Blockly.Events.VAR_RENAME = 'var_rename';
|
||||
|
||||
/**
|
||||
* Name of event that records a UI change.
|
||||
* @const
|
||||
@@ -276,6 +318,15 @@ Blockly.Events.fromJson = function(json, workspace) {
|
||||
case Blockly.Events.MOVE:
|
||||
event = new Blockly.Events.Move(null);
|
||||
break;
|
||||
case Blockly.Events.VAR_CREATE:
|
||||
event = new Blockly.Events.VarCreate(null);
|
||||
break;
|
||||
case Blockly.Events.VAR_DELETE:
|
||||
event = new Blockly.Events.VarDelete(null);
|
||||
break;
|
||||
case Blockly.Events.VAR_RENAME:
|
||||
event = new Blockly.Events.VarRename(null);
|
||||
break;
|
||||
case Blockly.Events.UI:
|
||||
event = new Blockly.Events.Ui(null);
|
||||
break;
|
||||
@@ -289,13 +340,17 @@ Blockly.Events.fromJson = function(json, workspace) {
|
||||
|
||||
/**
|
||||
* Abstract class for an event.
|
||||
* @param {Blockly.Block} block The block.
|
||||
* @param {Blockly.Block|Blockly.VariableModel} elem The block or variable.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Events.Abstract = function(block) {
|
||||
if (block) {
|
||||
this.blockId = block.id;
|
||||
this.workspaceId = block.workspace.id;
|
||||
Blockly.Events.Abstract = function(elem) {
|
||||
if (elem instanceof Blockly.Block) {
|
||||
this.blockId = elem.id;
|
||||
this.workspaceId = elem.workspace.id;
|
||||
}
|
||||
else if (elem instanceof Blockly.VariableModel){
|
||||
this.workspaceId = elem.workspace.id;
|
||||
this.varId = elem.getId();
|
||||
}
|
||||
this.group = Blockly.Events.group_;
|
||||
this.recordUndo = Blockly.Events.recordUndo;
|
||||
@@ -312,6 +367,9 @@ Blockly.Events.Abstract.prototype.toJson = function() {
|
||||
if (this.blockId) {
|
||||
json['blockId'] = this.blockId;
|
||||
}
|
||||
if (this.varId) {
|
||||
json['varId'] = this.varId;
|
||||
}
|
||||
if (this.group) {
|
||||
json['group'] = this.group;
|
||||
}
|
||||
@@ -324,6 +382,7 @@ Blockly.Events.Abstract.prototype.toJson = function() {
|
||||
*/
|
||||
Blockly.Events.Abstract.prototype.fromJson = function(json) {
|
||||
this.blockId = json['blockId'];
|
||||
this.varId = json['varId'];
|
||||
this.group = json['group'];
|
||||
};
|
||||
|
||||
@@ -343,6 +402,21 @@ Blockly.Events.Abstract.prototype.run = function(forward) {
|
||||
// Defined by subclasses.
|
||||
};
|
||||
|
||||
/**
|
||||
* Get workspace the event belongs to.
|
||||
* @return {Blockly.Workspace} The workspace the event belongs to.
|
||||
* @throws {Error} if workspace is null.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Events.Abstract.prototype.getEventWorkspace_ = function() {
|
||||
var workspace = Blockly.Workspace.getById(this.workspaceId);
|
||||
if (!workspace) {
|
||||
throw Error('Workspace is null. Event must have been generated from real' +
|
||||
' Blockly events.');
|
||||
}
|
||||
return workspace;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for a block creation event.
|
||||
* @param {Blockly.Block} block The created block. Null for a blank event.
|
||||
@@ -364,6 +438,14 @@ Blockly.Events.Create = function(block) {
|
||||
};
|
||||
goog.inherits(Blockly.Events.Create, Blockly.Events.Abstract);
|
||||
|
||||
/**
|
||||
* Class for a block creation event.
|
||||
* @param {Blockly.Block} block The created block. Null for a blank event.
|
||||
* @extends {Blockly.Events.Abstract}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Events.BlockCreate = Blockly.Events.Create;
|
||||
|
||||
/**
|
||||
* Type of this event.
|
||||
* @type {string}
|
||||
@@ -396,7 +478,7 @@ Blockly.Events.Create.prototype.fromJson = function(json) {
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.Create.prototype.run = function(forward) {
|
||||
var workspace = Blockly.Workspace.getById(this.workspaceId);
|
||||
var workspace = this.getEventWorkspace_();
|
||||
if (forward) {
|
||||
var xml = goog.dom.createDom('xml');
|
||||
xml.appendChild(this.xml);
|
||||
@@ -438,6 +520,14 @@ Blockly.Events.Delete = function(block) {
|
||||
};
|
||||
goog.inherits(Blockly.Events.Delete, Blockly.Events.Abstract);
|
||||
|
||||
/**
|
||||
* Class for a block deletion event.
|
||||
* @param {Blockly.Block} block The deleted block. Null for a blank event.
|
||||
* @extends {Blockly.Events.Abstract}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Events.BlockDelete = Blockly.Events.Delete;
|
||||
|
||||
/**
|
||||
* Type of this event.
|
||||
* @type {string}
|
||||
@@ -468,7 +558,7 @@ Blockly.Events.Delete.prototype.fromJson = function(json) {
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.Delete.prototype.run = function(forward) {
|
||||
var workspace = Blockly.Workspace.getById(this.workspaceId);
|
||||
var workspace = this.getEventWorkspace_();
|
||||
if (forward) {
|
||||
for (var i = 0, id; id = this.ids[i]; i++) {
|
||||
var block = workspace.getBlockById(id);
|
||||
@@ -508,6 +598,18 @@ Blockly.Events.Change = function(block, element, name, oldValue, newValue) {
|
||||
};
|
||||
goog.inherits(Blockly.Events.Change, Blockly.Events.Abstract);
|
||||
|
||||
/**
|
||||
* Class for a block change event.
|
||||
* @param {Blockly.Block} block The changed block. Null for a blank event.
|
||||
* @param {string} element One of 'field', 'comment', 'disabled', etc.
|
||||
* @param {?string} name Name of input or field affected, or null.
|
||||
* @param {string} oldValue Previous value of element.
|
||||
* @param {string} newValue New value of element.
|
||||
* @extends {Blockly.Events.Abstract}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Events.BlockChange = Blockly.Events.Change;
|
||||
|
||||
/**
|
||||
* Type of this event.
|
||||
* @type {string}
|
||||
@@ -552,7 +654,7 @@ Blockly.Events.Change.prototype.isNull = function() {
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.Change.prototype.run = function(forward) {
|
||||
var workspace = Blockly.Workspace.getById(this.workspaceId);
|
||||
var workspace = this.getEventWorkspace_();
|
||||
var block = workspace.getBlockById(this.blockId);
|
||||
if (!block) {
|
||||
console.warn("Can't change non-existant block: " + this.blockId);
|
||||
@@ -624,6 +726,15 @@ Blockly.Events.Move = function(block) {
|
||||
};
|
||||
goog.inherits(Blockly.Events.Move, Blockly.Events.Abstract);
|
||||
|
||||
|
||||
/**
|
||||
* Class for a block move event. Created before the move.
|
||||
* @param {Blockly.Block} block The moved block. Null for a blank event.
|
||||
* @extends {Blockly.Events.Abstract}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Events.BlockMove = Blockly.Events.Move;
|
||||
|
||||
/**
|
||||
* Type of this event.
|
||||
* @type {string}
|
||||
@@ -712,7 +823,7 @@ Blockly.Events.Move.prototype.isNull = function() {
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.Move.prototype.run = function(forward) {
|
||||
var workspace = Blockly.Workspace.getById(this.workspaceId);
|
||||
var workspace = this.getEventWorkspace_();
|
||||
var block = workspace.getBlockById(this.blockId);
|
||||
if (!block) {
|
||||
console.warn("Can't move non-existant block: " + this.blockId);
|
||||
@@ -801,6 +912,178 @@ Blockly.Events.Ui.prototype.fromJson = function(json) {
|
||||
this.newValue = json['newValue'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for a variable creation event.
|
||||
* @param {Blockly.VariableModel} variable The created variable.
|
||||
* Null for a blank event.
|
||||
* @extends {Blockly.Events.Abstract}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Events.VarCreate = function(variable) {
|
||||
if (!variable) {
|
||||
return; // Blank event to be populated by fromJson.
|
||||
}
|
||||
Blockly.Events.VarCreate.superClass_.constructor.call(this, variable);
|
||||
this.varType = variable.type;
|
||||
this.varName = variable.name;
|
||||
};
|
||||
goog.inherits(Blockly.Events.VarCreate, Blockly.Events.Abstract);
|
||||
|
||||
/**
|
||||
* Type of this event.
|
||||
* @type {string}
|
||||
*/
|
||||
Blockly.Events.VarCreate.prototype.type = Blockly.Events.VAR_CREATE;
|
||||
|
||||
/**
|
||||
* Encode the event as JSON.
|
||||
* @return {!Object} JSON representation.
|
||||
*/
|
||||
Blockly.Events.VarCreate.prototype.toJson = function() {
|
||||
var json = Blockly.Events.VarCreate.superClass_.toJson.call(this);
|
||||
json['varType'] = this.varType;
|
||||
json['varName'] = this.varName;
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode the JSON event.
|
||||
* @param {!Object} json JSON representation.
|
||||
*/
|
||||
Blockly.Events.VarCreate.prototype.fromJson = function(json) {
|
||||
Blockly.Events.VarCreate.superClass_.fromJson.call(this, json);
|
||||
this.varType = json['varType'];
|
||||
this.varName = json['varName'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Run a variable creation event.
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.VarCreate.prototype.run = function(forward) {
|
||||
var workspace = this.getEventWorkspace_();
|
||||
if (forward) {
|
||||
workspace.createVariable(this.varName, this.varType, this.varId);
|
||||
} else {
|
||||
workspace.deleteVariableById(this.varId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for a variable deletion event.
|
||||
* @param {Blockly.VariableModel} variable The deleted variable.
|
||||
* Null for a blank event.
|
||||
* @extends {Blockly.Events.Abstract}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Events.VarDelete = function(variable) {
|
||||
if (!variable) {
|
||||
return; // Blank event to be populated by fromJson.
|
||||
}
|
||||
Blockly.Events.VarDelete.superClass_.constructor.call(this, variable);
|
||||
this.varType = variable.type;
|
||||
this.varName = variable.name;
|
||||
};
|
||||
goog.inherits(Blockly.Events.VarDelete, Blockly.Events.Abstract);
|
||||
|
||||
/**
|
||||
* Type of this event.
|
||||
* @type {string}
|
||||
*/
|
||||
Blockly.Events.VarDelete.prototype.type = Blockly.Events.VAR_DELETE;
|
||||
|
||||
/**
|
||||
* Encode the event as JSON.
|
||||
* @return {!Object} JSON representation.
|
||||
*/
|
||||
Blockly.Events.VarDelete.prototype.toJson = function() {
|
||||
var json = Blockly.Events.VarDelete.superClass_.toJson.call(this);
|
||||
json['varType'] = this.varType;
|
||||
json['varName'] = this.varName;
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode the JSON event.
|
||||
* @param {!Object} json JSON representation.
|
||||
*/
|
||||
Blockly.Events.VarDelete.prototype.fromJson = function(json) {
|
||||
Blockly.Events.VarDelete.superClass_.fromJson.call(this, json);
|
||||
this.varType = json['varType'];
|
||||
this.varName = json['varName'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Run a variable deletion event.
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.VarDelete.prototype.run = function(forward) {
|
||||
var workspace = this.getEventWorkspace_();
|
||||
if (forward) {
|
||||
workspace.deleteVariableById(this.varId);
|
||||
} else {
|
||||
workspace.createVariable(this.varName, this.varType, this.varId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for a variable rename event.
|
||||
* @param {Blockly.VariableModel} variable The renamed variable.
|
||||
* Null for a blank event.
|
||||
* @param {string} newName The new name the variable will be changed to.
|
||||
* @extends {Blockly.Events.Abstract}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Events.VarRename = function(variable, newName) {
|
||||
if (!variable) {
|
||||
return; // Blank event to be populated by fromJson.
|
||||
}
|
||||
Blockly.Events.VarRename.superClass_.constructor.call(this, variable);
|
||||
this.oldName = variable.name;
|
||||
this.newName = newName;
|
||||
};
|
||||
goog.inherits(Blockly.Events.VarRename, Blockly.Events.Abstract);
|
||||
|
||||
/**
|
||||
* Type of this event.
|
||||
* @type {string}
|
||||
*/
|
||||
Blockly.Events.VarRename.prototype.type = Blockly.Events.VAR_RENAME;
|
||||
|
||||
/**
|
||||
* Encode the event as JSON.
|
||||
* @return {!Object} JSON representation.
|
||||
*/
|
||||
Blockly.Events.VarRename.prototype.toJson = function() {
|
||||
var json = Blockly.Events.VarRename.superClass_.toJson.call(this);
|
||||
json['oldName'] = this.oldName;
|
||||
json['newName'] = this.newName;
|
||||
return json;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode the JSON event.
|
||||
* @param {!Object} json JSON representation.
|
||||
*/
|
||||
Blockly.Events.VarRename.prototype.fromJson = function(json) {
|
||||
Blockly.Events.VarRename.superClass_.fromJson.call(this, json);
|
||||
this.oldName = json['oldName'];
|
||||
this.newName = json['newName'];
|
||||
};
|
||||
|
||||
/**
|
||||
* Run a variable rename event.
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.VarRename.prototype.run = function(forward) {
|
||||
var workspace = this.getEventWorkspace_();
|
||||
if (forward) {
|
||||
workspace.renameVariableById(this.varId, this.newName);
|
||||
} else {
|
||||
workspace.renameVariableById(this.varId, this.oldName);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable/disable a block depending on whether it is properly connected.
|
||||
* Use this on applications where all blocks should be connected to a top block.
|
||||
@@ -821,7 +1104,7 @@ Blockly.Events.disableOrphans = function(event) {
|
||||
child.setDisabled(false);
|
||||
}
|
||||
} else if ((block.outputConnection || block.previousConnection) &&
|
||||
Blockly.dragMode_ == Blockly.DRAG_NONE) {
|
||||
!workspace.isDragging()) {
|
||||
do {
|
||||
block.setDisabled(true);
|
||||
block = block.getNextBlock();
|
||||
|
||||
+18
-27
@@ -28,6 +28,8 @@
|
||||
|
||||
goog.provide('Blockly.Field');
|
||||
|
||||
goog.require('Blockly.Gesture');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.math.Size');
|
||||
@@ -152,9 +154,9 @@ Blockly.Field.prototype.init = function() {
|
||||
|
||||
this.updateEditable();
|
||||
this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_);
|
||||
this.mouseUpWrapper_ =
|
||||
Blockly.bindEventWithChecks_(this.fieldGroup_, 'mouseup', this,
|
||||
this.onMouseUp_);
|
||||
this.mouseDownWrapper_ =
|
||||
Blockly.bindEventWithChecks_(this.fieldGroup_, 'mousedown', this,
|
||||
this.onMouseDown_);
|
||||
// Force a render.
|
||||
this.render_();
|
||||
};
|
||||
@@ -170,9 +172,9 @@ Blockly.Field.prototype.initModel = function() {
|
||||
* Dispose of all DOM objects belonging to this editable field.
|
||||
*/
|
||||
Blockly.Field.prototype.dispose = function() {
|
||||
if (this.mouseUpWrapper_) {
|
||||
Blockly.unbindEvent_(this.mouseUpWrapper_);
|
||||
this.mouseUpWrapper_ = null;
|
||||
if (this.mouseDownWrapper_) {
|
||||
Blockly.unbindEvent_(this.mouseDownWrapper_);
|
||||
this.mouseDownWrapper_ = null;
|
||||
}
|
||||
this.sourceBlock_ = null;
|
||||
goog.dom.removeNode(this.fieldGroup_);
|
||||
@@ -493,39 +495,28 @@ Blockly.Field.prototype.setValue = function(newValue) {
|
||||
return;
|
||||
}
|
||||
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this.sourceBlock_, 'field', this.name, oldValue, newValue));
|
||||
}
|
||||
this.setText(newValue);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a mouse up event on an editable field.
|
||||
* @param {!Event} e Mouse up event.
|
||||
* Handle a mouse down event on a field.
|
||||
* @param {!Event} e Mouse down event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Field.prototype.onMouseUp_ = function(e) {
|
||||
if ((goog.userAgent.IPHONE || goog.userAgent.IPAD) &&
|
||||
!goog.userAgent.isVersionOrHigher('537.51.2') &&
|
||||
e.layerX !== 0 && e.layerY !== 0) {
|
||||
// Old iOS spawns a bogus event on the next touch after a 'prompt()' edit.
|
||||
// Unlike the real events, these have a layerX and layerY set.
|
||||
Blockly.Field.prototype.onMouseDown_ = function(e) {
|
||||
if (!this.sourceBlock_ || !this.sourceBlock_.workspace) {
|
||||
return;
|
||||
} else if (Blockly.utils.isRightButton(e)) {
|
||||
// Right-click.
|
||||
return;
|
||||
} else if (this.sourceBlock_.workspace.isDragging()) {
|
||||
// Drag operation is concluding. Don't open the editor.
|
||||
return;
|
||||
} else if (this.sourceBlock_.isEditable()) {
|
||||
// Non-abstract sub-classes must define a showEditor_ method.
|
||||
this.showEditor_();
|
||||
// The field is handling the touch, but we also want the blockSvg onMouseUp
|
||||
// handler to fire, so we will leave the touch identifier as it is.
|
||||
// The next onMouseUp is responsible for nulling it out.
|
||||
}
|
||||
var gesture = this.sourceBlock_.workspace.getGesture(e);
|
||||
if (gesture) {
|
||||
gesture.setStartField(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Change the tooltip text for this field.
|
||||
* @param {string|!Element} newTip Text for tooltip or a parent element to
|
||||
|
||||
@@ -93,7 +93,7 @@ Blockly.FieldCheckbox.prototype.setValue = function(newBool) {
|
||||
(newBool.toUpperCase() == 'TRUE') : !!newBool;
|
||||
if (this.state_ !== newState) {
|
||||
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this.sourceBlock_, 'field', this.name, this.state_, newState));
|
||||
}
|
||||
this.state_ = newState;
|
||||
|
||||
@@ -101,7 +101,7 @@ Blockly.FieldColour.prototype.getValue = function() {
|
||||
Blockly.FieldColour.prototype.setValue = function(colour) {
|
||||
if (this.sourceBlock_ && Blockly.Events.isEnabled() &&
|
||||
this.colour_ != colour) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this.sourceBlock_, 'field', this.name, this.colour_, colour));
|
||||
}
|
||||
this.colour_ = colour;
|
||||
|
||||
@@ -328,7 +328,7 @@ Blockly.FieldDropdown.prototype.setValue = function(newValue) {
|
||||
return; // No change if null.
|
||||
}
|
||||
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this.sourceBlock_, 'field', this.name, this.value_, newValue));
|
||||
}
|
||||
this.value_ = newValue;
|
||||
|
||||
+17
-1
@@ -38,10 +38,11 @@ goog.require('goog.userAgent');
|
||||
* @param {number} width Width of the image.
|
||||
* @param {number} height Height of the image.
|
||||
* @param {string=} opt_alt Optional alt text for when block is collapsed.
|
||||
* @param {function=} opt_onClick Optional function to be called when image is clicked
|
||||
* @extends {Blockly.Field}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.FieldImage = function(src, width, height, opt_alt) {
|
||||
Blockly.FieldImage = function(src, width, height, opt_alt, opt_onClick) {
|
||||
this.sourceBlock_ = null;
|
||||
|
||||
// Ensure height and width are numbers. Strings are bad at math.
|
||||
@@ -51,6 +52,10 @@ Blockly.FieldImage = function(src, width, height, opt_alt) {
|
||||
this.height_ + 2 * Blockly.BlockSvg.INLINE_PADDING_Y);
|
||||
this.text_ = opt_alt || '';
|
||||
this.setValue(src);
|
||||
|
||||
if (typeof opt_onClick === "function") {
|
||||
this.clickHandler_ = opt_onClick;
|
||||
}
|
||||
};
|
||||
goog.inherits(Blockly.FieldImage, Blockly.Field);
|
||||
|
||||
@@ -153,6 +158,7 @@ Blockly.FieldImage.prototype.setText = function(alt) {
|
||||
Blockly.FieldImage.prototype.render_ = function() {
|
||||
// NOP
|
||||
};
|
||||
|
||||
/**
|
||||
* Images are fixed width, no need to update.
|
||||
* @private
|
||||
@@ -160,3 +166,13 @@ Blockly.FieldImage.prototype.render_ = function() {
|
||||
Blockly.FieldImage.prototype.updateWidth = function() {
|
||||
// NOP
|
||||
};
|
||||
|
||||
/**
|
||||
* If field click is called, and click handler defined,
|
||||
* call the handler.
|
||||
*/
|
||||
Blockly.FieldImage.prototype.showEditor = function() {
|
||||
if (this.clickHandler_){
|
||||
this.clickHandler_(this);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -109,7 +109,7 @@ Blockly.FieldTextInput.prototype.setText = function(newText) {
|
||||
return;
|
||||
}
|
||||
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this.sourceBlock_, 'field', this.name, this.text_, newText));
|
||||
}
|
||||
Blockly.Field.prototype.setText.call(this, newText);
|
||||
|
||||
+64
-45
@@ -28,7 +28,9 @@ goog.provide('Blockly.FieldVariable');
|
||||
|
||||
goog.require('Blockly.FieldDropdown');
|
||||
goog.require('Blockly.Msg');
|
||||
goog.require('Blockly.VariableModel');
|
||||
goog.require('Blockly.Variables');
|
||||
goog.require('Blockly.VariableModel');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.string');
|
||||
|
||||
@@ -49,21 +51,6 @@ Blockly.FieldVariable = function(varname, opt_validator) {
|
||||
};
|
||||
goog.inherits(Blockly.FieldVariable, Blockly.FieldDropdown);
|
||||
|
||||
/**
|
||||
* The menu item index for the rename variable option.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldVariable.prototype.renameVarItemIndex_ = -1;
|
||||
|
||||
/**
|
||||
* The menu item index for the delete variable option.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldVariable.prototype.deleteVarItemIndex_ = -1;
|
||||
|
||||
|
||||
/**
|
||||
* Install this dropdown on a block.
|
||||
*/
|
||||
@@ -116,15 +103,29 @@ Blockly.FieldVariable.prototype.getValue = function() {
|
||||
|
||||
/**
|
||||
* Set the variable name.
|
||||
* @param {string} newValue New text.
|
||||
* @param {string} value New text.
|
||||
*/
|
||||
Blockly.FieldVariable.prototype.setValue = function(newValue) {
|
||||
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
this.sourceBlock_, 'field', this.name, this.value_, newValue));
|
||||
Blockly.FieldVariable.prototype.setValue = function(value) {
|
||||
var newValue = value;
|
||||
var newText = value;
|
||||
|
||||
if (this.sourceBlock_) {
|
||||
var variable = this.sourceBlock_.workspace.getVariableById(value);
|
||||
if (variable) {
|
||||
newText = variable.name;
|
||||
}
|
||||
// TODO(marisaleung): Remove name lookup after converting all Field Variable
|
||||
// instances to use id instead of name.
|
||||
else if (variable = this.sourceBlock_.workspace.getVariable(value)) {
|
||||
newValue = variable.getId();
|
||||
}
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
this.sourceBlock_, 'field', this.name, this.value_, newValue));
|
||||
}
|
||||
}
|
||||
this.value_ = newValue;
|
||||
this.setText(newValue);
|
||||
this.setText(newText);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -134,30 +135,42 @@ Blockly.FieldVariable.prototype.setValue = function(newValue) {
|
||||
* @this {Blockly.FieldVariable}
|
||||
*/
|
||||
Blockly.FieldVariable.dropdownCreate = function() {
|
||||
if (this.sourceBlock_ && this.sourceBlock_.workspace) {
|
||||
var variableModelList = [];
|
||||
var name = this.getText();
|
||||
// Don't create a new variable if there is nothing selected.
|
||||
var createSelectedVariable = name ? true : false;
|
||||
var workspace = null;
|
||||
if (this.sourceBlock_) {
|
||||
workspace = this.sourceBlock_.workspace;
|
||||
}
|
||||
|
||||
if (workspace) {
|
||||
// Get a copy of the list, so that adding rename and new variable options
|
||||
// doesn't modify the workspace's list.
|
||||
var variableList = this.sourceBlock_.workspace.variableList.slice(0);
|
||||
} else {
|
||||
var variableList = [];
|
||||
variableModelList = workspace.getVariablesOfType('');
|
||||
for (var i = 0; i < variableModelList.length; i++){
|
||||
if (createSelectedVariable &&
|
||||
goog.string.caseInsensitiveEquals(variableModelList[i].name, name)) {
|
||||
createSelectedVariable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ensure that the currently selected variable is an option.
|
||||
var name = this.getText();
|
||||
if (name && variableList.indexOf(name) == -1) {
|
||||
variableList.push(name);
|
||||
if (createSelectedVariable && workspace) {
|
||||
var newVar = workspace.createVariable(name);
|
||||
variableModelList.push(newVar);
|
||||
}
|
||||
variableList.sort(goog.string.caseInsensitiveCompare);
|
||||
|
||||
this.renameVarItemIndex_ = variableList.length;
|
||||
variableList.push(Blockly.Msg.RENAME_VARIABLE);
|
||||
|
||||
this.deleteVarItemIndex_ = variableList.length;
|
||||
variableList.push(Blockly.Msg.DELETE_VARIABLE.replace('%1', name));
|
||||
// Variables are not language-specific, use the name as both the user-facing
|
||||
// text and the internal representation.
|
||||
variableModelList.sort(Blockly.VariableModel.compareByName);
|
||||
var options = [];
|
||||
for (var i = 0; i < variableList.length; i++) {
|
||||
options[i] = [variableList[i], variableList[i]];
|
||||
for (var i = 0; i < variableModelList.length; i++) {
|
||||
// Set the uuid as the internal representation of the variable.
|
||||
options[i] = [variableModelList[i].name, variableModelList[i].getId()];
|
||||
}
|
||||
options.push([Blockly.Msg.RENAME_VARIABLE, Blockly.RENAME_VARIABLE_ID]);
|
||||
if (Blockly.Msg.DELETE_VARIABLE) {
|
||||
options.push([Blockly.Msg.DELETE_VARIABLE.replace('%1', name),
|
||||
Blockly.DELETE_VARIABLE_ID]);
|
||||
}
|
||||
return options;
|
||||
};
|
||||
@@ -170,11 +183,18 @@ Blockly.FieldVariable.dropdownCreate = function() {
|
||||
* @param {!goog.ui.MenuItem} menuItem The MenuItem selected within menu.
|
||||
*/
|
||||
Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) {
|
||||
var itemText = menuItem.getValue();
|
||||
if (this.sourceBlock_) {
|
||||
var id = menuItem.getValue();
|
||||
// TODO(marisaleung): change setValue() to take in an id as the parameter.
|
||||
// Then remove itemText.
|
||||
var itemText;
|
||||
if (this.sourceBlock_ && this.sourceBlock_.workspace) {
|
||||
var workspace = this.sourceBlock_.workspace;
|
||||
if (this.renameVarItemIndex_ >= 0 &&
|
||||
menu.getChildAt(this.renameVarItemIndex_) === menuItem) {
|
||||
var variable = workspace.getVariableById(id);
|
||||
// If the item selected is a variable, set itemText to the variable name.
|
||||
if (variable) {
|
||||
itemText = variable.name;
|
||||
}
|
||||
else if (id == Blockly.RENAME_VARIABLE_ID) {
|
||||
// Rename variable.
|
||||
var oldName = this.getText();
|
||||
Blockly.hideChaff();
|
||||
@@ -186,8 +206,7 @@ Blockly.FieldVariable.prototype.onItemSelected = function(menu, menuItem) {
|
||||
}
|
||||
});
|
||||
return;
|
||||
} else if (this.deleteVarItemIndex_ >= 0 &&
|
||||
menu.getChildAt(this.deleteVarItemIndex_) === menuItem) {
|
||||
} else if (id == Blockly.DELETE_VARIABLE_ID) {
|
||||
// Delete variable.
|
||||
workspace.deleteVariable(this.getText());
|
||||
return;
|
||||
|
||||
-1485
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,754 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2011 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Flyout tray containing blocks which may be created.
|
||||
* @author fraser@google.com (Neil Fraser)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Flyout');
|
||||
|
||||
goog.require('Blockly.Block');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.FlyoutButton');
|
||||
goog.require('Blockly.Gesture');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.WorkspaceSvg');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.math.Rect');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a flyout.
|
||||
* @param {!Object} workspaceOptions Dictionary of options for the workspace.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Flyout = function(workspaceOptions) {
|
||||
workspaceOptions.getMetrics = this.getMetrics_.bind(this);
|
||||
workspaceOptions.setMetrics = this.setMetrics_.bind(this);
|
||||
|
||||
/**
|
||||
* @type {!Blockly.Workspace}
|
||||
* @private
|
||||
*/
|
||||
this.workspace_ = new Blockly.WorkspaceSvg(workspaceOptions);
|
||||
this.workspace_.isFlyout = true;
|
||||
|
||||
/**
|
||||
* Is RTL vs LTR.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.RTL = !!workspaceOptions.RTL;
|
||||
|
||||
/**
|
||||
* Position of the toolbox and flyout relative to the workspace.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.toolboxPosition_ = workspaceOptions.toolboxPosition;
|
||||
|
||||
/**
|
||||
* Opaque data that can be passed to Blockly.unbindEvent_.
|
||||
* @type {!Array.<!Array>}
|
||||
* @private
|
||||
*/
|
||||
this.eventWrappers_ = [];
|
||||
|
||||
/**
|
||||
* List of background buttons that lurk behind each block to catch clicks
|
||||
* landing in the blocks' lakes and bays.
|
||||
* @type {!Array.<!Element>}
|
||||
* @private
|
||||
*/
|
||||
this.backgroundButtons_ = [];
|
||||
|
||||
/**
|
||||
* List of visible buttons.
|
||||
* @type {!Array.<!Blockly.FlyoutButton>}
|
||||
* @private
|
||||
*/
|
||||
this.buttons_ = [];
|
||||
|
||||
/**
|
||||
* List of event listeners.
|
||||
* @type {!Array.<!Array>}
|
||||
* @private
|
||||
*/
|
||||
this.listeners_ = [];
|
||||
|
||||
/**
|
||||
* List of blocks that should always be disabled.
|
||||
* @type {!Array.<!Blockly.Block>}
|
||||
* @private
|
||||
*/
|
||||
this.permanentlyDisabled_ = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Does the flyout automatically close when a block is created?
|
||||
* @type {boolean}
|
||||
*/
|
||||
Blockly.Flyout.prototype.autoClose = true;
|
||||
|
||||
/**
|
||||
* Whether the flyout is visible.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.isVisible_ = false;
|
||||
|
||||
/**
|
||||
* Whether the workspace containing this flyout is visible.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.containerVisible_ = true;
|
||||
|
||||
/**
|
||||
* Corner radius of the flyout background.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
Blockly.Flyout.prototype.CORNER_RADIUS = 8;
|
||||
|
||||
/**
|
||||
* Margin around the edges of the blocks in the flyout.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
Blockly.Flyout.prototype.MARGIN = Blockly.Flyout.prototype.CORNER_RADIUS;
|
||||
|
||||
/**
|
||||
* TODO: Move GAP_X and GAP_Y to their appropriate files.
|
||||
* Gap between items in horizontal flyouts. Can be overridden with the "sep"
|
||||
* element.
|
||||
* @const {number}
|
||||
*/
|
||||
Blockly.Flyout.prototype.GAP_X = Blockly.Flyout.prototype.MARGIN * 3;
|
||||
|
||||
/**
|
||||
* Gap between items in vertical flyouts. Can be overridden with the "sep"
|
||||
* element.
|
||||
* @const {number}
|
||||
*/
|
||||
Blockly.Flyout.prototype.GAP_Y = Blockly.Flyout.prototype.MARGIN * 3;
|
||||
|
||||
/**
|
||||
* Top/bottom padding between scrollbar and edge of flyout background.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
Blockly.Flyout.prototype.SCROLLBAR_PADDING = 2;
|
||||
|
||||
/**
|
||||
* Width of flyout.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.width_ = 0;
|
||||
|
||||
/**
|
||||
* Height of flyout.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.height_ = 0;
|
||||
|
||||
/**
|
||||
* Range of a drag angle from a flyout considered "dragging toward workspace".
|
||||
* Drags that are within the bounds of this many degrees from the orthogonal
|
||||
* line to the flyout edge are considered to be "drags toward the workspace".
|
||||
* Example:
|
||||
* Flyout Edge Workspace
|
||||
* [block] / <-within this angle, drags "toward workspace" |
|
||||
* [block] ---- orthogonal to flyout boundary ---- |
|
||||
* [block] \ |
|
||||
* The angle is given in degrees from the orthogonal.
|
||||
*
|
||||
* This is used to know when to create a new block and when to scroll the
|
||||
* flyout. Setting it to 360 means that all drags create a new block.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.dragAngleRange_ = 70;
|
||||
|
||||
/**
|
||||
* Creates the flyout's DOM. Only needs to be called once. The flyout can
|
||||
* either exist as its own svg element or be a g element nested inside a
|
||||
* separate svg element.
|
||||
* @param {string} tagName The type of tag to put the flyout in. This
|
||||
* should be <svg> or <g>.
|
||||
* @return {!Element} The flyout's SVG group.
|
||||
*/
|
||||
Blockly.Flyout.prototype.createDom = function(tagName) {
|
||||
/*
|
||||
<svg | g>
|
||||
<path class="blocklyFlyoutBackground"/>
|
||||
<g class="blocklyFlyout"></g>
|
||||
</ svg | g>
|
||||
*/
|
||||
// Setting style to display:none to start. The toolbox and flyout
|
||||
// hide/show code will set up proper visibility and size later.
|
||||
this.svgGroup_ = Blockly.utils.createSvgElement(tagName,
|
||||
{'class': 'blocklyFlyout', 'style': 'display: none'}, null);
|
||||
this.svgBackground_ = Blockly.utils.createSvgElement('path',
|
||||
{'class': 'blocklyFlyoutBackground'}, this.svgGroup_);
|
||||
this.svgGroup_.appendChild(this.workspace_.createDom());
|
||||
return this.svgGroup_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the flyout.
|
||||
* @param {!Blockly.Workspace} targetWorkspace The workspace in which to create
|
||||
* new blocks.
|
||||
*/
|
||||
Blockly.Flyout.prototype.init = function(targetWorkspace) {
|
||||
this.targetWorkspace_ = targetWorkspace;
|
||||
this.workspace_.targetWorkspace = targetWorkspace;
|
||||
// Add scrollbar.
|
||||
this.scrollbar_ = new Blockly.Scrollbar(this.workspace_,
|
||||
this.horizontalLayout_, false, 'blocklyFlyoutScrollbar');
|
||||
|
||||
this.hide();
|
||||
|
||||
Array.prototype.push.apply(this.eventWrappers_,
|
||||
Blockly.bindEventWithChecks_(this.svgGroup_, 'wheel', this, this.wheel_));
|
||||
if (!this.autoClose) {
|
||||
this.filterWrapper_ = this.filterForCapacity_.bind(this);
|
||||
this.targetWorkspace_.addChangeListener(this.filterWrapper_);
|
||||
}
|
||||
|
||||
// Dragging the flyout up and down.
|
||||
Array.prototype.push.apply(this.eventWrappers_,
|
||||
Blockly.bindEventWithChecks_(this.svgBackground_, 'mousedown', this,
|
||||
this.onMouseDown_));
|
||||
|
||||
// A flyout connected to a workspace doesn't have its own current gesture.
|
||||
this.workspace_.getGesture =
|
||||
this.targetWorkspace_.getGesture.bind(this.targetWorkspace_);
|
||||
|
||||
// Get variables from the main workspace rather than the target workspace.
|
||||
this.workspace_.getVariable =
|
||||
this.targetWorkspace_.getVariable.bind(this.targetWorkspace_);
|
||||
|
||||
this.workspace_.getVariableById =
|
||||
this.targetWorkspace_.getVariableById.bind(this.targetWorkspace_);
|
||||
|
||||
this.workspace_.getVariablesOfType =
|
||||
this.targetWorkspace_.getVariablesOfType.bind(this.targetWorkspace_);
|
||||
|
||||
this.workspace_.deleteVariable =
|
||||
this.targetWorkspace_.deleteVariable.bind(this.targetWorkspace_);
|
||||
|
||||
this.workspace_.deleteVariableById =
|
||||
this.targetWorkspace_.deleteVariableById.bind(this.targetWorkspace_);
|
||||
|
||||
this.workspace_.renameVariable =
|
||||
this.targetWorkspace_.renameVariable.bind(this.targetWorkspace_);
|
||||
|
||||
this.workspace_.renameVariableById =
|
||||
this.targetWorkspace_.renameVariableById.bind(this.targetWorkspace_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispose of this flyout.
|
||||
* Unlink from all DOM elements to prevent memory leaks.
|
||||
*/
|
||||
Blockly.Flyout.prototype.dispose = function() {
|
||||
this.hide();
|
||||
Blockly.unbindEvent_(this.eventWrappers_);
|
||||
if (this.filterWrapper_) {
|
||||
this.targetWorkspace_.removeChangeListener(this.filterWrapper_);
|
||||
this.filterWrapper_ = null;
|
||||
}
|
||||
if (this.scrollbar_) {
|
||||
this.scrollbar_.dispose();
|
||||
this.scrollbar_ = null;
|
||||
}
|
||||
if (this.workspace_) {
|
||||
this.workspace_.targetWorkspace = null;
|
||||
this.workspace_.dispose();
|
||||
this.workspace_ = null;
|
||||
}
|
||||
if (this.svgGroup_) {
|
||||
goog.dom.removeNode(this.svgGroup_);
|
||||
this.svgGroup_ = null;
|
||||
}
|
||||
this.svgBackground_ = null;
|
||||
this.targetWorkspace_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the width of the flyout.
|
||||
* @return {number} The width of the flyout.
|
||||
*/
|
||||
Blockly.Flyout.prototype.getWidth = function() {
|
||||
return this.width_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the height of the flyout.
|
||||
* @return {number} The width of the flyout.
|
||||
*/
|
||||
Blockly.Flyout.prototype.getHeight = function() {
|
||||
return this.height_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the workspace inside the flyout.
|
||||
* @return {!Blockly.WorkspaceSvg} The workspace inside the flyout.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Flyout.prototype.getWorkspace = function() {
|
||||
return this.workspace_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Is the flyout visible?
|
||||
* @return {boolean} True if visible.
|
||||
*/
|
||||
Blockly.Flyout.prototype.isVisible = function() {
|
||||
return this.isVisible_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set whether the flyout is visible. A value of true does not necessarily mean
|
||||
* that the flyout is shown. It could be hidden because its container is hidden.
|
||||
* @param {boolean} visible True if visible.
|
||||
*/
|
||||
Blockly.Flyout.prototype.setVisible = function(visible) {
|
||||
var visibilityChanged = (visible != this.isVisible());
|
||||
|
||||
this.isVisible_ = visible;
|
||||
if (visibilityChanged) {
|
||||
this.updateDisplay_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set whether this flyout's container is visible.
|
||||
* @param {boolean} visible Whether the container is visible.
|
||||
*/
|
||||
Blockly.Flyout.prototype.setContainerVisible = function(visible) {
|
||||
var visibilityChanged = (visible != this.containerVisible_);
|
||||
this.containerVisible_ = visible;
|
||||
if (visibilityChanged) {
|
||||
this.updateDisplay_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the display property of the flyout based whether it thinks it should
|
||||
* be visible and whether its containing workspace is visible.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.updateDisplay_ = function() {
|
||||
var show = true;
|
||||
if (!this.containerVisible_) {
|
||||
show = false;
|
||||
} else {
|
||||
show = this.isVisible();
|
||||
}
|
||||
this.svgGroup_.style.display = show ? 'block' : 'none';
|
||||
// Update the scrollbar's visiblity too since it should mimic the
|
||||
// flyout's visibility.
|
||||
this.scrollbar_.setContainerVisible(show);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the view based on coordinates calculated in position().
|
||||
* @param {number} width The computed width of the flyout's SVG group
|
||||
* @param {number} height The computed height of the flyout's SVG group.
|
||||
* @param {number} x The computed x origin of the flyout's SVG group.
|
||||
* @param {number} y The computed y origin of the flyout's SVG group.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.positionAt_ = function(width, height, x, y) {
|
||||
this.svgGroup_.setAttribute("width", width);
|
||||
this.svgGroup_.setAttribute("height", height);
|
||||
var transform = 'translate(' + x + 'px,' + y + 'px)';
|
||||
Blockly.utils.setCssTransform(this.svgGroup_, transform);
|
||||
|
||||
// Update the scrollbar (if one exists).
|
||||
if (this.scrollbar_) {
|
||||
// Set the scrollbars origin to be the top left of the flyout.
|
||||
this.scrollbar_.setOrigin(x, y);
|
||||
this.scrollbar_.resize();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide and empty the flyout.
|
||||
*/
|
||||
Blockly.Flyout.prototype.hide = function() {
|
||||
if (!this.isVisible()) {
|
||||
return;
|
||||
}
|
||||
this.setVisible(false);
|
||||
// Delete all the event listeners.
|
||||
for (var x = 0, listen; listen = this.listeners_[x]; x++) {
|
||||
Blockly.unbindEvent_(listen);
|
||||
}
|
||||
this.listeners_.length = 0;
|
||||
if (this.reflowWrapper_) {
|
||||
this.workspace_.removeChangeListener(this.reflowWrapper_);
|
||||
this.reflowWrapper_ = null;
|
||||
}
|
||||
// Do NOT delete the blocks here. Wait until Flyout.show.
|
||||
// https://neil.fraser.name/news/2014/08/09/
|
||||
};
|
||||
|
||||
/**
|
||||
* Show and populate the flyout.
|
||||
* @param {!Array|string} xmlList List of blocks to show.
|
||||
* Variables and procedures have a custom set of blocks.
|
||||
*/
|
||||
Blockly.Flyout.prototype.show = function(xmlList) {
|
||||
this.workspace_.setResizesEnabled(false);
|
||||
this.hide();
|
||||
this.clearOldBlocks_();
|
||||
|
||||
// Handle dynamic categories, represented by a name instead of a list of XML.
|
||||
// Look up the correct category generation function and call that to get a
|
||||
// valid XML list.
|
||||
if (typeof xmlList == 'string') {
|
||||
var fnToApply = this.workspace_.targetWorkspace.getToolboxCategoryCallback(
|
||||
xmlList);
|
||||
goog.asserts.assert(goog.isFunction(fnToApply),
|
||||
'Couldn\'t find a callback function when opening a toolbox category.');
|
||||
xmlList = fnToApply(this.workspace_.targetWorkspace);
|
||||
goog.asserts.assert(goog.isArray(xmlList),
|
||||
'The result of a toolbox category callback must be an array.');
|
||||
}
|
||||
|
||||
this.setVisible(true);
|
||||
// Create the blocks to be shown in this flyout.
|
||||
var contents = [];
|
||||
var gaps = [];
|
||||
this.permanentlyDisabled_.length = 0;
|
||||
for (var i = 0, xml; xml = xmlList[i]; i++) {
|
||||
if (xml.tagName) {
|
||||
var tagName = xml.tagName.toUpperCase();
|
||||
var default_gap = this.horizontalLayout_ ? this.GAP_X : this.GAP_Y;
|
||||
if (tagName == 'BLOCK') {
|
||||
var curBlock = Blockly.Xml.domToBlock(xml, this.workspace_);
|
||||
if (curBlock.disabled) {
|
||||
// Record blocks that were initially disabled.
|
||||
// Do not enable these blocks as a result of capacity filtering.
|
||||
this.permanentlyDisabled_.push(curBlock);
|
||||
}
|
||||
contents.push({type: 'block', block: curBlock});
|
||||
var gap = parseInt(xml.getAttribute('gap'), 10);
|
||||
gaps.push(isNaN(gap) ? default_gap : gap);
|
||||
} else if (xml.tagName.toUpperCase() == 'SEP') {
|
||||
// Change the gap between two blocks.
|
||||
// <sep gap="36"></sep>
|
||||
// The default gap is 24, can be set larger or smaller.
|
||||
// This overwrites the gap attribute on the previous block.
|
||||
// Note that a deprecated method is to add a gap to a block.
|
||||
// <block type="math_arithmetic" gap="8"></block>
|
||||
var newGap = parseInt(xml.getAttribute('gap'), 10);
|
||||
// Ignore gaps before the first block.
|
||||
if (!isNaN(newGap) && gaps.length > 0) {
|
||||
gaps[gaps.length - 1] = newGap;
|
||||
} else {
|
||||
gaps.push(default_gap);
|
||||
}
|
||||
} else if (tagName == 'BUTTON' || tagName == 'LABEL') {
|
||||
// Labels behave the same as buttons, but are styled differently.
|
||||
var isLabel = tagName == 'LABEL';
|
||||
var curButton = new Blockly.FlyoutButton(this.workspace_,
|
||||
this.targetWorkspace_, xml, isLabel);
|
||||
contents.push({type: 'button', button: curButton});
|
||||
gaps.push(default_gap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.layout_(contents, gaps);
|
||||
|
||||
// IE 11 is an incompetent browser that fails to fire mouseout events.
|
||||
// When the mouse is over the background, deselect all blocks.
|
||||
var deselectAll = function() {
|
||||
var topBlocks = this.workspace_.getTopBlocks(false);
|
||||
for (var i = 0, block; block = topBlocks[i]; i++) {
|
||||
block.removeSelect();
|
||||
}
|
||||
};
|
||||
|
||||
this.listeners_.push(Blockly.bindEventWithChecks_(this.svgBackground_,
|
||||
'mouseover', this, deselectAll));
|
||||
|
||||
if (this.horizontalLayout_) {
|
||||
this.height_ = 0;
|
||||
} else {
|
||||
this.width_ = 0;
|
||||
}
|
||||
this.workspace_.setResizesEnabled(true);
|
||||
this.reflow();
|
||||
|
||||
this.filterForCapacity_();
|
||||
|
||||
// Correctly position the flyout's scrollbar when it opens.
|
||||
this.position();
|
||||
|
||||
this.reflowWrapper_ = this.reflow.bind(this);
|
||||
this.workspace_.addChangeListener(this.reflowWrapper_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete blocks and background buttons from a previous showing of the flyout.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.clearOldBlocks_ = function() {
|
||||
// Delete any blocks from a previous showing.
|
||||
var oldBlocks = this.workspace_.getTopBlocks(false);
|
||||
for (var i = 0, block; block = oldBlocks[i]; i++) {
|
||||
if (block.workspace == this.workspace_) {
|
||||
block.dispose(false, false);
|
||||
}
|
||||
}
|
||||
// Delete any background buttons from a previous showing.
|
||||
for (var j = 0, rect; rect = this.backgroundButtons_[j]; j++) {
|
||||
goog.dom.removeNode(rect);
|
||||
}
|
||||
this.backgroundButtons_.length = 0;
|
||||
|
||||
for (var i = 0, button; button = this.buttons_[i]; i++) {
|
||||
button.dispose();
|
||||
}
|
||||
this.buttons_.length = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add listeners to a block that has been added to the flyout.
|
||||
* @param {!Element} root The root node of the SVG group the block is in.
|
||||
* @param {!Blockly.Block} block The block to add listeners for.
|
||||
* @param {!Element} rect The invisible rectangle under the block that acts as
|
||||
* a button for that block.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.addBlockListeners_ = function(root, block, rect) {
|
||||
this.listeners_.push(Blockly.bindEventWithChecks_(root, 'mousedown', null,
|
||||
this.blockMouseDown_(block)));
|
||||
this.listeners_.push(Blockly.bindEventWithChecks_(rect, 'mousedown', null,
|
||||
this.blockMouseDown_(block)));
|
||||
this.listeners_.push(Blockly.bindEvent_(root, 'mouseover', block,
|
||||
block.addSelect));
|
||||
this.listeners_.push(Blockly.bindEvent_(root, 'mouseout', block,
|
||||
block.removeSelect));
|
||||
this.listeners_.push(Blockly.bindEvent_(rect, 'mouseover', block,
|
||||
block.addSelect));
|
||||
this.listeners_.push(Blockly.bindEvent_(rect, 'mouseout', block,
|
||||
block.removeSelect));
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a mouse-down on an SVG block in a non-closing flyout.
|
||||
* @param {!Blockly.Block} block The flyout block to copy.
|
||||
* @return {!Function} Function to call when block is clicked.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.blockMouseDown_ = function(block) {
|
||||
var flyout = this;
|
||||
return function(e) {
|
||||
var gesture = flyout.targetWorkspace_.getGesture(e);
|
||||
if (gesture) {
|
||||
gesture.setStartBlock(block);
|
||||
gesture.handleFlyoutStart(e, flyout);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Mouse down on the flyout background. Start a vertical scroll drag.
|
||||
* @param {!Event} e Mouse down event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.onMouseDown_ = function(e) {
|
||||
var gesture = this.targetWorkspace_.getGesture(e);
|
||||
if (gesture) {
|
||||
gesture.handleFlyoutStart(e, this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a copy of this block on the workspace.
|
||||
* @param {!Blockly.BlockSvg} originalBlock The block to copy from the flyout.
|
||||
* @return {Blockly.BlockSvg} The newly created block, or null if something
|
||||
* went wrong with deserialization.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Flyout.prototype.createBlock = function(originalBlock) {
|
||||
var newBlock = null;
|
||||
Blockly.Events.disable();
|
||||
this.targetWorkspace_.setResizesEnabled(false);
|
||||
try {
|
||||
newBlock = this.placeNewBlock_(originalBlock);
|
||||
//Force a render on IE and Edge to get around the issue described in
|
||||
//Blockly.Field.getCachedWidth
|
||||
if (goog.userAgent.IE || goog.userAgent.EDGE) {
|
||||
var blocks = newBlock.getDescendants();
|
||||
for (var i = blocks.length - 1; i >= 0; i--) {
|
||||
blocks[i].render(false);
|
||||
}
|
||||
}
|
||||
// Close the flyout.
|
||||
Blockly.hideChaff();
|
||||
} finally {
|
||||
Blockly.Events.enable();
|
||||
}
|
||||
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.setGroup(true);
|
||||
Blockly.Events.fire(new Blockly.Events.Create(newBlock));
|
||||
}
|
||||
if (this.autoClose) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.filterForCapacity_();
|
||||
}
|
||||
return newBlock;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the given button: move it to the correct location,
|
||||
* add listeners, etc.
|
||||
* @param {!Blockly.FlyoutButton} button The button to initialize and place.
|
||||
* @param {number} x The x position of the cursor during this layout pass.
|
||||
* @param {number} y The y position of the cursor during this layout pass.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.initFlyoutButton_ = function(button, x, y) {
|
||||
var buttonSvg = button.createDom();
|
||||
button.moveTo(x, y);
|
||||
button.show();
|
||||
// Clicking on a flyout button or label is a lot like clicking on the
|
||||
// flyout background.
|
||||
this.listeners_.push(Blockly.bindEventWithChecks_(buttonSvg, 'mousedown',
|
||||
this, this.onMouseDown_));
|
||||
|
||||
this.buttons_.push(button);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create and place a rectangle corresponding to the given block.
|
||||
* @param {!Blockly.Block} block The block to associate the rect to.
|
||||
* @param {number} x The x position of the cursor during this layout pass.
|
||||
* @param {number} y The y position of the cursor during this layout pass.
|
||||
* @param {!{height: number, width: number}} blockHW The height and width of the
|
||||
* block.
|
||||
* @param {number} index The index into the background buttons list where this
|
||||
* rect should be placed.
|
||||
* @return {!SVGElement} Newly created SVG element for the rectangle behind the
|
||||
* block.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.createRect_ = function(block, x, y, blockHW, index) {
|
||||
// Create an invisible rectangle under the block to act as a button. Just
|
||||
// using the block as a button is poor, since blocks have holes in them.
|
||||
var rect = Blockly.utils.createSvgElement('rect',
|
||||
{
|
||||
'fill-opacity': 0,
|
||||
'x': x,
|
||||
'y': y,
|
||||
'height': blockHW.height,
|
||||
'width': blockHW.width
|
||||
}, null);
|
||||
rect.tooltip = block;
|
||||
Blockly.Tooltip.bindMouseEvents(rect);
|
||||
// Add the rectangles under the blocks, so that the blocks' tooltips work.
|
||||
this.workspace_.getCanvas().insertBefore(rect, block.getSvgRoot());
|
||||
|
||||
block.flyoutRect_ = rect;
|
||||
this.backgroundButtons_[index] = rect;
|
||||
return rect;
|
||||
};
|
||||
|
||||
/**
|
||||
* Move a rectangle to sit exactly behind a block, taking into account tabs,
|
||||
* hats, and any other protrusions we invent.
|
||||
* @param {!SVGElement} rect The rectangle to move directly behind the block.
|
||||
* @param {!Blockly.BlockSvg} block The block the rectangle should be behind.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.moveRectToBlock_ = function(rect, block) {
|
||||
var blockHW = block.getHeightWidth();
|
||||
rect.setAttribute('width', blockHW.width);
|
||||
rect.setAttribute('height', blockHW.height);
|
||||
|
||||
// For hat blocks we want to shift them down by the hat height
|
||||
// since the y coordinate is the corner, not the top of the hat.
|
||||
var hatOffset =
|
||||
block.startHat_ ? Blockly.BlockSvg.START_HAT_HEIGHT : 0;
|
||||
if (hatOffset) {
|
||||
block.moveBy(0, hatOffset);
|
||||
}
|
||||
|
||||
// Blocks with output tabs are shifted a bit.
|
||||
var tab = block.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0;
|
||||
var blockXY = block.getRelativeToSurfaceXY();
|
||||
rect.setAttribute('y', blockXY.y);
|
||||
rect.setAttribute('x',
|
||||
this.RTL ? blockXY.x - blockHW.width + tab : blockXY.x - tab);
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter the blocks on the flyout to disable the ones that are above the
|
||||
* capacity limit. For instance, if the user may only place two more blocks on
|
||||
* the workspace, an "a + b" block that has two shadow blocks would be disabled.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Flyout.prototype.filterForCapacity_ = function() {
|
||||
var remainingCapacity = this.targetWorkspace_.remainingCapacity();
|
||||
var blocks = this.workspace_.getTopBlocks(false);
|
||||
for (var i = 0, block; block = blocks[i]; i++) {
|
||||
if (this.permanentlyDisabled_.indexOf(block) == -1) {
|
||||
var allBlocks = block.getDescendants();
|
||||
block.setDisabled(allBlocks.length > remainingCapacity);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reflow blocks and their buttons.
|
||||
*/
|
||||
Blockly.Flyout.prototype.reflow = function() {
|
||||
if (this.reflowWrapper_) {
|
||||
this.workspace_.removeChangeListener(this.reflowWrapper_);
|
||||
}
|
||||
var blocks = this.workspace_.getTopBlocks(false);
|
||||
this.reflowInternal_(blocks);
|
||||
if (this.reflowWrapper_) {
|
||||
this.workspace_.addChangeListener(this.reflowWrapper_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean} True if this flyout may be scrolled with a scrollbar or by
|
||||
* dragging.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Flyout.prototype.isScrollable = function() {
|
||||
return this.scrollbar_ ? this.scrollbar_.isVisible() : false;
|
||||
};
|
||||
+19
-8
@@ -115,6 +115,13 @@ Blockly.FlyoutButton.prototype.width = 0;
|
||||
*/
|
||||
Blockly.FlyoutButton.prototype.height = 0;
|
||||
|
||||
/**
|
||||
* Opaque data that can be passed to Blockly.unbindEvent_.
|
||||
* @type {Array.<!Array>}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FlyoutButton.prototype.onMouseUpWrapper_ = null;
|
||||
|
||||
/**
|
||||
* Create the button elements.
|
||||
* @return {!Element} The button's SVG group.
|
||||
@@ -163,6 +170,9 @@ Blockly.FlyoutButton.prototype.createDom = function() {
|
||||
svgText.setAttribute('y', this.height - Blockly.FlyoutButton.MARGIN);
|
||||
|
||||
this.updateTransform_();
|
||||
|
||||
this.mouseUpWrapper_ = Blockly.bindEventWithChecks_(this.svgGroup_, 'mouseup',
|
||||
this, this.onMouseUp_);
|
||||
return this.svgGroup_;
|
||||
};
|
||||
|
||||
@@ -207,6 +217,9 @@ Blockly.FlyoutButton.prototype.getTargetWorkspace = function() {
|
||||
* Dispose of this button.
|
||||
*/
|
||||
Blockly.FlyoutButton.prototype.dispose = function() {
|
||||
if (this.onMouseUpWrapper_) {
|
||||
Blockly.unbindEvent_(this.onMouseUpWrapper_);
|
||||
}
|
||||
if (this.svgGroup_) {
|
||||
goog.dom.removeNode(this.svgGroup_);
|
||||
this.svgGroup_ = null;
|
||||
@@ -218,15 +231,13 @@ Blockly.FlyoutButton.prototype.dispose = function() {
|
||||
/**
|
||||
* Do something when the button is clicked.
|
||||
* @param {!Event} e Mouse up event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FlyoutButton.prototype.onMouseUp = function(e) {
|
||||
// Don't scroll the page.
|
||||
e.preventDefault();
|
||||
// Don't propagate mousewheel event (zooming).
|
||||
e.stopPropagation();
|
||||
// Stop binding to mouseup and mousemove events--flyout mouseup would normally
|
||||
// do this, but we're skipping that.
|
||||
Blockly.Flyout.terminateDrag_();
|
||||
Blockly.FlyoutButton.prototype.onMouseUp_ = function(e) {
|
||||
var gesture = this.targetWorkspace_.getGesture(e);
|
||||
if (gesture) {
|
||||
gesture.cancel();
|
||||
}
|
||||
|
||||
// Call the callback registered to this button.
|
||||
if (this.callback_) {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Methods for dragging a flyout visually.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.FlyoutDragger');
|
||||
|
||||
goog.require('Blockly.WorkspaceDragger');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.math.Coordinate');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a flyout dragger. It moves a flyout workspace around when it is
|
||||
* being dragged by a mouse or touch.
|
||||
* Note that the workspace itself manages whether or not it has a drag surface
|
||||
* and how to do translations based on that. This simply passes the right
|
||||
* commands based on events.
|
||||
* @param {!Blockly.Flyout} flyout The flyout to drag.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.FlyoutDragger = function(flyout) {
|
||||
Blockly.FlyoutDragger.superClass_.constructor.call(this,
|
||||
flyout.getWorkspace());
|
||||
|
||||
/**
|
||||
* The scrollbar to update to move the flyout.
|
||||
* Unlike the main workspace, the flyout has only one scrollbar, in either the
|
||||
* horizontal or the vertical direction.
|
||||
* @type {!Blockly.Scrollbar}
|
||||
* @private
|
||||
*/
|
||||
this.scrollbar_ = flyout.scrollbar_;
|
||||
|
||||
/**
|
||||
* Whether the flyout scrolls horizontally. If false, the flyout scrolls
|
||||
* vertically.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.horizontalLayout_ = flyout.horizontalLayout_;
|
||||
};
|
||||
goog.inherits(Blockly.FlyoutDragger, Blockly.WorkspaceDragger);
|
||||
|
||||
/**
|
||||
* Move the appropriate scrollbar to drag the flyout.
|
||||
* Since flyouts only scroll in one direction at a time, this will discard one
|
||||
* of the calculated values.
|
||||
* x and y are in pixels.
|
||||
* @param {number} x The new x position to move the scrollbar to.
|
||||
* @param {number} y The new y position to move the scrollbar to.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FlyoutDragger.prototype.updateScroll_ = function(x, y) {
|
||||
// Move the scrollbar and the flyout will scroll automatically.
|
||||
if (this.horizontalLayout_) {
|
||||
this.scrollbar_.set(x);
|
||||
} else {
|
||||
this.scrollbar_.set(y);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,456 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Horizontal flyout tray containing blocks which may be created.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.HorizontalFlyout');
|
||||
|
||||
goog.require('Blockly.Block');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.FlyoutButton');
|
||||
goog.require('Blockly.Flyout');
|
||||
goog.require('Blockly.WorkspaceSvg');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.math.Rect');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a flyout.
|
||||
* @param {!Object} workspaceOptions Dictionary of options for the workspace.
|
||||
* @extends {Blockly.Flyout}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.HorizontalFlyout = function(workspaceOptions) {
|
||||
workspaceOptions.getMetrics = this.getMetrics_.bind(this);
|
||||
workspaceOptions.setMetrics = this.setMetrics_.bind(this);
|
||||
|
||||
Blockly.HorizontalFlyout.superClass_.constructor.call(this, workspaceOptions);
|
||||
/**
|
||||
* Flyout should be laid out horizontally.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.horizontalLayout_ = true;
|
||||
};
|
||||
goog.inherits(Blockly.HorizontalFlyout, Blockly.Flyout);
|
||||
|
||||
/**
|
||||
* Return an object with all the metrics required to size scrollbars for the
|
||||
* flyout. The following properties are computed:
|
||||
* .viewHeight: Height of the visible rectangle,
|
||||
* .viewWidth: Width of the visible rectangle,
|
||||
* .contentHeight: Height of the contents,
|
||||
* .contentWidth: Width of the contents,
|
||||
* .viewTop: Offset of top edge of visible rectangle from parent,
|
||||
* .contentTop: Offset of the top-most content from the y=0 coordinate,
|
||||
* .absoluteTop: Top-edge of view.
|
||||
* .viewLeft: Offset of the left edge of visible rectangle from parent,
|
||||
* .contentLeft: Offset of the left-most content from the x=0 coordinate,
|
||||
* .absoluteLeft: Left-edge of view.
|
||||
* @return {Object} Contains size and position metrics of the flyout.
|
||||
* @private
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.getMetrics_ = function() {
|
||||
if (!this.isVisible()) {
|
||||
// Flyout is hidden.
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
var optionBox = this.workspace_.getCanvas().getBBox();
|
||||
} catch (e) {
|
||||
// Firefox has trouble with hidden elements (Bug 528969).
|
||||
var optionBox = {height: 0, y: 0, width: 0, x: 0};
|
||||
}
|
||||
|
||||
var absoluteTop = this.SCROLLBAR_PADDING;
|
||||
var absoluteLeft = this.SCROLLBAR_PADDING;
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) {
|
||||
absoluteTop = 0;
|
||||
}
|
||||
var viewHeight = this.height_;
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) {
|
||||
viewHeight -= this.SCROLLBAR_PADDING;
|
||||
}
|
||||
var viewWidth = this.width_ - 2 * this.SCROLLBAR_PADDING;
|
||||
|
||||
var metrics = {
|
||||
viewHeight: viewHeight,
|
||||
viewWidth: viewWidth,
|
||||
contentHeight: (optionBox.height + 2 * this.MARGIN) * this.workspace_.scale,
|
||||
contentWidth: (optionBox.width + 2 * this.MARGIN) * this.workspace_.scale,
|
||||
viewTop: -this.workspace_.scrollY,
|
||||
viewLeft: -this.workspace_.scrollX,
|
||||
contentTop: optionBox.y,
|
||||
contentLeft: optionBox.x,
|
||||
absoluteTop: absoluteTop,
|
||||
absoluteLeft: absoluteLeft
|
||||
};
|
||||
return metrics;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the translation of the flyout to match the scrollbars.
|
||||
* @param {!Object} xyRatio Contains a y property which is a float
|
||||
* between 0 and 1 specifying the degree of scrolling and a
|
||||
* similar x property.
|
||||
* @private
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.setMetrics_ = function(xyRatio) {
|
||||
var metrics = this.getMetrics_();
|
||||
// This is a fix to an apparent race condition.
|
||||
if (!metrics) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (goog.isNumber(xyRatio.x)) {
|
||||
this.workspace_.scrollX = -metrics.contentWidth * xyRatio.x;
|
||||
}
|
||||
|
||||
this.workspace_.translate(this.workspace_.scrollX + metrics.absoluteLeft,
|
||||
this.workspace_.scrollY + metrics.absoluteTop);
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the flyout to the edge of the workspace.
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.position = function() {
|
||||
if (!this.isVisible()) {
|
||||
return;
|
||||
}
|
||||
var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics();
|
||||
if (!targetWorkspaceMetrics) {
|
||||
// Hidden components will return null.
|
||||
return;
|
||||
}
|
||||
// Record the width for Blockly.Flyout.getMetrics_.
|
||||
this.width_ = targetWorkspaceMetrics.viewWidth;
|
||||
|
||||
var edgeWidth = targetWorkspaceMetrics.viewWidth - 2 * this.CORNER_RADIUS;
|
||||
var edgeHeight = this.height_ - this.CORNER_RADIUS;
|
||||
this.setBackgroundPath_(edgeWidth, edgeHeight);
|
||||
|
||||
var x = targetWorkspaceMetrics.absoluteLeft;
|
||||
var y = targetWorkspaceMetrics.absoluteTop;
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) {
|
||||
y += (targetWorkspaceMetrics.viewHeight - this.height_);
|
||||
}
|
||||
this.positionAt_(this.width_, this.height_, x, y);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create and set the path for the visible boundaries of the flyout.
|
||||
* @param {number} width The width of the flyout, not including the
|
||||
* rounded corners.
|
||||
* @param {number} height The height of the flyout, not including
|
||||
* rounded corners.
|
||||
* @private
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.setBackgroundPath_ = function(width,
|
||||
height) {
|
||||
var atTop = this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP;
|
||||
// Start at top left.
|
||||
var path = ['M 0,' + (atTop ? 0 : this.CORNER_RADIUS)];
|
||||
|
||||
if (atTop) {
|
||||
// Top.
|
||||
path.push('h', width + 2 * this.CORNER_RADIUS);
|
||||
// Right.
|
||||
path.push('v', height);
|
||||
// Bottom.
|
||||
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
|
||||
-this.CORNER_RADIUS, this.CORNER_RADIUS);
|
||||
path.push('h', -1 * width);
|
||||
// Left.
|
||||
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
|
||||
-this.CORNER_RADIUS, -this.CORNER_RADIUS);
|
||||
path.push('z');
|
||||
} else {
|
||||
// Top.
|
||||
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
|
||||
this.CORNER_RADIUS, -this.CORNER_RADIUS);
|
||||
path.push('h', width);
|
||||
// Right.
|
||||
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0, 1,
|
||||
this.CORNER_RADIUS, this.CORNER_RADIUS);
|
||||
path.push('v', height);
|
||||
// Bottom.
|
||||
path.push('h', -width - 2 * this.CORNER_RADIUS);
|
||||
// Left.
|
||||
path.push('z');
|
||||
}
|
||||
this.svgBackground_.setAttribute('d', path.join(' '));
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the flyout to the top.
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.scrollToStart = function() {
|
||||
this.scrollbar_.set(this.RTL ? Infinity : 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the flyout.
|
||||
* @param {!Event} e Mouse wheel scroll event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.wheel_ = function(e) {
|
||||
var delta = e.deltaX;
|
||||
|
||||
if (delta) {
|
||||
if (goog.userAgent.GECKO) {
|
||||
// Firefox's deltas are a tenth that of Chrome/Safari.
|
||||
delta *= 10;
|
||||
}
|
||||
// TODO: #1093
|
||||
var metrics = this.getMetrics_();
|
||||
var pos = metrics.viewLeft + delta;
|
||||
var limit = metrics.contentWidth - metrics.viewWidth;
|
||||
pos = Math.min(pos, limit);
|
||||
pos = Math.max(pos, 0);
|
||||
this.scrollbar_.set(pos);
|
||||
// When the flyout moves from a wheel event, hide WidgetDiv.
|
||||
Blockly.WidgetDiv.hide();
|
||||
}
|
||||
|
||||
// Don't scroll the page.
|
||||
e.preventDefault();
|
||||
// Don't propagate mousewheel event (zooming).
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
/**
|
||||
* Lay out the blocks in the flyout.
|
||||
* @param {!Array.<!Object>} contents The blocks and buttons to lay out.
|
||||
* @param {!Array.<number>} gaps The visible gaps between blocks.
|
||||
* @private
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.layout_ = function(contents, gaps) {
|
||||
this.workspace_.scale = this.targetWorkspace_.scale;
|
||||
var margin = this.MARGIN;
|
||||
var cursorX = this.RTL ? margin : margin + Blockly.BlockSvg.TAB_WIDTH;
|
||||
var cursorY = margin;
|
||||
if (this.RTL) {
|
||||
contents = contents.reverse();
|
||||
}
|
||||
|
||||
for (var i = 0, item; item = contents[i]; i++) {
|
||||
if (item.type == 'block') {
|
||||
var block = item.block;
|
||||
var allBlocks = block.getDescendants();
|
||||
for (var j = 0, child; child = allBlocks[j]; j++) {
|
||||
// Mark blocks as being inside a flyout. This is used to detect and
|
||||
// prevent the closure of the flyout if the user right-clicks on such a
|
||||
// block.
|
||||
child.isInFlyout = true;
|
||||
}
|
||||
block.render();
|
||||
var root = block.getSvgRoot();
|
||||
var blockHW = block.getHeightWidth();
|
||||
|
||||
// Figure out where to place the block.
|
||||
var tab = block.outputConnection ? Blockly.BlockSvg.TAB_WIDTH : 0;
|
||||
if (this.RTL) {
|
||||
var moveX = cursorX + blockHW.width;
|
||||
} else {
|
||||
var moveX = cursorX + tab;
|
||||
}
|
||||
block.moveBy(moveX, cursorY);
|
||||
|
||||
var rect = this.createRect_(block, moveX, cursorY, blockHW, i);
|
||||
cursorX += (blockHW.width + gaps[i]);
|
||||
|
||||
this.addBlockListeners_(root, block, rect);
|
||||
} else if (item.type == 'button') {
|
||||
this.initFlyoutButton_(item.button, cursorX, cursorY);
|
||||
cursorX += (item.button.width + gaps[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if a drag delta is toward the workspace, based on the position
|
||||
* and orientation of the flyout. This is used in determineDragIntention_ to
|
||||
* determine if a new block should be created or if the flyout should scroll.
|
||||
* @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at mouse down, in pixel units.
|
||||
* @return {boolean} true if the drag is toward the workspace.
|
||||
* @package
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.isDragTowardWorkspace = function(
|
||||
currentDragDeltaXY) {
|
||||
var dx = currentDragDeltaXY.x;
|
||||
var dy = currentDragDeltaXY.y;
|
||||
// Direction goes from -180 to 180, with 0 toward the right and 90 on top.
|
||||
var dragDirection = Math.atan2(dy, dx) / Math.PI * 180;
|
||||
|
||||
var range = this.dragAngleRange_;
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) {
|
||||
// Horizontal at top.
|
||||
if (dragDirection < 90 + range && dragDirection > 90 - range) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Horizontal at bottom.
|
||||
if (dragDirection > -90 - range && dragDirection < -90 + range) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Copy a block from the flyout to the workspace and position it correctly.
|
||||
* @param {!Blockly.Block} originBlock The flyout block to copy..
|
||||
* @return {!Blockly.Block} The new block in the main workspace.
|
||||
* @private
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.placeNewBlock_ = function(originBlock) {
|
||||
var targetWorkspace = this.targetWorkspace_;
|
||||
var svgRootOld = originBlock.getSvgRoot();
|
||||
if (!svgRootOld) {
|
||||
throw 'originBlock is not rendered.';
|
||||
}
|
||||
// Figure out where the original block is on the screen, relative to the upper
|
||||
// left corner of the main workspace.
|
||||
if (targetWorkspace.isMutator) {
|
||||
var xyOld = this.workspace_.getSvgXY(/** @type {!Element} */ (svgRootOld));
|
||||
} else {
|
||||
var xyOld = Blockly.utils.getInjectionDivXY_(svgRootOld);
|
||||
}
|
||||
|
||||
// Take into account that the flyout might have been scrolled horizontally
|
||||
// (separately from the main workspace).
|
||||
// Generally a no-op in vertical mode but likely to happen in horizontal
|
||||
// mode.
|
||||
var scrollX = this.workspace_.scrollX;
|
||||
var scale = this.workspace_.scale;
|
||||
xyOld.x += scrollX / scale - scrollX;
|
||||
|
||||
// Take into account that the flyout might have been scrolled vertically
|
||||
// (separately from the main workspace).
|
||||
// Generally a no-op in horizontal mode but likely to happen in vertical
|
||||
// mode.
|
||||
var scrollY = this.workspace_.scrollY;
|
||||
scale = this.workspace_.scale;
|
||||
xyOld.y += scrollY / scale - scrollY;
|
||||
// If the flyout is on the bottom, (0, 0) in the flyout is offset to be below
|
||||
// (0, 0) in the main workspace. Add an offset to take that into account.
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) {
|
||||
scrollY = targetWorkspace.getMetrics().viewHeight - this.height_;
|
||||
scale = targetWorkspace.scale;
|
||||
xyOld.y += scrollY / scale - scrollY;
|
||||
}
|
||||
|
||||
// Create the new block by cloning the block in the flyout (via XML).
|
||||
var xml = Blockly.Xml.blockToDom(originBlock);
|
||||
var block = Blockly.Xml.domToBlock(xml, targetWorkspace);
|
||||
var svgRootNew = block.getSvgRoot();
|
||||
if (!svgRootNew) {
|
||||
throw 'block is not rendered.';
|
||||
}
|
||||
// Figure out where the new block got placed on the screen, relative to the
|
||||
// upper left corner of the workspace. This may not be the same as the
|
||||
// original block because the flyout's origin may not be the same as the
|
||||
// main workspace's origin.
|
||||
if (targetWorkspace.isMutator) {
|
||||
var xyNew = targetWorkspace.getSvgXY(/* @type {!Element} */(svgRootNew));
|
||||
} else {
|
||||
var xyNew = Blockly.utils.getInjectionDivXY_(svgRootNew);
|
||||
}
|
||||
|
||||
// Scale the scroll (getSvgXY_ did not do this).
|
||||
xyNew.x +=
|
||||
targetWorkspace.scrollX / targetWorkspace.scale - targetWorkspace.scrollX;
|
||||
xyNew.y +=
|
||||
targetWorkspace.scrollY / targetWorkspace.scale - targetWorkspace.scrollY;
|
||||
// If the flyout is collapsible and the workspace can't be scrolled.
|
||||
if (targetWorkspace.toolbox_ && !targetWorkspace.scrollbar) {
|
||||
xyNew.x += targetWorkspace.toolbox_.getWidth() / targetWorkspace.scale;
|
||||
xyNew.y += targetWorkspace.toolbox_.getHeight() / targetWorkspace.scale;
|
||||
}
|
||||
|
||||
// Move the new block to where the old block is.
|
||||
block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y);
|
||||
return block;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the deletion rectangle for this flyout in viewport coordinates.
|
||||
* @return {goog.math.Rect} Rectangle in which to delete.
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.getClientRect = function() {
|
||||
if (!this.svgGroup_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var flyoutRect = this.svgGroup_.getBoundingClientRect();
|
||||
// BIG_NUM is offscreen padding so that blocks dragged beyond the shown flyout
|
||||
// area are still deleted. Must be larger than the largest screen size,
|
||||
// but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE).
|
||||
var BIG_NUM = 1000000000;
|
||||
var y = flyoutRect.top;
|
||||
var height = flyoutRect.height;
|
||||
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_TOP) {
|
||||
return new goog.math.Rect(-BIG_NUM, y - BIG_NUM, BIG_NUM * 2,
|
||||
BIG_NUM + height);
|
||||
} else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_BOTTOM) {
|
||||
return new goog.math.Rect(-BIG_NUM, y, BIG_NUM * 2,
|
||||
BIG_NUM + height);
|
||||
}
|
||||
// TODO: Else throw error (should never happen).
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute height of flyout. Position button under each block.
|
||||
* For RTL: Lay out the blocks right-aligned.
|
||||
* @param {!Array<!Blockly.Block>} blocks The blocks to reflow.
|
||||
* @private
|
||||
*/
|
||||
Blockly.HorizontalFlyout.prototype.reflowInternal_ = function(blocks) {
|
||||
this.workspace_.scale = this.targetWorkspace_.scale;
|
||||
var flyoutHeight = 0;
|
||||
for (var i = 0, block; block = blocks[i]; i++) {
|
||||
flyoutHeight = Math.max(flyoutHeight, block.getHeightWidth().height);
|
||||
}
|
||||
flyoutHeight += this.MARGIN * 1.5;
|
||||
flyoutHeight *= this.workspace_.scale;
|
||||
flyoutHeight += Blockly.Scrollbar.scrollbarThickness;
|
||||
|
||||
if (this.height_ != flyoutHeight) {
|
||||
for (var i = 0, block; block = blocks[i]; i++) {
|
||||
if (block.flyoutRect_) {
|
||||
this.moveRectToBlock_(block.flyoutRect_, block);
|
||||
}
|
||||
}
|
||||
// Record the height for .getMetrics_ and .position.
|
||||
this.height_ = flyoutHeight;
|
||||
// Call this since it is possible the trash and zoom buttons need
|
||||
// to move. e.g. on a bottom positioned flyout when zoom is clicked.
|
||||
this.targetWorkspace_.resize();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,452 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Layout code for a vertical variant of the flyout.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.VerticalFlyout');
|
||||
|
||||
goog.require('Blockly.Block');
|
||||
goog.require('Blockly.Events');
|
||||
goog.require('Blockly.Flyout');
|
||||
goog.require('Blockly.FlyoutButton');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.WorkspaceSvg');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.events');
|
||||
goog.require('goog.math.Rect');
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a flyout.
|
||||
* @param {!Object} workspaceOptions Dictionary of options for the workspace.
|
||||
* @extends {Blockly.Flyout}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.VerticalFlyout = function(workspaceOptions) {
|
||||
workspaceOptions.getMetrics = this.getMetrics_.bind(this);
|
||||
workspaceOptions.setMetrics = this.setMetrics_.bind(this);
|
||||
|
||||
Blockly.VerticalFlyout.superClass_.constructor.call(this, workspaceOptions);
|
||||
/**
|
||||
* Flyout should be laid out vertically.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.horizontalLayout_ = false;
|
||||
};
|
||||
goog.inherits(Blockly.VerticalFlyout, Blockly.Flyout);
|
||||
|
||||
/**
|
||||
* Return an object with all the metrics required to size scrollbars for the
|
||||
* flyout. The following properties are computed:
|
||||
* .viewHeight: Height of the visible rectangle,
|
||||
* .viewWidth: Width of the visible rectangle,
|
||||
* .contentHeight: Height of the contents,
|
||||
* .contentWidth: Width of the contents,
|
||||
* .viewTop: Offset of top edge of visible rectangle from parent,
|
||||
* .contentTop: Offset of the top-most content from the y=0 coordinate,
|
||||
* .absoluteTop: Top-edge of view.
|
||||
* .viewLeft: Offset of the left edge of visible rectangle from parent,
|
||||
* .contentLeft: Offset of the left-most content from the x=0 coordinate,
|
||||
* .absoluteLeft: Left-edge of view.
|
||||
* @return {Object} Contains size and position metrics of the flyout.
|
||||
* @private
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.getMetrics_ = function() {
|
||||
if (!this.isVisible()) {
|
||||
// Flyout is hidden.
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
var optionBox = this.workspace_.getCanvas().getBBox();
|
||||
} catch (e) {
|
||||
// Firefox has trouble with hidden elements (Bug 528969).
|
||||
var optionBox = {height: 0, y: 0, width: 0, x: 0};
|
||||
}
|
||||
|
||||
// Padding for the end of the scrollbar.
|
||||
var absoluteTop = this.SCROLLBAR_PADDING;
|
||||
var absoluteLeft = 0;
|
||||
|
||||
var viewHeight = this.height_ - 2 * this.SCROLLBAR_PADDING;
|
||||
var viewWidth = this.width_;
|
||||
if (!this.RTL) {
|
||||
viewWidth -= this.SCROLLBAR_PADDING;
|
||||
}
|
||||
|
||||
var metrics = {
|
||||
viewHeight: viewHeight,
|
||||
viewWidth: viewWidth,
|
||||
contentHeight: optionBox.height * this.workspace_.scale + 2 * this.MARGIN,
|
||||
contentWidth: optionBox.width * this.workspace_.scale + 2 * this.MARGIN,
|
||||
viewTop: -this.workspace_.scrollY + optionBox.y,
|
||||
viewLeft: -this.workspace_.scrollX,
|
||||
contentTop: optionBox.y,
|
||||
contentLeft: optionBox.x,
|
||||
absoluteTop: absoluteTop,
|
||||
absoluteLeft: absoluteLeft
|
||||
};
|
||||
return metrics;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the translation of the flyout to match the scrollbars.
|
||||
* @param {!Object} xyRatio Contains a y property which is a float
|
||||
* between 0 and 1 specifying the degree of scrolling and a
|
||||
* similar x property.
|
||||
* @private
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.setMetrics_ = function(xyRatio) {
|
||||
var metrics = this.getMetrics_();
|
||||
// This is a fix to an apparent race condition.
|
||||
if (!metrics) {
|
||||
return;
|
||||
}
|
||||
if (goog.isNumber(xyRatio.y)) {
|
||||
this.workspace_.scrollY = -metrics.contentHeight * xyRatio.y;
|
||||
}
|
||||
this.workspace_.translate(this.workspace_.scrollX + metrics.absoluteLeft,
|
||||
this.workspace_.scrollY + metrics.absoluteTop);
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the flyout to the edge of the workspace.
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.position = function() {
|
||||
if (!this.isVisible()) {
|
||||
return;
|
||||
}
|
||||
var targetWorkspaceMetrics = this.targetWorkspace_.getMetrics();
|
||||
if (!targetWorkspaceMetrics) {
|
||||
// Hidden components will return null.
|
||||
return;
|
||||
}
|
||||
// Record the height for Blockly.Flyout.getMetrics_
|
||||
this.height_ = targetWorkspaceMetrics.viewHeight;
|
||||
|
||||
var edgeWidth = this.width_ - this.CORNER_RADIUS;
|
||||
var edgeHeight = targetWorkspaceMetrics.viewHeight - 2 * this.CORNER_RADIUS;
|
||||
this.setBackgroundPath_(edgeWidth, edgeHeight);
|
||||
|
||||
var y = targetWorkspaceMetrics.absoluteTop;
|
||||
var x = targetWorkspaceMetrics.absoluteLeft;
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) {
|
||||
x += (targetWorkspaceMetrics.viewWidth - this.width_);
|
||||
}
|
||||
this.positionAt_(this.width_, this.height_, x, y);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create and set the path for the visible boundaries of the flyout.
|
||||
* @param {number} width The width of the flyout, not including the
|
||||
* rounded corners.
|
||||
* @param {number} height The height of the flyout, not including
|
||||
* rounded corners.
|
||||
* @private
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.setBackgroundPath_ = function(width, height) {
|
||||
var atRight = this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT;
|
||||
var totalWidth = width + this.CORNER_RADIUS;
|
||||
|
||||
// Decide whether to start on the left or right.
|
||||
var path = ['M ' + (atRight ? totalWidth : 0) + ',0'];
|
||||
// Top.
|
||||
path.push('h', atRight ? -width : width);
|
||||
// Rounded corner.
|
||||
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0,
|
||||
atRight ? 0 : 1,
|
||||
atRight ? -this.CORNER_RADIUS : this.CORNER_RADIUS,
|
||||
this.CORNER_RADIUS);
|
||||
// Side closest to workspace.
|
||||
path.push('v', Math.max(0, height));
|
||||
// Rounded corner.
|
||||
path.push('a', this.CORNER_RADIUS, this.CORNER_RADIUS, 0, 0,
|
||||
atRight ? 0 : 1,
|
||||
atRight ? this.CORNER_RADIUS : -this.CORNER_RADIUS,
|
||||
this.CORNER_RADIUS);
|
||||
// Bottom.
|
||||
path.push('h', atRight ? width : -width);
|
||||
path.push('z');
|
||||
this.svgBackground_.setAttribute('d', path.join(' '));
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the flyout to the top.
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.scrollToStart = function() {
|
||||
this.scrollbar_.set(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the flyout.
|
||||
* @param {!Event} e Mouse wheel scroll event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.wheel_ = function(e) {
|
||||
var delta = e.deltaY;
|
||||
|
||||
if (delta) {
|
||||
if (goog.userAgent.GECKO) {
|
||||
// Firefox's deltas are a tenth that of Chrome/Safari.
|
||||
delta *= 10;
|
||||
}
|
||||
var metrics = this.getMetrics_();
|
||||
var pos = metrics.viewTop + delta;
|
||||
var limit = metrics.contentHeight - metrics.viewHeight;
|
||||
pos = Math.min(pos, limit);
|
||||
pos = Math.max(pos, 0);
|
||||
this.scrollbar_.set(pos);
|
||||
// When the flyout moves from a wheel event, hide WidgetDiv.
|
||||
Blockly.WidgetDiv.hide();
|
||||
}
|
||||
|
||||
// Don't scroll the page.
|
||||
e.preventDefault();
|
||||
// Don't propagate mousewheel event (zooming).
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
/**
|
||||
* Lay out the blocks in the flyout.
|
||||
* @param {!Array.<!Object>} contents The blocks and buttons to lay out.
|
||||
* @param {!Array.<number>} gaps The visible gaps between blocks.
|
||||
* @private
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.layout_ = function(contents, gaps) {
|
||||
this.workspace_.scale = this.targetWorkspace_.scale;
|
||||
var margin = this.MARGIN;
|
||||
var cursorX = this.RTL ? margin : margin + Blockly.BlockSvg.TAB_WIDTH;
|
||||
var cursorY = margin;
|
||||
|
||||
for (var i = 0, item; item = contents[i]; i++) {
|
||||
if (item.type == 'block') {
|
||||
var block = item.block;
|
||||
var allBlocks = block.getDescendants();
|
||||
for (var j = 0, child; child = allBlocks[j]; j++) {
|
||||
// Mark blocks as being inside a flyout. This is used to detect and
|
||||
// prevent the closure of the flyout if the user right-clicks on such a
|
||||
// block.
|
||||
child.isInFlyout = true;
|
||||
}
|
||||
block.render();
|
||||
var root = block.getSvgRoot();
|
||||
var blockHW = block.getHeightWidth();
|
||||
block.moveBy(cursorX, cursorY);
|
||||
|
||||
var rect = this.createRect_(block,
|
||||
this.RTL ? cursorX - blockHW.width : cursorX, cursorY, blockHW, i);
|
||||
|
||||
this.addBlockListeners_(root, block, rect);
|
||||
|
||||
cursorY += blockHW.height + gaps[i];
|
||||
} else if (item.type == 'button') {
|
||||
this.initFlyoutButton_(item.button, cursorX, cursorY);
|
||||
cursorY += item.button.height + gaps[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if a drag delta is toward the workspace, based on the position
|
||||
* and orientation of the flyout. This is used in determineDragIntention_ to
|
||||
* determine if a new block should be created or if the flyout should scroll.
|
||||
* @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at mouse down, in pixel units.
|
||||
* @return {boolean} true if the drag is toward the workspace.
|
||||
* @package
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.isDragTowardWorkspace = function(
|
||||
currentDragDeltaXY) {
|
||||
var dx = currentDragDeltaXY.x;
|
||||
var dy = currentDragDeltaXY.y;
|
||||
// Direction goes from -180 to 180, with 0 toward the right and 90 on top.
|
||||
var dragDirection = Math.atan2(dy, dx) / Math.PI * 180;
|
||||
|
||||
var range = this.dragAngleRange_;
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) {
|
||||
// Vertical at left.
|
||||
if (dragDirection < range && dragDirection > -range) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// Vertical at right.
|
||||
if (dragDirection < -180 + range || dragDirection > 180 - range) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Copy a block from the flyout to the workspace and position it correctly.
|
||||
* @param {!Blockly.Block} originBlock The flyout block to copy.
|
||||
* @return {!Blockly.Block} The new block in the main workspace.
|
||||
* @private
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.placeNewBlock_ = function(originBlock) {
|
||||
var targetWorkspace = this.targetWorkspace_;
|
||||
var svgRootOld = originBlock.getSvgRoot();
|
||||
if (!svgRootOld) {
|
||||
throw 'originBlock is not rendered.';
|
||||
}
|
||||
// Figure out where the original block is on the screen, relative to the upper
|
||||
// left corner of the main workspace.
|
||||
if (targetWorkspace.isMutator) {
|
||||
var xyOld = this.workspace_.getSvgXY(/** @type {!Element} */ (svgRootOld));
|
||||
} else {
|
||||
var xyOld = Blockly.utils.getInjectionDivXY_(svgRootOld);
|
||||
}
|
||||
|
||||
// Take into account that the flyout might have been scrolled horizontally
|
||||
// (separately from the main workspace).
|
||||
// Generally a no-op in vertical mode but likely to happen in horizontal
|
||||
// mode.
|
||||
var scrollX = this.workspace_.scrollX;
|
||||
var scale = this.workspace_.scale;
|
||||
xyOld.x += scrollX / scale - scrollX;
|
||||
|
||||
var targetMetrics = targetWorkspace.getMetrics();
|
||||
|
||||
// If the flyout is on the right side, (0, 0) in the flyout is offset to
|
||||
// the right of (0, 0) in the main workspace. Add an offset to take that
|
||||
// into account.
|
||||
var scrollX = 0;
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_RIGHT) {
|
||||
scrollX = targetMetrics.viewWidth - this.width_;
|
||||
// Scale the scroll (getSvgXY_ did not do this).
|
||||
xyOld.x += scrollX / scale - scrollX;
|
||||
}
|
||||
|
||||
// Take into account that the flyout might have been scrolled vertically
|
||||
// (separately from the main workspace).
|
||||
// Generally a no-op in horizontal mode but likely to happen in vertical
|
||||
// mode.
|
||||
var scrollY = this.workspace_.scrollY;
|
||||
scale = this.workspace_.scale;
|
||||
xyOld.y += scrollY / scale - scrollY;
|
||||
|
||||
// Create the new block by cloning the block in the flyout (via XML).
|
||||
var xml = Blockly.Xml.blockToDom(originBlock);
|
||||
var block = Blockly.Xml.domToBlock(xml, targetWorkspace);
|
||||
var svgRootNew = block.getSvgRoot();
|
||||
if (!svgRootNew) {
|
||||
throw 'block is not rendered.';
|
||||
}
|
||||
// Figure out where the new block got placed on the screen, relative to the
|
||||
// upper left corner of the workspace. This may not be the same as the
|
||||
// original block because the flyout's origin may not be the same as the
|
||||
// main workspace's origin.
|
||||
if (targetWorkspace.isMutator) {
|
||||
var xyNew = targetWorkspace.getSvgXY(/* @type {!Element} */(svgRootNew));
|
||||
} else {
|
||||
var xyNew = Blockly.utils.getInjectionDivXY_(svgRootNew);
|
||||
}
|
||||
|
||||
// Scale the scroll (getSvgXY_ did not do this).
|
||||
xyNew.x +=
|
||||
targetWorkspace.scrollX / targetWorkspace.scale - targetWorkspace.scrollX;
|
||||
xyNew.y +=
|
||||
targetWorkspace.scrollY / targetWorkspace.scale - targetWorkspace.scrollY;
|
||||
|
||||
// If the flyout is collapsible and the workspace can't be scrolled.
|
||||
if (targetWorkspace.toolbox_ && !targetWorkspace.scrollbar) {
|
||||
xyNew.x += targetWorkspace.toolbox_.getWidth() / targetWorkspace.scale;
|
||||
xyNew.y += targetWorkspace.toolbox_.getHeight() / targetWorkspace.scale;
|
||||
}
|
||||
|
||||
// Move the new block to where the old block is.
|
||||
block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y);
|
||||
return block;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the deletion rectangle for this flyout in viewport coordinates.
|
||||
* @return {goog.math.Rect} Rectangle in which to delete.
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.getClientRect = function() {
|
||||
if (!this.svgGroup_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var flyoutRect = this.svgGroup_.getBoundingClientRect();
|
||||
// BIG_NUM is offscreen padding so that blocks dragged beyond the shown flyout
|
||||
// area are still deleted. Must be larger than the largest screen size,
|
||||
// but be smaller than half Number.MAX_SAFE_INTEGER (not available on IE).
|
||||
var BIG_NUM = 1000000000;
|
||||
var x = flyoutRect.left;
|
||||
var width = flyoutRect.width;
|
||||
|
||||
if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) {
|
||||
return new goog.math.Rect(x - BIG_NUM, -BIG_NUM, BIG_NUM + width,
|
||||
BIG_NUM * 2);
|
||||
} else { // Right
|
||||
return new goog.math.Rect(x, -BIG_NUM, BIG_NUM + width, BIG_NUM * 2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Compute width of flyout. Position button under each block.
|
||||
* For RTL: Lay out the blocks right-aligned.
|
||||
* @param {!Array<!Blockly.Block>} blocks The blocks to reflow.
|
||||
* @private
|
||||
*/
|
||||
Blockly.VerticalFlyout.prototype.reflowInternal_ = function(blocks) {
|
||||
this.workspace_.scale = this.targetWorkspace_.scale;
|
||||
var flyoutWidth = 0;
|
||||
for (var i = 0, block; block = blocks[i]; i++) {
|
||||
var width = block.getHeightWidth().width;
|
||||
if (block.outputConnection) {
|
||||
width -= Blockly.BlockSvg.TAB_WIDTH;
|
||||
}
|
||||
flyoutWidth = Math.max(flyoutWidth, width);
|
||||
}
|
||||
for (var i = 0, button; button = this.buttons_[i]; i++) {
|
||||
flyoutWidth = Math.max(flyoutWidth, button.width);
|
||||
}
|
||||
flyoutWidth += this.MARGIN * 1.5 + Blockly.BlockSvg.TAB_WIDTH;
|
||||
flyoutWidth *= this.workspace_.scale;
|
||||
flyoutWidth += Blockly.Scrollbar.scrollbarThickness;
|
||||
|
||||
if (this.width_ != flyoutWidth) {
|
||||
for (var i = 0, block; block = blocks[i]; i++) {
|
||||
if (this.RTL) {
|
||||
// With the flyoutWidth known, right-align the blocks.
|
||||
var oldX = block.getRelativeToSurfaceXY().x;
|
||||
var newX = flyoutWidth / this.workspace_.scale - this.MARGIN;
|
||||
newX -= Blockly.BlockSvg.TAB_WIDTH;
|
||||
block.moveBy(newX - oldX, 0);
|
||||
}
|
||||
if (block.flyoutRect_) {
|
||||
this.moveRectToBlock_(block.flyoutRect_, block);
|
||||
}
|
||||
}
|
||||
// Record the width for .getMetrics_ and .position.
|
||||
this.width_ = flyoutWidth;
|
||||
// Call this since it is possible the trash and zoom buttons need
|
||||
// to move. e.g. on a bottom positioned flyout when zoom is clicked.
|
||||
this.targetWorkspace_.resize();
|
||||
}
|
||||
};
|
||||
+783
@@ -0,0 +1,783 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview The class representing an in-progress gesture, usually a drag
|
||||
* or a tap.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Gesture');
|
||||
|
||||
goog.require('Blockly.BlockDragger');
|
||||
goog.require('Blockly.constants');
|
||||
goog.require('Blockly.FlyoutDragger');
|
||||
goog.require('Blockly.Tooltip');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.WorkspaceDragger');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.math.Coordinate');
|
||||
|
||||
|
||||
/**
|
||||
* NB: In this file "start" refers to touchstart, mousedown, and pointerstart
|
||||
* events. "End" refers to touchend, mouseup, and pointerend events.
|
||||
* TODO: Consider touchcancel/pointercancel.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class for one gesture.
|
||||
* @param {!Event} e The event that kicked off this gesture.
|
||||
* @param {!Blockly.WorkspaceSvg} creatorWorkspace The workspace that created
|
||||
* this gesture and has a reference to it.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Gesture = function(e, creatorWorkspace) {
|
||||
|
||||
/**
|
||||
* The position of the mouse when the gesture started. Units are css pixels,
|
||||
* with (0, 0) at the top left of the browser window (mouseEvent clientX/Y).
|
||||
* @type {goog.math.Coordinate}
|
||||
*/
|
||||
this.mouseDownXY_ = null;
|
||||
|
||||
/**
|
||||
* How far the mouse has moved during this drag, in pixel units.
|
||||
* (0, 0) is at this.mouseDownXY_.
|
||||
* @type {goog.math.Coordinate}
|
||||
* private
|
||||
*/
|
||||
this.currentDragDeltaXY_ = 0;
|
||||
|
||||
/**
|
||||
* The field that the gesture started on, or null if it did not start on a
|
||||
* field.
|
||||
* @type {Blockly.Field}
|
||||
* @private
|
||||
*/
|
||||
this.startField_ = null;
|
||||
|
||||
/**
|
||||
* The block that the gesture started on, or null if it did not start on a
|
||||
* block.
|
||||
* @type {Blockly.BlockSvg}
|
||||
* @private
|
||||
*/
|
||||
this.startBlock_ = null;
|
||||
|
||||
/**
|
||||
* The block that this gesture targets. If the gesture started on a
|
||||
* shadow block, this is the first non-shadow parent of the block. If the
|
||||
* gesture started in the flyout, this is the root block of the block group
|
||||
* that was clicked or dragged.
|
||||
* @type {Blockly.BlockSvg}
|
||||
* @private
|
||||
*/
|
||||
this.targetBlock_ = null;
|
||||
|
||||
/**
|
||||
* The workspace that the gesture started on. There may be multiple
|
||||
* workspaces on a page; this is more accurate than using
|
||||
* Blockly.getMainWorkspace().
|
||||
* @type {Blockly.WorkspaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.startWorkspace_ = null;
|
||||
|
||||
/**
|
||||
* The workspace that created this gesture. This workspace keeps a reference
|
||||
* to the gesture, which will need to be cleared at deletion.
|
||||
* This may be different from the start workspace. For instance, a flyout is
|
||||
* a workspace, but its parent workspace manages gestures for it.
|
||||
* @type {Blockly.WorkspaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.creatorWorkspace_ = creatorWorkspace;
|
||||
|
||||
/**
|
||||
* Whether the pointer has at any point moved out of the drag radius.
|
||||
* A gesture that exceeds the drag radius is a drag even if it ends exactly at
|
||||
* its start point.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.hasExceededDragRadius_ = false;
|
||||
|
||||
/**
|
||||
* Whether the workspace is currently being dragged.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isDraggingWorkspace_ = false;
|
||||
|
||||
/**
|
||||
* Whether the block is currently being dragged.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isDraggingBlock_ = false;
|
||||
|
||||
/**
|
||||
* The event that most recently updated this gesture.
|
||||
* @type {!Event}
|
||||
* @private
|
||||
*/
|
||||
this.mostRecentEvent_ = e;
|
||||
|
||||
/**
|
||||
* A handle to use to unbind a mouse move listener at the end of a drag.
|
||||
* Opaque data returned from Blockly.bindEventWithChecks_.
|
||||
* @type {Array.<!Array>}
|
||||
* @private
|
||||
*/
|
||||
this.onMoveWrapper_ = null;
|
||||
|
||||
/**
|
||||
* A handle to use to unbind a mouse up listener at the end of a drag.
|
||||
* Opaque data returned from Blockly.bindEventWithChecks_.
|
||||
* @type {Array.<!Array>}
|
||||
* @private
|
||||
*/
|
||||
this.onUpWrapper_ = null;
|
||||
|
||||
/**
|
||||
* The object tracking a block drag, or null if none is in progress.
|
||||
* @type {Blockly.BlockDragger}
|
||||
* @private
|
||||
*/
|
||||
this.blockDragger_ = null;
|
||||
|
||||
/**
|
||||
* The object tracking a workspace or flyout workspace drag, or null if none
|
||||
* is in progress.
|
||||
* @type {Blockly.WorkspaceDragger}
|
||||
* @private
|
||||
*/
|
||||
this.workspaceDragger_ = null;
|
||||
|
||||
/**
|
||||
* The flyout a gesture started in, if any.
|
||||
* @type {Blockly.Flyout}
|
||||
* @private
|
||||
*/
|
||||
this.flyout_ = null;
|
||||
|
||||
/**
|
||||
* Boolean for sanity-checking that some code is only called once.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.calledUpdateIsDragging_ = false;
|
||||
|
||||
/**
|
||||
* Boolean for sanity-checking that some code is only called once.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.hasStarted_ = false;
|
||||
|
||||
/**
|
||||
* Boolean used internally to break a cycle in disposal.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.isEnding_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sever all links from this object.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.dispose = function() {
|
||||
Blockly.Touch.clearTouchIdentifier();
|
||||
Blockly.Tooltip.unblock();
|
||||
// Clear the owner's reference to this gesture.
|
||||
this.creatorWorkspace_.clearGesture();
|
||||
|
||||
if (this.onMoveWrapper_) {
|
||||
Blockly.unbindEvent_(this.onMoveWrapper_);
|
||||
}
|
||||
if (this.onUpWrapper_) {
|
||||
Blockly.unbindEvent_(this.onUpWrapper_);
|
||||
}
|
||||
|
||||
|
||||
this.startField_ = null;
|
||||
this.startBlock_ = null;
|
||||
this.targetBlock_ = null;
|
||||
this.startWorkspace_ = null;
|
||||
this.flyout_ = null;
|
||||
|
||||
if (this.blockDragger_) {
|
||||
this.blockDragger_.dispose();
|
||||
this.blockDragger_ = null;
|
||||
}
|
||||
if (this.workspaceDragger_) {
|
||||
this.workspaceDragger_.dispose();
|
||||
this.workspaceDragger_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update internal state based on an event.
|
||||
* @param {!Event} e The most recent mouse or touch event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.updateFromEvent_ = function(e) {
|
||||
var currentXY = new goog.math.Coordinate(e.clientX, e.clientY);
|
||||
var changed = this.updateDragDelta_(currentXY);
|
||||
// Exceeded the drag radius for the first time.
|
||||
if (changed){
|
||||
this.updateIsDragging_();
|
||||
Blockly.longStop_();
|
||||
}
|
||||
this.mostRecentEvent_ = e;
|
||||
};
|
||||
|
||||
/**
|
||||
* DO MATH to set currentDragDeltaXY_ based on the most recent mouse position.
|
||||
* @param {!goog.math.Coordinate} currentXY The most recent mouse/pointer
|
||||
* position, in pixel units, with (0, 0) at the window's top left corner.
|
||||
* @return {boolean} True if the drag just exceeded the drag radius for the
|
||||
* first time.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.updateDragDelta_ = function(currentXY) {
|
||||
this.currentDragDeltaXY_ = goog.math.Coordinate.difference(currentXY,
|
||||
this.mouseDownXY_);
|
||||
|
||||
if (!this.hasExceededDragRadius_) {
|
||||
var currentDragDelta = goog.math.Coordinate.magnitude(
|
||||
this.currentDragDeltaXY_);
|
||||
|
||||
// The flyout has a different drag radius from the rest of Blockly.
|
||||
var limitRadius = this.flyout_ ? Blockly.FLYOUT_DRAG_RADIUS :
|
||||
Blockly.DRAG_RADIUS;
|
||||
|
||||
this.hasExceededDragRadius_ = currentDragDelta > limitRadius;
|
||||
return this.hasExceededDragRadius_;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update this gesture to record whether a block is being dragged from the
|
||||
* flyout.
|
||||
* This function should be called on a mouse/touch move event the first time the
|
||||
* drag radius is exceeded. It should be called no more than once per gesture.
|
||||
* If a block should be dragged from the flyout this function creates the new
|
||||
* block on the main workspace and updates targetBlock_ and startWorkspace_.
|
||||
* @return {boolean} True if a block is being dragged from the flyout.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.updateIsDraggingFromFlyout_ = function() {
|
||||
// Disabled blocks may not be dragged from the flyout.
|
||||
if (this.targetBlock_.disabled) {
|
||||
return false;
|
||||
}
|
||||
if (!this.flyout_.isScrollable() ||
|
||||
this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)) {
|
||||
this.startWorkspace_ = this.flyout_.targetWorkspace_;
|
||||
this.startWorkspace_.updateScreenCalculationsIfScrolled();
|
||||
// Start the event group now, so that the same event group is used for block
|
||||
// creation and block dragging.
|
||||
if (!Blockly.Events.getGroup()) {
|
||||
Blockly.Events.setGroup(true);
|
||||
}
|
||||
// The start block is no longer relevant, because this is a drag.
|
||||
this.startBlock_ = null;
|
||||
this.targetBlock_ = this.flyout_.createBlock(this.targetBlock_);
|
||||
this.targetBlock_.select();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update this gesture to record whether a block is being dragged.
|
||||
* This function should be called on a mouse/touch move event the first time the
|
||||
* drag radius is exceeded. It should be called no more than once per gesture.
|
||||
* If a block should be dragged, either from the flyout or in the workspace,
|
||||
* this function creates the necessary BlockDragger and starts the drag.
|
||||
* @return {boolean} true if a block is being dragged.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.updateIsDraggingBlock_ = function() {
|
||||
if (!this.targetBlock_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.flyout_) {
|
||||
this.isDraggingBlock_ = this.updateIsDraggingFromFlyout_();
|
||||
} else if (this.targetBlock_.isMovable()){
|
||||
this.isDraggingBlock_ = true;
|
||||
}
|
||||
|
||||
if (this.isDraggingBlock_) {
|
||||
this.startDraggingBlock_();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update this gesture to record whether a workspace is being dragged.
|
||||
* This function should be called on a mouse/touch move event the first time the
|
||||
* drag radius is exceeded. It should be called no more than once per gesture.
|
||||
* If a workspace is being dragged this function creates the necessary
|
||||
* WorkspaceDragger or FlyoutDragger and starts the drag.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.updateIsDraggingWorkspace_ = function() {
|
||||
var wsMovable = this.flyout_ ? this.flyout_.isScrollable() :
|
||||
this.startWorkspace_ && this.startWorkspace_.isDraggable();
|
||||
|
||||
if (!wsMovable) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.flyout_) {
|
||||
this.workspaceDragger_ = new Blockly.FlyoutDragger(this.flyout_);
|
||||
} else {
|
||||
this.workspaceDragger_ = new Blockly.WorkspaceDragger(this.startWorkspace_);
|
||||
}
|
||||
|
||||
this.isDraggingWorkspace_ = true;
|
||||
this.workspaceDragger_.startDrag();
|
||||
};
|
||||
|
||||
/**
|
||||
* Update this gesture to record whether anything is being dragged.
|
||||
* This function should be called on a mouse/touch move event the first time the
|
||||
* drag radius is exceeded. It should be called no more than once per gesture.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.updateIsDragging_ = function() {
|
||||
// Sanity check.
|
||||
goog.asserts.assert(!this.calledUpdateIsDragging_,
|
||||
'updateIsDragging_ should only be called once per gesture.');
|
||||
this.calledUpdateIsDragging_ = true;
|
||||
|
||||
// First check if it was a block drag.
|
||||
if (this.updateIsDraggingBlock_()) {
|
||||
return;
|
||||
}
|
||||
// Then check if it's a workspace drag.
|
||||
this.updateIsDraggingWorkspace_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a block dragger and start dragging the selected block.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.startDraggingBlock_ = function() {
|
||||
this.blockDragger_ = new Blockly.BlockDragger(this.targetBlock_,
|
||||
this.startWorkspace_);
|
||||
this.blockDragger_.startBlockDrag(this.currentDragDeltaXY_);
|
||||
this.blockDragger_.dragBlock(this.mostRecentEvent_,
|
||||
this.currentDragDeltaXY_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a gesture: update the workspace to indicate that a gesture is in
|
||||
* progress and bind mousemove and mouseup handlers.
|
||||
* @param {!Event} e A mouse down or touch start event.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.doStart = function(e) {
|
||||
if (Blockly.utils.isTargetInput(e)) {
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
this.hasStarted_ = true;
|
||||
|
||||
Blockly.BlockSvg.disconnectUiStop_();
|
||||
this.startWorkspace_.updateScreenCalculationsIfScrolled();
|
||||
if (this.startWorkspace_.isMutator) {
|
||||
// Mutator's coordinate system could be out of date because the bubble was
|
||||
// dragged, the block was moved, the parent workspace zoomed, etc.
|
||||
this.startWorkspace_.resize();
|
||||
}
|
||||
this.startWorkspace_.markFocused();
|
||||
this.mostRecentEvent_ = e;
|
||||
|
||||
// Hide chaff also hides the flyout, so don't do it if the click is in a flyout.
|
||||
Blockly.hideChaff(!!this.flyout_);
|
||||
Blockly.Tooltip.block();
|
||||
|
||||
if (this.targetBlock_) {
|
||||
this.targetBlock_.select();
|
||||
}
|
||||
|
||||
if (Blockly.utils.isRightButton(e)) {
|
||||
this.handleRightClick(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (goog.string.caseInsensitiveEquals(e.type, 'touchstart')) {
|
||||
Blockly.longStart_(e, this);
|
||||
}
|
||||
|
||||
this.mouseDownXY_ = new goog.math.Coordinate(e.clientX, e.clientY);
|
||||
|
||||
this.onMoveWrapper_ = Blockly.bindEventWithChecks_(
|
||||
document, 'mousemove', null, this.handleMove.bind(this));
|
||||
this.onUpWrapper_ = Blockly.bindEventWithChecks_(
|
||||
document, 'mouseup', null, this.handleUp.bind(this));
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a mouse move or touch move event.
|
||||
* @param {!Event} e A mouse move or touch move event.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.handleMove = function(e) {
|
||||
this.updateFromEvent_(e);
|
||||
if (this.isDraggingWorkspace_) {
|
||||
this.workspaceDragger_.drag(this.currentDragDeltaXY_);
|
||||
} else if (this.isDraggingBlock_) {
|
||||
this.blockDragger_.dragBlock(this.mostRecentEvent_,
|
||||
this.currentDragDeltaXY_);
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a mouse up or touch end event.
|
||||
* @param {!Event} e A mouse up or touch end event.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.handleUp = function(e) {
|
||||
this.updateFromEvent_(e);
|
||||
Blockly.longStop_();
|
||||
|
||||
if (this.isEnding_) {
|
||||
console.log('Trying to end a gesture recursively.');
|
||||
return;
|
||||
}
|
||||
this.isEnding_ = true;
|
||||
// The ordering of these checks is important: drags have higher priority than
|
||||
// clicks. Fields have higher priority than blocks; blocks have higher
|
||||
// priority than workspaces.
|
||||
if (this.isDraggingBlock_) {
|
||||
this.blockDragger_.endBlockDrag(e, this.currentDragDeltaXY_);
|
||||
} else if (this.isDraggingWorkspace_) {
|
||||
this.workspaceDragger_.endDrag(this.currentDragDeltaXY_);
|
||||
} else if (this.isFieldClick_()) {
|
||||
this.doFieldClick_();
|
||||
} else if (this.isBlockClick_()) {
|
||||
this.doBlockClick_();
|
||||
} else if (this.isWorkspaceClick_()) {
|
||||
this.doWorkspaceClick_();
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.dispose();
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel an in-progress gesture. If a workspace or block drag is in progress,
|
||||
* end the drag at the most recent location.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.cancel = function() {
|
||||
// Disposing of a block cancels in-progress drags, but dragging to a delete
|
||||
// area disposes of a block and leads to recursive disposal. Break that cycle.
|
||||
if (this.isEnding_) {
|
||||
return;
|
||||
}
|
||||
Blockly.longStop_();
|
||||
if (this.isDraggingBlock_) {
|
||||
this.blockDragger_.endBlockDrag(this.mostRecentEvent_,
|
||||
this.currentDragDeltaXY_);
|
||||
} else if (this.isDraggingWorkspace_) {
|
||||
this.workspaceDragger_.endDrag(this.currentDragDeltaXY_);
|
||||
}
|
||||
this.dispose();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a real or faked right-click event by showing a context menu.
|
||||
* @param {!Event} e A mouse move or touch move event.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.handleRightClick = function(e) {
|
||||
if (this.targetBlock_) {
|
||||
this.bringBlockToFront_();
|
||||
Blockly.hideChaff(this.flyout_);
|
||||
this.targetBlock_.showContextMenu_(e);
|
||||
} else if (this.startWorkspace_ && !this.flyout_) {
|
||||
Blockly.hideChaff();
|
||||
this.startWorkspace_.showContextMenu_(e);
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.dispose();
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a mousedown/touchstart event on a workspace.
|
||||
* @param {!Event} e A mouse down or touch start event.
|
||||
* @param {!Blockly.Workspace} ws The workspace the event hit.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.handleWsStart = function(e, ws) {
|
||||
goog.asserts.assert(!this.hasStarted_,
|
||||
'Tried to call gesture.handleWsStart, but the gesture had already been ' +
|
||||
'started.');
|
||||
this.setStartWorkspace_(ws);
|
||||
this.mostRecentEvent_ = e;
|
||||
this.doStart(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a mousedown/touchstart event on a flyout.
|
||||
* @param {!Event} e A mouse down or touch start event.
|
||||
* @param {!Blockly.Flyout} flyout The flyout the event hit.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.handleFlyoutStart = function(e, flyout) {
|
||||
goog.asserts.assert(!this.hasStarted_,
|
||||
'Tried to call gesture.handleFlyoutStart, but the gesture had already been ' +
|
||||
'started.');
|
||||
this.setStartFlyout_(flyout);
|
||||
this.handleWsStart(e, flyout.getWorkspace());
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a mousedown/touchstart event on a block.
|
||||
* @param {!Event} e A mouse down or touch start event.
|
||||
* @param {!Blockly.BlockSvg} block The block the event hit.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.handleBlockStart = function(e, block) {
|
||||
goog.asserts.assert(!this.hasStarted_,
|
||||
'Tried to call gesture.handleBlockStart, but the gesture had already been ' +
|
||||
'started.');
|
||||
this.setStartBlock(block);
|
||||
this.mostRecentEvent_ = e;
|
||||
};
|
||||
|
||||
/* Begin functions defining what actions to take to execute clicks on each type
|
||||
* of target. Any developer wanting to add behaviour on clicks should modify
|
||||
* only this code. */
|
||||
|
||||
/**
|
||||
* Execute a field click.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.doFieldClick_ = function() {
|
||||
this.startField_.showEditor_();
|
||||
this.bringBlockToFront_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a block click.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.doBlockClick_ = function() {
|
||||
// Block click in an autoclosing flyout.
|
||||
if (this.flyout_ && this.flyout_.autoClose) {
|
||||
if (!this.targetBlock_.disabled) {
|
||||
if (!Blockly.Events.getGroup()) {
|
||||
Blockly.Events.setGroup(true);
|
||||
}
|
||||
var newBlock = this.flyout_.createBlock(this.targetBlock_);
|
||||
newBlock.scheduleSnapAndBump();
|
||||
}
|
||||
} else {
|
||||
// Clicks events are on the start block, even if it was a shadow.
|
||||
Blockly.Events.fire(
|
||||
new Blockly.Events.Ui(this.startBlock_, 'click', undefined, undefined));
|
||||
}
|
||||
this.bringBlockToFront_();
|
||||
Blockly.Events.setGroup(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a workspace click.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.doWorkspaceClick_ = function() {
|
||||
if (Blockly.selected) {
|
||||
Blockly.selected.unselect();
|
||||
}
|
||||
};
|
||||
|
||||
/* End functions defining what actions to take to execute clicks on each type
|
||||
* of target. */
|
||||
|
||||
/**
|
||||
* Move the dragged/clicked block to the front of the workspace so that it is
|
||||
* not occluded by other blocks.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.bringBlockToFront_ = function() {
|
||||
// Blocks in the flyout don't overlap, so skip the work.
|
||||
if (this.targetBlock_ && !this.flyout_) {
|
||||
this.targetBlock_.bringToFront();
|
||||
}
|
||||
};
|
||||
|
||||
/* Begin functions for populating a gesture at mouse down. */
|
||||
|
||||
/**
|
||||
* Record the field that a gesture started on.
|
||||
* @param {Blockly.Field} field The field the gesture started on.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.setStartField = function(field) {
|
||||
goog.asserts.assert(!this.hasStarted_,
|
||||
'Tried to call gesture.setStartField, but the gesture had already been ' +
|
||||
'started.');
|
||||
if (!this.startField_) {
|
||||
this.startField_ = field;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Record the block that a gesture started on, and set the target block
|
||||
* appropriately.
|
||||
* @param {Blockly.BlockSvg} block The block the gesture started on.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.setStartBlock = function(block) {
|
||||
if (!this.startBlock_) {
|
||||
this.startBlock_ = block;
|
||||
if (block.isInFlyout && block != block.getRootBlock()) {
|
||||
this.setTargetBlock_(block.getRootBlock());
|
||||
} else {
|
||||
this.setTargetBlock_(block);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Record the block that a gesture targets, meaning the block that will be
|
||||
* dragged if this turns into a drag. If this block is a shadow, that will be
|
||||
* its first non-shadow parent.
|
||||
* @param {Blockly.BlockSvg} block The block the gesture targets.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.setTargetBlock_ = function(block) {
|
||||
if (block.isShadow()) {
|
||||
this.setTargetBlock_(block.getParent());
|
||||
} else {
|
||||
this.targetBlock_ = block;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Record the workspace that a gesture started on.
|
||||
* @param {Blockly.WorkspaceSvg} ws The workspace the gesture started on.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.setStartWorkspace_ = function(ws) {
|
||||
if (!this.startWorkspace_) {
|
||||
this.startWorkspace_ = ws;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Record the flyout that a gesture started on.
|
||||
* @param {Blockly.Flyout} flyout The flyout the gesture started on.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.setStartFlyout_ = function(flyout) {
|
||||
if (!this.flyout_) {
|
||||
this.flyout_ = flyout;
|
||||
}
|
||||
};
|
||||
|
||||
/* End functions for populating a gesture at mouse down. */
|
||||
|
||||
/* Begin helper functions defining types of clicks. Any developer wanting
|
||||
* to change the definition of a click should modify only this code. */
|
||||
|
||||
/**
|
||||
* Whether this gesture is a click on a block. This should only be called when
|
||||
* ending a gesture (mouse up, touch end).
|
||||
* @return {boolean} whether this gesture was a click on a block.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.isBlockClick_ = function() {
|
||||
// A block click starts on a block, never escapes the drag radius, and is not
|
||||
// a field click.
|
||||
var hasStartBlock = !!this.startBlock_;
|
||||
return hasStartBlock && !this.hasExceededDragRadius_ && !this.isFieldClick_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this gesture is a click on a field. This should only be called when
|
||||
* ending a gesture (mouse up, touch end).
|
||||
* @return {boolean} whether this gesture was a click on a field.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.isFieldClick_ = function() {
|
||||
var fieldEditable = this.startField_ ?
|
||||
this.startField_.isCurrentlyEditable() : false;
|
||||
return fieldEditable && !this.hasExceededDragRadius_ && (!this.flyout_ ||
|
||||
!this.flyout_.autoClose);
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this gesture is a click on a workspace. This should only be called
|
||||
* when ending a gesture (mouse up, touch end).
|
||||
* @return {boolean} whether this gesture was a click on a workspace.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Gesture.prototype.isWorkspaceClick_ = function() {
|
||||
var onlyTouchedWorkspace = !this.startBlock_ && !this.startField_;
|
||||
return onlyTouchedWorkspace && !this.hasExceededDragRadius_;
|
||||
};
|
||||
|
||||
/* End helper functions defining types of clicks. */
|
||||
|
||||
/**
|
||||
* Whether this gesture is a drag of either a workspace or block.
|
||||
* This function is called externally to block actions that cannot be taken
|
||||
* mid-drag (e.g. using the keyboard to delete the selected blocks).
|
||||
* @return {boolean} true if this gesture is a drag of a workspace or block.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.isDragging = function() {
|
||||
return this.isDraggingWorkspace_ || this.isDraggingBlock_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether this gesture has already been started. In theory every mouse down
|
||||
* has a corresponding mouse up, but in reality it is possible to lose a
|
||||
* mouse up, leaving an in-process gesture hanging.
|
||||
* @return {boolean} whether this gesture was a click on a workspace.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Gesture.prototype.hasStarted = function() {
|
||||
return this.hasStarted_;
|
||||
};
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Object for configuring and updating a workspace grid in
|
||||
* Blockly.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Grid');
|
||||
|
||||
goog.require('Blockly.utils');
|
||||
|
||||
goog.require('goog.userAgent');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a workspace's grid.
|
||||
* @param {!SVGElement} pattern The grid's SVG pattern, created during injection.
|
||||
* @param {!Object} options A dictionary of normalized options for the grid.
|
||||
* See grid documentation:
|
||||
* https://developers.google.com/blockly/guides/configure/web/grid
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Grid = function(pattern, options) {
|
||||
/**
|
||||
* The grid's SVG pattern, created during injection.
|
||||
* @type {!SVGElement}
|
||||
* @private
|
||||
*/
|
||||
this.gridPattern_ = pattern;
|
||||
|
||||
/**
|
||||
* The spacing of the grid lines (in px).
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.spacing_ = options['spacing'];
|
||||
|
||||
/**
|
||||
* How long the grid lines should be (in px).
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.length_ = options['length'];
|
||||
|
||||
/**
|
||||
* The horizontal grid line, if it exists.
|
||||
* @type {SVGElement}
|
||||
* @private
|
||||
*/
|
||||
this.line1_ = pattern.firstChild;
|
||||
|
||||
/**
|
||||
* The vertical grid line, if it exists.
|
||||
* @type {SVGElement}
|
||||
* @private
|
||||
*/
|
||||
this.line2_ = this.line1_ && this.line1_.nextSibling;
|
||||
|
||||
/**
|
||||
* Whether blocks should snap to the grid.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.snapToGrid_ = options['snap'];
|
||||
};
|
||||
|
||||
/**
|
||||
* The scale of the grid, used to set stroke width on grid lines.
|
||||
* This should always be the same as the workspace scale.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Grid.prototype.scale_ = 1;
|
||||
|
||||
/**
|
||||
* Dispose of this grid and unlink from the DOM.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Grid.prototype.dispose = function() {
|
||||
this.gridPattern_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether blocks should snap to the grid, based on the initial configuration.
|
||||
* @return {boolean} True if blocks should snap, false otherwise.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Grid.prototype.shouldSnap = function() {
|
||||
return this.snapToGrid_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the spacing of the grid points (in px).
|
||||
* @return {number} The spacing of the grid points.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Grid.prototype.getSpacing = function() {
|
||||
return this.spacing_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the id of the pattern element, which should be randomized to avoid
|
||||
* conflicts with other Blockly instances on the page.
|
||||
* @return {string} The pattern id.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Grid.prototype.getPatternId = function() {
|
||||
return this.gridPattern_.id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the grid with a new scale.
|
||||
* @param {number} scale The new workspace scale.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Grid.prototype.update = function(scale) {
|
||||
this.scale_ = scale;
|
||||
// MSIE freaks if it sees a 0x0 pattern, so set empty patterns to 100x100.
|
||||
var safeSpacing = (this.spacing_ * scale) || 100;
|
||||
|
||||
this.gridPattern_.setAttribute('width', safeSpacing);
|
||||
this.gridPattern_.setAttribute('height', safeSpacing);
|
||||
|
||||
var half = Math.floor(this.spacing_ / 2) + 0.5;
|
||||
var start = half - this.length_ / 2;
|
||||
var end = half + this.length_ / 2;
|
||||
|
||||
half *= scale;
|
||||
start *= scale;
|
||||
end *= scale;
|
||||
|
||||
this.setLineAttributes_(this.line1_, scale, start, end, half, half);
|
||||
this.setLineAttributes_(this.line2_, scale, half, half, start, end);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the attributes on one of the lines in the grid. Use this to update the
|
||||
* length and stroke width of the grid lines.
|
||||
* @param {!SVGElement} line Which line to update.
|
||||
* @param {number} width The new stroke size (in px).
|
||||
* @param {number} x1 The new x start position of the line (in px).
|
||||
* @param {number} x2 The new x end position of the line (in px).
|
||||
* @param {number} y1 The new y start position of the line (in px).
|
||||
* @param {number} y2 The new y end position of the line (in px).
|
||||
* @private
|
||||
*/
|
||||
Blockly.Grid.prototype.setLineAttributes_ = function(line, width, x1, x2, y1, y2) {
|
||||
if (line) {
|
||||
line.setAttribute('stroke-width', width);
|
||||
line.setAttribute('x1', x1);
|
||||
line.setAttribute('y1', y1);
|
||||
line.setAttribute('x2', x2);
|
||||
line.setAttribute('y2', y2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the grid to a new x and y position, and make sure that change is visible.
|
||||
* @param {number} x The new x position of the grid (in px).
|
||||
* @param {number} y The new y position ofthe grid (in px).
|
||||
* @package
|
||||
*/
|
||||
Blockly.Grid.prototype.moveTo = function(x, y) {
|
||||
this.gridPattern_.setAttribute('x', x);
|
||||
this.gridPattern_.setAttribute('y', y);
|
||||
|
||||
if (goog.userAgent.IE || goog.userAgent.EDGE) {
|
||||
// IE/Edge doesn't notice that the x/y offsets have changed.
|
||||
// Force an update.
|
||||
this.update(this.scale_);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the DOM for the grid described by options.
|
||||
* @param {string} rnd A random ID to append to the pattern's ID.
|
||||
* @param {!Object} gridOptions The object containing grid configuration.
|
||||
* @param {!SVGElement} defs The root SVG element for this workspace's defs.
|
||||
* @return {!SVGElement} The SVG element for the grid pattern.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Grid.createDom = function(rnd, gridOptions, defs) {
|
||||
/*
|
||||
<pattern id="blocklyGridPattern837493" patternUnits="userSpaceOnUse">
|
||||
<rect stroke="#888" />
|
||||
<rect stroke="#888" />
|
||||
</pattern>
|
||||
*/
|
||||
var gridPattern = Blockly.utils.createSvgElement('pattern',
|
||||
{'id': 'blocklyGridPattern' + rnd,
|
||||
'patternUnits': 'userSpaceOnUse'}, defs);
|
||||
if (gridOptions['length'] > 0 && gridOptions['spacing'] > 0) {
|
||||
Blockly.utils.createSvgElement('line',
|
||||
{'stroke': gridOptions['colour']}, gridPattern);
|
||||
if (gridOptions['length'] > 1) {
|
||||
Blockly.utils.createSvgElement('line',
|
||||
{'stroke': gridOptions['colour']}, gridPattern);
|
||||
}
|
||||
// x1, y1, x1, x2 properties will be set later in update.
|
||||
}
|
||||
return gridPattern;
|
||||
};
|
||||
+3
-2
@@ -170,7 +170,7 @@ Blockly.Icon.prototype.renderIcon = function(cursorX) {
|
||||
|
||||
/**
|
||||
* Notification that the icon has moved. Update the arrow accordingly.
|
||||
* @param {!goog.math.Coordinate} xy Absolute location.
|
||||
* @param {!goog.math.Coordinate} xy Absolute location in workspace coordinates.
|
||||
*/
|
||||
Blockly.Icon.prototype.setIconLocation = function(xy) {
|
||||
this.iconXY_ = xy;
|
||||
@@ -197,7 +197,8 @@ Blockly.Icon.prototype.computeIconLocation = function() {
|
||||
|
||||
/**
|
||||
* Returns the center of the block's icon relative to the surface.
|
||||
* @return {!goog.math.Coordinate} Object with x and y properties.
|
||||
* @return {!goog.math.Coordinate} Object with x and y properties in workspace
|
||||
* coordinates.
|
||||
*/
|
||||
Blockly.Icon.prototype.getIconLocation = function() {
|
||||
return this.iconXY_;
|
||||
|
||||
+9
-30
@@ -28,6 +28,7 @@ goog.provide('Blockly.inject');
|
||||
|
||||
goog.require('Blockly.BlockDragSurfaceSvg');
|
||||
goog.require('Blockly.Css');
|
||||
goog.require('Blockly.Grid');
|
||||
goog.require('Blockly.Options');
|
||||
goog.require('Blockly.WorkspaceSvg');
|
||||
goog.require('Blockly.WorkspaceDragSurfaceSvg');
|
||||
@@ -164,27 +165,8 @@ Blockly.createDom_ = function(container, options) {
|
||||
Blockly.utils.createSvgElement('path',
|
||||
{'d': 'M 0 0 L 10 10 M 10 0 L 0 10', 'stroke': '#cc0'}, disabledPattern);
|
||||
options.disabledPatternId = disabledPattern.id;
|
||||
/*
|
||||
<pattern id="blocklyGridPattern837493" patternUnits="userSpaceOnUse">
|
||||
<rect stroke="#888" />
|
||||
<rect stroke="#888" />
|
||||
</pattern>
|
||||
*/
|
||||
var gridPattern = Blockly.utils.createSvgElement('pattern',
|
||||
{'id': 'blocklyGridPattern' + rnd,
|
||||
'patternUnits': 'userSpaceOnUse'}, defs);
|
||||
if (options.gridOptions['length'] > 0 && options.gridOptions['spacing'] > 0) {
|
||||
Blockly.utils.createSvgElement('line',
|
||||
{'stroke': options.gridOptions['colour']},
|
||||
gridPattern);
|
||||
if (options.gridOptions['length'] > 1) {
|
||||
Blockly.utils.createSvgElement('line',
|
||||
{'stroke': options.gridOptions['colour']},
|
||||
gridPattern);
|
||||
}
|
||||
// x1, y1, x1, x2 properties will be set later in updateGridPattern_.
|
||||
}
|
||||
options.gridPattern = gridPattern;
|
||||
|
||||
options.gridPattern = Blockly.Grid.createDom(rnd, options.gridOptions, defs);
|
||||
return svg;
|
||||
};
|
||||
|
||||
@@ -217,7 +199,7 @@ Blockly.createMainWorkspace_ = function(svg, options, blockDragSurface, workspac
|
||||
|
||||
if (!options.readOnly && !options.hasScrollbars) {
|
||||
var workspaceChanged = function() {
|
||||
if (Blockly.dragMode_ == Blockly.DRAG_NONE) {
|
||||
if (!mainWorkspace.isDragging()) {
|
||||
var metrics = mainWorkspace.getMetrics();
|
||||
var edgeLeft = metrics.viewLeft + metrics.absoluteLeft;
|
||||
var edgeTop = metrics.viewTop + metrics.absoluteTop;
|
||||
@@ -343,10 +325,6 @@ Blockly.inject.bindDocumentEvents_ = function() {
|
||||
// should run regardless of what other touch event handlers have run.
|
||||
Blockly.bindEvent_(document, 'touchend', null, Blockly.longStop_);
|
||||
Blockly.bindEvent_(document, 'touchcancel', null, Blockly.longStop_);
|
||||
// Don't use bindEvent_ for document's mouseup since that would create a
|
||||
// corresponding touch handler that would squelch the ability to interact
|
||||
// with non-Blockly elements.
|
||||
document.addEventListener('mouseup', Blockly.onMouseUp_, false);
|
||||
// Some iPad versions don't fire resize after portrait to landscape change.
|
||||
if (goog.userAgent.IPAD) {
|
||||
Blockly.bindEventWithChecks_(window, 'orientationchange', document,
|
||||
@@ -366,15 +344,16 @@ Blockly.inject.bindDocumentEvents_ = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.inject.loadSounds_ = function(pathToMedia, workspace) {
|
||||
workspace.loadAudio_(
|
||||
var audioMgr = workspace.getAudioManager();
|
||||
audioMgr.load(
|
||||
[pathToMedia + 'click.mp3',
|
||||
pathToMedia + 'click.wav',
|
||||
pathToMedia + 'click.ogg'], 'click');
|
||||
workspace.loadAudio_(
|
||||
audioMgr.load(
|
||||
[pathToMedia + 'disconnect.wav',
|
||||
pathToMedia + 'disconnect.mp3',
|
||||
pathToMedia + 'disconnect.ogg'], 'disconnect');
|
||||
workspace.loadAudio_(
|
||||
audioMgr.load(
|
||||
[pathToMedia + 'delete.mp3',
|
||||
pathToMedia + 'delete.ogg',
|
||||
pathToMedia + 'delete.wav'], 'delete');
|
||||
@@ -385,7 +364,7 @@ Blockly.inject.loadSounds_ = function(pathToMedia, workspace) {
|
||||
while (soundBinds.length) {
|
||||
Blockly.unbindEvent_(soundBinds.pop());
|
||||
}
|
||||
workspace.preloadAudio_();
|
||||
audioMgr.preload();
|
||||
};
|
||||
|
||||
// These are bound on mouse/touch events with Blockly.bindEventWithChecks_, so
|
||||
|
||||
+7
-3
@@ -289,7 +289,7 @@ Blockly.Mutator.prototype.setVisible = function(visible) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.Mutator.prototype.workspaceChanged_ = function() {
|
||||
if (Blockly.dragMode_ == Blockly.DRAG_NONE) {
|
||||
if (!this.workspace_.isDragging()) {
|
||||
var blocks = this.workspace_.getTopBlocks(false);
|
||||
var MARGIN = 20;
|
||||
for (var b = 0, block; block = blocks[b]; b++) {
|
||||
@@ -320,7 +320,7 @@ Blockly.Mutator.prototype.workspaceChanged_ = function() {
|
||||
var newMutationDom = block.mutationToDom();
|
||||
var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom);
|
||||
if (oldMutation != newMutation) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
block, 'mutation', null, oldMutation, newMutation));
|
||||
// Ensure that any bump is part of this mutation's event group.
|
||||
var group = Blockly.Events.getGroup();
|
||||
@@ -333,7 +333,11 @@ Blockly.Mutator.prototype.workspaceChanged_ = function() {
|
||||
if (block.rendered) {
|
||||
block.render();
|
||||
}
|
||||
this.resizeBubble_();
|
||||
// Don't update the bubble until the drag has ended, to avoid moving blocks
|
||||
// under the cursor.
|
||||
if (!this.workspace_.isDragging()) {
|
||||
this.resizeBubble_();
|
||||
}
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -278,7 +278,7 @@ Blockly.Procedures.mutateCallers = function(defBlock) {
|
||||
// undo action since it is deterministically tied to the procedure's
|
||||
// definition mutation.
|
||||
Blockly.Events.recordUndo = false;
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
Blockly.Events.fire(new Blockly.Events.BlockChange(
|
||||
caller, 'mutation', null, oldMutation, newMutation));
|
||||
Blockly.Events.recordUndo = oldRecordUndo;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ Blockly.RenderedConnection.prototype.distanceFrom = function(otherConnection) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.RenderedConnection.prototype.bumpAwayFrom_ = function(staticConnection) {
|
||||
if (Blockly.dragMode_ != Blockly.DRAG_NONE) {
|
||||
if (this.sourceBlock_.workspace.isDragging()) {
|
||||
// Don't move blocks around while the user is doing the same.
|
||||
return;
|
||||
}
|
||||
@@ -168,6 +168,7 @@ Blockly.RenderedConnection.prototype.tighten_ = function() {
|
||||
if (!svgRoot) {
|
||||
throw 'block is not rendered.';
|
||||
}
|
||||
// Workspace coordinates.
|
||||
var xy = Blockly.utils.getRelativeXY(svgRoot);
|
||||
block.getSvgRoot().setAttribute('transform',
|
||||
'translate(' + (xy.x - dx) + ',' + (xy.y - dy) + ')');
|
||||
@@ -177,7 +178,7 @@ Blockly.RenderedConnection.prototype.tighten_ = function() {
|
||||
|
||||
/**
|
||||
* Find the closest compatible connection to this connection.
|
||||
* All parameters are in workspace units
|
||||
* All parameters are in workspace units.
|
||||
* @param {number} maxLimit The maximum radius to another connection.
|
||||
* @param {number} dx Horizontal offset between this connection's location
|
||||
* in the database and the current location (as a result of dragging).
|
||||
|
||||
+3
-1
@@ -240,6 +240,7 @@ Blockly.Scrollbar.prototype.origin_ = new goog.math.Coordinate(0, 0);
|
||||
|
||||
/**
|
||||
* The size of the area within which the scrollbar handle can move.
|
||||
* Coordinate system: pixel coordinates.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
@@ -247,6 +248,7 @@ Blockly.Scrollbar.prototype.scrollViewSize_ = 0;
|
||||
|
||||
/**
|
||||
* The length of the scrollbar handle.
|
||||
* Coordinate system: pixel coordinates.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
@@ -254,6 +256,7 @@ Blockly.Scrollbar.prototype.handleLength_ = 0;
|
||||
|
||||
/**
|
||||
* The offset of the start of the handle from the start of the scrollbar range.
|
||||
* Coordinate system: pixel coordinates.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
@@ -276,7 +279,6 @@ Blockly.Scrollbar.prototype.containerVisible_ = true;
|
||||
/**
|
||||
* Width of vertical scrollbar or height of horizontal scrollbar.
|
||||
* Increase the size of scrollbars on touch devices.
|
||||
* Don't define if there is no document object (e.g. node.js).
|
||||
*/
|
||||
Blockly.Scrollbar.scrollbarThickness = 15;
|
||||
if (goog.events.BrowserFeature.TOUCH_ENABLED) {
|
||||
|
||||
+9
-2
@@ -27,7 +27,9 @@
|
||||
goog.provide('Blockly.Toolbox');
|
||||
|
||||
goog.require('Blockly.Flyout');
|
||||
goog.require('Blockly.HorizontalFlyout');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.VerticalFlyout');
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.TagName');
|
||||
goog.require('goog.events');
|
||||
@@ -181,7 +183,12 @@ Blockly.Toolbox.prototype.init = function() {
|
||||
* @type {!Blockly.Flyout}
|
||||
* @private
|
||||
*/
|
||||
this.flyout_ = new Blockly.Flyout(workspaceOptions);
|
||||
this.flyout_ = null;
|
||||
if (workspace.horizontalLayout) {
|
||||
this.flyout_ = new Blockly.HorizontalFlyout(workspaceOptions);
|
||||
} else {
|
||||
this.flyout_ = new Blockly.VerticalFlyout(workspaceOptions);
|
||||
}
|
||||
goog.dom.insertSiblingAfter(this.flyout_.createDom('svg'),
|
||||
this.workspace_.getParentSvg());
|
||||
this.flyout_.init(workspace);
|
||||
@@ -423,7 +430,7 @@ Blockly.Toolbox.prototype.addDeleteStyle = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove styles from the toolbox that indicate blocks will be deleted.
|
||||
* Remove styles from the toolbox that indicate blocks will be deleted.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Toolbox.prototype.removeDeleteStyle = function() {
|
||||
|
||||
+42
-3
@@ -44,6 +44,13 @@ goog.require('goog.dom.TagName');
|
||||
*/
|
||||
Blockly.Tooltip.visible = false;
|
||||
|
||||
/**
|
||||
* Is someone else blocking the tooltip from being shown?
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Tooltip.blocked_ = false;
|
||||
|
||||
/**
|
||||
* Maximum width (in characters) of a tooltip.
|
||||
*/
|
||||
@@ -153,6 +160,10 @@ Blockly.Tooltip.bindMouseEvents = function(element) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.Tooltip.onMouseOver_ = function(e) {
|
||||
if (Blockly.Tooltip.blocked_) {
|
||||
// Someone doesn't want us to show tooltips.
|
||||
return;
|
||||
}
|
||||
// If the tooltip is an object, treat it as a pointer to the next object in
|
||||
// the chain to look at. Terminate when a string or function is found.
|
||||
var element = e.target;
|
||||
@@ -174,6 +185,10 @@ Blockly.Tooltip.onMouseOver_ = function(e) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.Tooltip.onMouseOut_ = function(e) {
|
||||
if (Blockly.Tooltip.blocked_) {
|
||||
// Someone doesn't want us to show tooltips.
|
||||
return;
|
||||
}
|
||||
// Moving from one element to another (overlapping or with no gap) generates
|
||||
// a mouseOut followed instantly by a mouseOver. Fork off the mouseOut
|
||||
// event and kill it if a mouseOver is received immediately.
|
||||
@@ -196,12 +211,13 @@ Blockly.Tooltip.onMouseMove_ = function(e) {
|
||||
if (!Blockly.Tooltip.element_ || !Blockly.Tooltip.element_.tooltip) {
|
||||
// No tooltip here to show.
|
||||
return;
|
||||
} else if (Blockly.dragMode_ != Blockly.DRAG_NONE) {
|
||||
// Don't display a tooltip during a drag.
|
||||
return;
|
||||
} else if (Blockly.WidgetDiv.isVisible()) {
|
||||
// Don't display a tooltip if a widget is open (tooltip would be under it).
|
||||
return;
|
||||
} else if (Blockly.Tooltip.blocked_) {
|
||||
// Someone doesn't want us to show tooltips. We are probably handling a
|
||||
// user gesture, such as a click or drag.
|
||||
return;
|
||||
}
|
||||
if (Blockly.Tooltip.visible) {
|
||||
// Compute the distance between the mouse position when the tooltip was
|
||||
@@ -235,11 +251,34 @@ Blockly.Tooltip.hide = function() {
|
||||
clearTimeout(Blockly.Tooltip.showPid_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide any in-progress tooltips and block showing new tooltips until the next
|
||||
* call to unblock().
|
||||
* @package
|
||||
*/
|
||||
Blockly.Tooltip.block = function() {
|
||||
Blockly.Tooltip.hide();
|
||||
Blockly.Tooltip.blocked_ = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unblock tooltips: allow them to be scheduled and shown according to their own
|
||||
* logic.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Tooltip.unblock = function() {
|
||||
Blockly.Tooltip.blocked_ = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the tooltip and show it.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Tooltip.show_ = function() {
|
||||
if (Blockly.Tooltip.blocked_) {
|
||||
// Someone doesn't want us to show tooltips.
|
||||
return;
|
||||
}
|
||||
Blockly.Tooltip.poisonedElement_ = Blockly.Tooltip.element_;
|
||||
if (!Blockly.Tooltip.DIV) {
|
||||
return;
|
||||
|
||||
+8
-71
@@ -41,13 +41,6 @@ goog.require('goog.string');
|
||||
*/
|
||||
Blockly.Touch.touchIdentifier_ = null;
|
||||
|
||||
/**
|
||||
* Wrapper function called when a touch mouseUp occurs during a drag operation.
|
||||
* @type {Array.<!Array>}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Touch.onTouchUpWrapper_ = null;
|
||||
|
||||
/**
|
||||
* The TOUCH_MAP lookup dictionary specifies additional touch events to fire,
|
||||
* in conjunction with mouse events.
|
||||
@@ -75,11 +68,10 @@ Blockly.longPid_ = 0;
|
||||
* which after about a second opens the context menu. The tasks is killed
|
||||
* if the touch event terminates early.
|
||||
* @param {!Event} e Touch start event.
|
||||
* @param {!Blockly.Block|!Blockly.WorkspaceSvg} uiObject The block or workspace
|
||||
* under the touchstart event.
|
||||
* @param {Blockly.Gesture} gesture The gesture that triggered this longStart.
|
||||
* @private
|
||||
*/
|
||||
Blockly.longStart_ = function(e, uiObject) {
|
||||
Blockly.longStart_ = function(e, gesture) {
|
||||
Blockly.longStop_();
|
||||
// Punt on multitouch events.
|
||||
if (e.changedTouches.length != 1) {
|
||||
@@ -90,7 +82,12 @@ Blockly.longStart_ = function(e, uiObject) {
|
||||
// e was a touch event. It needs to pretend to be a mouse event.
|
||||
e.clientX = e.changedTouches[0].clientX;
|
||||
e.clientY = e.changedTouches[0].clientY;
|
||||
uiObject.onMouseDown_(e);
|
||||
|
||||
// Let the gesture route the right-click correctly.
|
||||
if (gesture) {
|
||||
gesture.handleRightClick(e);
|
||||
}
|
||||
|
||||
}, Blockly.LONGPRESS);
|
||||
};
|
||||
|
||||
@@ -106,66 +103,6 @@ Blockly.longStop_ = function() {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle a mouse-up anywhere on the page.
|
||||
* @param {!Event} e Mouse up event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.onMouseUp_ = function(e) {
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
if (workspace.dragMode_ == Blockly.DRAG_NONE) {
|
||||
return;
|
||||
}
|
||||
Blockly.Touch.clearTouchIdentifier();
|
||||
|
||||
// TODO(#781): Check whether this needs to be called for all drag modes.
|
||||
workspace.resetDragSurface();
|
||||
workspace.dragMode_ = Blockly.DRAG_NONE;
|
||||
// Unbind the touch event if it exists.
|
||||
if (Blockly.Touch.onTouchUpWrapper_) {
|
||||
Blockly.unbindEvent_(Blockly.Touch.onTouchUpWrapper_);
|
||||
Blockly.Touch.onTouchUpWrapper_ = null;
|
||||
}
|
||||
if (Blockly.onMouseMoveWrapper_) {
|
||||
Blockly.unbindEvent_(Blockly.onMouseMoveWrapper_);
|
||||
Blockly.onMouseMoveWrapper_ = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a mouse-move on SVG drawing surface.
|
||||
* @param {!Event} e Mouse move event.
|
||||
* @private
|
||||
*/
|
||||
Blockly.onMouseMove_ = function(e) {
|
||||
var workspace = Blockly.getMainWorkspace();
|
||||
if (workspace.dragMode_ != Blockly.DRAG_NONE) {
|
||||
var dx = e.clientX - workspace.startDragMouseX;
|
||||
var dy = e.clientY - workspace.startDragMouseY;
|
||||
var metrics = workspace.startDragMetrics;
|
||||
var x = workspace.startScrollX + dx;
|
||||
var y = workspace.startScrollY + dy;
|
||||
x = Math.min(x, -metrics.contentLeft);
|
||||
y = Math.min(y, -metrics.contentTop);
|
||||
x = Math.max(x, metrics.viewWidth - metrics.contentLeft -
|
||||
metrics.contentWidth);
|
||||
y = Math.max(y, metrics.viewHeight - metrics.contentTop -
|
||||
metrics.contentHeight);
|
||||
|
||||
// Move the scrollbars and the page will scroll automatically.
|
||||
workspace.scrollbar.set(-x - metrics.contentLeft,
|
||||
-y - metrics.contentTop);
|
||||
// Cancel the long-press if the drag has moved too far.
|
||||
if (Math.sqrt(dx * dx + dy * dy) > Blockly.DRAG_RADIUS) {
|
||||
Blockly.longStop_();
|
||||
workspace.dragMode_ = Blockly.DRAG_FREE;
|
||||
}
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the touch identifier that tracks which touch stream to pay attention
|
||||
* to. This ends the current drag/gesture and allows other pointers to be
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Object representing a map of variables and their types.
|
||||
* @author marisaleung@google.com (Marisa Leung)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.VariableMap');
|
||||
|
||||
/**
|
||||
* Class for a variable map. This contains a dictionary data structure with
|
||||
* variable types as keys and lists of variables as values. The list of
|
||||
* variables are the type indicated by the key.
|
||||
* @param {!Blockly.Workspace} workspace The workspace this map belongs to.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.VariableMap = function(workspace) {
|
||||
/**
|
||||
* @type {!Object<string, !Array.<Blockly.VariableModel>>}
|
||||
* A map from variable type to list of variable names. The lists contain all
|
||||
* of the named variables in the workspace, including variables
|
||||
* that are not currently in use.
|
||||
* @private
|
||||
*/
|
||||
this.variableMap_ = {};
|
||||
|
||||
/**
|
||||
* The workspace this map belongs to.
|
||||
* @type {!Blockly.Workspace}
|
||||
*/
|
||||
this.workspace = workspace;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the variable map.
|
||||
*/
|
||||
Blockly.VariableMap.prototype.clear = function() {
|
||||
this.variableMap_ = new Object(null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Rename the given variable by updating its name in the variable map.
|
||||
* @param {?Blockly.VariableModel} variable Variable to rename.
|
||||
* @param {string} newName New variable name.
|
||||
*/
|
||||
Blockly.VariableMap.prototype.renameVariable = function(variable, newName) {
|
||||
var newVariable = this.getVariable(newName);
|
||||
var variableIndex = -1;
|
||||
var newVariableIndex = -1;
|
||||
var type = '';
|
||||
if (variable || newVariable) {
|
||||
type = (variable || newVariable).type;
|
||||
}
|
||||
|
||||
var variableList = this.getVariablesOfType(type);
|
||||
if (variable) {
|
||||
variableIndex = variableList.indexOf(variable);
|
||||
}
|
||||
if (newVariable){ // see if I can get rid of newVariable dependency
|
||||
newVariableIndex = variableList.indexOf(newVariable);
|
||||
}
|
||||
|
||||
if (variableIndex == -1 && newVariableIndex == -1) {
|
||||
this.createVariable(newName, '');
|
||||
console.log('Tried to rename an non-existent variable.');
|
||||
} else if (variableIndex == newVariableIndex ||
|
||||
variableIndex != -1 && newVariableIndex == -1) {
|
||||
// Only changing case, or renaming to a completely novel name.
|
||||
var variableToRename = this.variableMap_[type][variableIndex];
|
||||
Blockly.Events.fire(new Blockly.Events.VarRename(variableToRename,
|
||||
newName));
|
||||
variableToRename.name = newName;
|
||||
} else if (variableIndex != -1 && newVariableIndex != -1) {
|
||||
// Renaming one existing variable to another existing variable.
|
||||
// The case might have changed, so we update the destination ID.
|
||||
var variableToRename = this.variableMap_[type][newVariableIndex];
|
||||
Blockly.Events.fire(new Blockly.Events.VarRename(variableToRename,
|
||||
newName));
|
||||
var variableToDelete = this.variableMap_[type][variableIndex];
|
||||
Blockly.Events.fire(new Blockly.Events.VarDelete(variableToDelete));
|
||||
variableToRename.name = newName;
|
||||
this.variableMap_[type].splice(variableIndex, 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a variable with a given name, optional type, and optional id.
|
||||
* @param {!string} name The name of the variable. This must be unique across
|
||||
* variables and procedures.
|
||||
* @param {?string} opt_type The type of the variable like 'int' or 'string'.
|
||||
* Does not need to be unique. Field_variable can filter variables based on
|
||||
* their type. This will default to '' which is a specific type.
|
||||
* @param {?string} opt_id The unique id of the variable. This will default to
|
||||
* a UUID.
|
||||
* @return {?Blockly.VariableModel} The newly created variable.
|
||||
*/
|
||||
Blockly.VariableMap.prototype.createVariable = function(name, opt_type, opt_id) {
|
||||
var variable = this.getVariable(name);
|
||||
if (variable) {
|
||||
if (opt_type && variable.type != opt_type) {
|
||||
throw Error('Variable "' + name + '" is already in use and its type is "'
|
||||
+ variable.type + '" which conflicts with the passed in ' +
|
||||
'type, "' + opt_type + '".');
|
||||
}
|
||||
if (opt_id && variable.getId() != opt_id) {
|
||||
throw Error('Variable "' + name + '" is already in use and its id is "'
|
||||
+ variable.getId() + '" which conflicts with the passed in ' +
|
||||
'id, "' + opt_id + '".');
|
||||
}
|
||||
// The variable already exists and has the same id and type.
|
||||
return variable;
|
||||
}
|
||||
if (opt_id && this.getVariableById(opt_id)) {
|
||||
throw Error('Variable id, "' + opt_id + '", is already in use.');
|
||||
}
|
||||
opt_id = opt_id || Blockly.utils.genUid();
|
||||
opt_type = opt_type || '';
|
||||
|
||||
variable = new Blockly.VariableModel(this.workspace, name, opt_type, opt_id);
|
||||
// If opt_type is not a key, create a new list.
|
||||
if (!this.variableMap_[opt_type]) {
|
||||
this.variableMap_[opt_type] = [variable];
|
||||
} else {
|
||||
// Else append the variable to the preexisting list.
|
||||
this.variableMap_[opt_type].push(variable);
|
||||
}
|
||||
return variable;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a variable.
|
||||
* @param {Blockly.VariableModel} variable Variable to delete.
|
||||
*/
|
||||
Blockly.VariableMap.prototype.deleteVariable = function(variable) {
|
||||
var variableList = this.variableMap_[variable.type];
|
||||
for (var i = 0, tempVar; tempVar = variableList[i]; i++) {
|
||||
if (tempVar.getId() == variable.getId()) {
|
||||
variableList.splice(i, 1);
|
||||
Blockly.Events.fire(new Blockly.Events.VarDelete(variable));
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the variable by the given name and return it. Return null if it is not
|
||||
* found.
|
||||
* @param {!string} name The name to check for.
|
||||
* @return {?Blockly.VariableModel} The variable with the given name, or null if
|
||||
* it was not found.
|
||||
*/
|
||||
Blockly.VariableMap.prototype.getVariable = function(name) {
|
||||
var keys = Object.keys(this.variableMap_);
|
||||
for (var i = 0; i < keys.length; i++ ) {
|
||||
var key = keys[i];
|
||||
for (var j = 0, variable; variable = this.variableMap_[key][j]; j++) {
|
||||
if (Blockly.Names.equals(variable.name, name)) {
|
||||
return variable;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the variable by the given id and return it. Return null if it is not
|
||||
* found.
|
||||
* @param {!string} id The id to check for.
|
||||
* @return {?Blockly.VariableModel} The variable with the given id.
|
||||
*/
|
||||
Blockly.VariableMap.prototype.getVariableById = function(id) {
|
||||
var keys = Object.keys(this.variableMap_);
|
||||
for (var i = 0; i < keys.length; i++ ) {
|
||||
var key = keys[i];
|
||||
for (var j = 0, variable; variable = this.variableMap_[key][j]; j++) {
|
||||
if (variable.getId() == id) {
|
||||
return variable;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a list containing all of the variables of a specified type. If type is
|
||||
* null, return list of variables with empty string type.
|
||||
* @param {?string} type Type of the variables to find.
|
||||
* @return {Array.<Blockly.VariableModel>} The sought after variables of the
|
||||
* passed in type. An empty array if none are found.
|
||||
*/
|
||||
Blockly.VariableMap.prototype.getVariablesOfType = function(type) {
|
||||
type = type || '';
|
||||
var variable_list = this.variableMap_[type];
|
||||
if (variable_list) {
|
||||
return variable_list.slice();
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Return all variable types.
|
||||
* @return {!Array.<string>} List of variable types.
|
||||
*/
|
||||
Blockly.VariableMap.prototype.getVariableTypes = function() {
|
||||
return Object.keys(this.variableMap_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return all variables of all types.
|
||||
* @return {!Array.<Blockly.VariableModel>} List of variable models.
|
||||
*/
|
||||
Blockly.VariableMap.prototype.getAllVariables = function() {
|
||||
var all_variables = [];
|
||||
var keys = Object.keys(this.variableMap_);
|
||||
for (var i = 0; i < keys.length; i++ ) {
|
||||
all_variables = all_variables.concat(this.variableMap_[keys[i]]);
|
||||
}
|
||||
return all_variables;
|
||||
};
|
||||
+27
-3
@@ -26,20 +26,30 @@
|
||||
|
||||
goog.provide('Blockly.VariableModel');
|
||||
|
||||
goog.require('goog.string');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a variable model.
|
||||
* Holds information for the variable including name, id, and type.
|
||||
* Holds information for the variable including name, id, and type.
|
||||
* @param {!Blockly.Workspace} workspace The variable's workspace.
|
||||
* @param {!string} name The name of the variable. This must be unique across
|
||||
* variables and procedures.
|
||||
* @param {?string} opt_type The type of the variable like 'int' or 'string'.
|
||||
* Does not need to be unique. Field_variable can filter variables based on
|
||||
* their type. This will default to '' which is a specific type.
|
||||
* @param {?string} opt_id The unique id of the variable. This will default to
|
||||
* @param {?string} opt_id The unique id of the variable. This will default to
|
||||
* a UUID.
|
||||
* @see {Blockly.FieldVariable}
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.VariableModel = function(name, opt_type, opt_id) {
|
||||
Blockly.VariableModel = function(workspace, name, opt_type, opt_id) {
|
||||
/**
|
||||
* The workspace the variable is in.
|
||||
* @type {!Blockly.Workspace}
|
||||
*/
|
||||
this.workspace = workspace;
|
||||
|
||||
/**
|
||||
* The name of the variable, typically defined by the user. It must be
|
||||
* unique across all names used for procedures and variables. It may be
|
||||
@@ -65,6 +75,8 @@ Blockly.VariableModel = function(name, opt_type, opt_id) {
|
||||
* @private
|
||||
*/
|
||||
this.id_ = opt_id || Blockly.utils.genUid();
|
||||
|
||||
Blockly.Events.fire(new Blockly.Events.VarCreate(this));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -73,3 +85,15 @@ Blockly.VariableModel = function(name, opt_type, opt_id) {
|
||||
Blockly.VariableModel.prototype.getId = function() {
|
||||
return this.id_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A custom compare function for the VariableModel objects.
|
||||
* @param {Blockly.VariableModel} var1 First variable to compare.
|
||||
* @param {Blockly.VariableModel} var2 Second variable to compare.
|
||||
* @return {number} -1 if name of var1 is less than name of var2, 0 if equal,
|
||||
* and 1 if greater.
|
||||
* @package
|
||||
*/
|
||||
Blockly.VariableModel.compareByName = function(var1, var2) {
|
||||
return goog.string.caseInsensitiveCompare(var1.name, var2.name);
|
||||
};
|
||||
|
||||
+55
-16
@@ -32,6 +32,7 @@ goog.provide('Blockly.Variables');
|
||||
|
||||
goog.require('Blockly.Blocks');
|
||||
goog.require('Blockly.constants');
|
||||
goog.require('Blockly.VariableModel');
|
||||
goog.require('Blockly.Workspace');
|
||||
goog.require('goog.string');
|
||||
|
||||
@@ -86,7 +87,7 @@ Blockly.Variables.allUsedVariables = function(root) {
|
||||
* Find all variables that the user has created through the workspace or
|
||||
* toolbox. For use by generators.
|
||||
* @param {!Blockly.Workspace} root The workspace to inspect.
|
||||
* @return {!Array.<string>} Array of variable names.
|
||||
* @return {!Array.<Blockly.VariableModel>} Array of variable models.
|
||||
*/
|
||||
Blockly.Variables.allVariables = function(root) {
|
||||
if (root instanceof Blockly.Block) {
|
||||
@@ -94,19 +95,18 @@ Blockly.Variables.allVariables = function(root) {
|
||||
console.warn('Deprecated call to Blockly.Variables.allVariables ' +
|
||||
'with a block instead of a workspace. You may want ' +
|
||||
'Blockly.Variables.allUsedVariables');
|
||||
return {};
|
||||
}
|
||||
return root.variableList;
|
||||
return root.getAllVariables();
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct the blocks required by the flyout for the variable category.
|
||||
* Construct the elements (blocks and button) required by the flyout for the
|
||||
* variable category.
|
||||
* @param {!Blockly.Workspace} workspace The workspace contianing variables.
|
||||
* @return {!Array.<!Element>} Array of XML block elements.
|
||||
* @return {!Array.<!Element>} Array of XML elements.
|
||||
*/
|
||||
Blockly.Variables.flyoutCategory = function(workspace) {
|
||||
var variableList = workspace.variableList;
|
||||
variableList.sort(goog.string.caseInsensitiveCompare);
|
||||
|
||||
var xmlList = [];
|
||||
var button = goog.dom.createDom('button');
|
||||
button.setAttribute('text', Blockly.Msg.NEW_VARIABLE);
|
||||
@@ -118,12 +118,28 @@ Blockly.Variables.flyoutCategory = function(workspace) {
|
||||
|
||||
xmlList.push(button);
|
||||
|
||||
if (variableList.length > 0) {
|
||||
var blockList = Blockly.Variables.flyoutCategoryBlocks(workspace);
|
||||
xmlList = xmlList.concat(blockList);
|
||||
return xmlList;
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct the blocks required by the flyout for the variable category.
|
||||
* @param {!Blockly.Workspace} workspace The workspace contianing variables.
|
||||
* @return {!Array.<!Element>} Array of XML block elements.
|
||||
*/
|
||||
Blockly.Variables.flyoutCategoryBlocks = function(workspace) {
|
||||
var variableModelList = workspace.getVariablesOfType('');
|
||||
variableModelList.sort(Blockly.VariableModel.compareByName);
|
||||
|
||||
var xmlList = [];
|
||||
if (variableModelList.length > 0) {
|
||||
var firstVariable = variableModelList[0];
|
||||
if (Blockly.Blocks['variables_set']) {
|
||||
var gap = Blockly.Blocks['math_change'] ? 8 : 24;
|
||||
var blockText = '<xml>' +
|
||||
'<block type="variables_set" gap="' + gap + '">' +
|
||||
'<field name="VAR">' + variableList[0] + '</field>' +
|
||||
Blockly.Variables.generateVariableFieldXml_(firstVariable) +
|
||||
'</block>' +
|
||||
'</xml>';
|
||||
var block = Blockly.Xml.textToDom(blockText).firstChild;
|
||||
@@ -133,7 +149,7 @@ Blockly.Variables.flyoutCategory = function(workspace) {
|
||||
var gap = Blockly.Blocks['variables_get'] ? 20 : 8;
|
||||
var blockText = '<xml>' +
|
||||
'<block type="math_change" gap="' + gap + '">' +
|
||||
'<field name="VAR">' + variableList[0] + '</field>' +
|
||||
Blockly.Variables.generateVariableFieldXml_(firstVariable) +
|
||||
'<value name="DELTA">' +
|
||||
'<shadow type="math_number">' +
|
||||
'<field name="NUM">1</field>' +
|
||||
@@ -145,11 +161,11 @@ Blockly.Variables.flyoutCategory = function(workspace) {
|
||||
xmlList.push(block);
|
||||
}
|
||||
|
||||
for (var i = 0; i < variableList.length; i++) {
|
||||
for (var i = 0, variable; variable = variableModelList[i]; i++) {
|
||||
if (Blockly.Blocks['variables_get']) {
|
||||
var blockText = '<xml>' +
|
||||
'<block type="variables_get" gap="8">' +
|
||||
'<field name="VAR">' + variableList[i] + '</field>' +
|
||||
Blockly.Variables.generateVariableFieldXml_(variable) +
|
||||
'</block>' +
|
||||
'</xml>';
|
||||
var block = Blockly.Xml.textToDom(blockText).firstChild;
|
||||
@@ -169,7 +185,7 @@ Blockly.Variables.flyoutCategory = function(workspace) {
|
||||
* @return {string} New variable name.
|
||||
*/
|
||||
Blockly.Variables.generateUniqueName = function(workspace) {
|
||||
var variableList = workspace.variableList;
|
||||
var variableList = workspace.getAllVariables();
|
||||
var newName = '';
|
||||
if (variableList.length) {
|
||||
var nameSuffix = 1;
|
||||
@@ -179,7 +195,7 @@ Blockly.Variables.generateUniqueName = function(workspace) {
|
||||
while (!newName) {
|
||||
var inUse = false;
|
||||
for (var i = 0; i < variableList.length; i++) {
|
||||
if (variableList[i].toLowerCase() == potName) {
|
||||
if (variableList[i].name.toLowerCase() == potName) {
|
||||
// This potential name is already used.
|
||||
inUse = true;
|
||||
break;
|
||||
@@ -222,13 +238,21 @@ Blockly.Variables.createVariable = function(workspace, opt_callback) {
|
||||
Blockly.Variables.promptName(Blockly.Msg.NEW_VARIABLE_TITLE, defaultName,
|
||||
function(text) {
|
||||
if (text) {
|
||||
if (workspace.variableIndexOf(text) != -1) {
|
||||
if (workspace.getVariable(text)) {
|
||||
Blockly.alert(Blockly.Msg.VARIABLE_ALREADY_EXISTS.replace('%1',
|
||||
text.toLowerCase()),
|
||||
function() {
|
||||
promptAndCheckWithAlert(text); // Recurse
|
||||
});
|
||||
} else {
|
||||
}
|
||||
else if (!Blockly.Procedures.isLegalName_(text, workspace)) {
|
||||
Blockly.alert(Blockly.Msg.PROCEDURE_ALREADY_EXISTS.replace('%1',
|
||||
text.toLowerCase()),
|
||||
function() {
|
||||
promptAndCheckWithAlert(text); // Recurse
|
||||
});
|
||||
}
|
||||
else {
|
||||
workspace.createVariable(text);
|
||||
if (opt_callback) {
|
||||
opt_callback(text);
|
||||
@@ -267,3 +291,18 @@ Blockly.Variables.promptName = function(promptText, defaultText, callback) {
|
||||
callback(newVar);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate XML string for variable field.
|
||||
* @param {!Blockly.VariableModel} variableModel The variable model to generate
|
||||
* an XML string from.
|
||||
* @return {string} The generated XML.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Variables.generateVariableFieldXml_ = function(variableModel) {
|
||||
var xmlString = '<field name="VAR" ' + 'variableType="' +
|
||||
variableModel.type + '" id="' + variableModel.getId() + '">'+
|
||||
variableModel.name +
|
||||
'</field>';
|
||||
return xmlString;
|
||||
};
|
||||
|
||||
+180
-83
@@ -26,6 +26,7 @@
|
||||
|
||||
goog.provide('Blockly.Workspace');
|
||||
|
||||
goog.require('Blockly.VariableMap');
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.math');
|
||||
|
||||
@@ -74,12 +75,15 @@ Blockly.Workspace = function(opt_options) {
|
||||
* @private
|
||||
*/
|
||||
this.blockDB_ = Object.create(null);
|
||||
/*
|
||||
* @type {!Array.<string>}
|
||||
* A list of all of the named variables in the workspace, including variables
|
||||
|
||||
/**
|
||||
* @type {!Blockly.VariableMap}
|
||||
* A map from variable type to list of variable names. The lists contain all
|
||||
* of the named variables in the workspace, including variables
|
||||
* that are not currently in use.
|
||||
* @private
|
||||
*/
|
||||
this.variableList = [];
|
||||
this.variableMap_ = new Blockly.VariableMap(this);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -115,19 +119,20 @@ Blockly.Workspace.SCAN_ANGLE = 3;
|
||||
|
||||
/**
|
||||
* Add a block to the list of top blocks.
|
||||
* @param {!Blockly.Block} block Block to remove.
|
||||
* @param {!Blockly.Block} block Block to add.
|
||||
*/
|
||||
Blockly.Workspace.prototype.addTopBlock = function(block) {
|
||||
this.topBlocks_.push(block);
|
||||
if (this.isFlyout) {
|
||||
// This is for the (unlikely) case where you have a variable in a block in
|
||||
// an always-open flyout. It needs to be possible to edit the block in the
|
||||
// flyout, so the contents of the dropdown need to be correct.
|
||||
var variables = Blockly.Variables.allUsedVariables(block);
|
||||
for (var i = 0; i < variables.length; i++) {
|
||||
if (this.variableList.indexOf(variables[i]) == -1) {
|
||||
this.variableList.push(variables[i]);
|
||||
}
|
||||
if (!this.isFlyout) {
|
||||
return;
|
||||
}
|
||||
// This is for the (unlikely) case where you have a variable in a block in
|
||||
// an always-open flyout. It needs to be possible to edit the block in the
|
||||
// flyout, so the contents of the dropdown need to be correct.
|
||||
var variableNames = Blockly.Variables.allUsedVariables(block);
|
||||
for (var i = 0, name; name = variableNames[i]; i++) {
|
||||
if (!this.getVariable(name)) {
|
||||
this.createVariable(name);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -191,85 +196,117 @@ Blockly.Workspace.prototype.clear = function() {
|
||||
if (!existingGroup) {
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
|
||||
this.variableList.length = 0;
|
||||
this.variableMap_.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* Walk the workspace and update the list of variables to only contain ones in
|
||||
* Walk the workspace and update the map of variables to only contain ones in
|
||||
* use on the workspace. Use when loading new workspaces from disk.
|
||||
* @param {boolean} clearList True if the old variable list should be cleared.
|
||||
* @param {boolean} clear True if the old variable map should be cleared.
|
||||
*/
|
||||
Blockly.Workspace.prototype.updateVariableList = function(clearList) {
|
||||
Blockly.Workspace.prototype.updateVariableStore = function(clear) {
|
||||
// TODO: Sort
|
||||
if (!this.isFlyout) {
|
||||
// Update the list in place so that the flyout's references stay correct.
|
||||
if (clearList) {
|
||||
this.variableList.length = 0;
|
||||
if (this.isFlyout) {
|
||||
return;
|
||||
}
|
||||
var variableNames = Blockly.Variables.allUsedVariables(this);
|
||||
var varList = [];
|
||||
for (var i = 0, name; name = variableNames[i]; i++) {
|
||||
// Get variable model with the used variable name.
|
||||
var tempVar = this.getVariable(name);
|
||||
if (tempVar) {
|
||||
varList.push({'name': tempVar.name, 'type': tempVar.type,
|
||||
'id': tempVar.getId()});
|
||||
}
|
||||
var allVariables = Blockly.Variables.allUsedVariables(this);
|
||||
for (var i = 0; i < allVariables.length; i++) {
|
||||
this.createVariable(allVariables[i]);
|
||||
else {
|
||||
varList.push({'name': name, 'type': null, 'id': null});
|
||||
// TODO(marisaleung): Use variable.type and variable.getId() once variable
|
||||
// instances are storing more than just name.
|
||||
}
|
||||
}
|
||||
if (clear) {
|
||||
this.variableMap_.clear();
|
||||
}
|
||||
// Update the list in place so that the flyout's references stay correct.
|
||||
for (var i = 0, varDict; varDict = varList[i]; i++) {
|
||||
if (!this.getVariable(varDict.name)) {
|
||||
this.createVariable(varDict.name, varDict.type, varDict.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Rename a variable by updating its name in the variable list.
|
||||
* TODO: #468
|
||||
* @param {string} oldName Variable to rename.
|
||||
* Rename a variable by updating its name in the variable map. Identify the
|
||||
* variable to rename with the given variable.
|
||||
* @param {?Blockly.VariableModel} variable Variable to rename.
|
||||
* @param {string} newName New variable name.
|
||||
*/
|
||||
Blockly.Workspace.prototype.renameVariable = function(oldName, newName) {
|
||||
// Find the old name in the list.
|
||||
var variableIndex = this.variableIndexOf(oldName);
|
||||
var newVariableIndex = this.variableIndexOf(newName);
|
||||
Blockly.Workspace.prototype.renameVariableInternal_ = function(variable, newName) {
|
||||
var newVariable = this.getVariable(newName);
|
||||
var oldCase;
|
||||
|
||||
// We might be renaming to an existing name but with different case. If so,
|
||||
// we will also update all of the blocks using the new name to have the
|
||||
// correct case.
|
||||
if (newVariableIndex != -1 &&
|
||||
this.variableList[newVariableIndex] != newName) {
|
||||
var oldCase = this.variableList[newVariableIndex];
|
||||
// If they are different types, throw an error.
|
||||
if (variable && newVariable && variable.type != newVariable.type) {
|
||||
throw Error('Variable "' + variable.name + '" is type "' + variable.type +
|
||||
'" and variable "' + newName + '" is type "' + newVariable.type +
|
||||
'". Both must be the same type.');
|
||||
}
|
||||
|
||||
// Find if newVariable case is different.
|
||||
if (newVariable && newVariable.name != newName) {
|
||||
oldCase = newVariable.name;
|
||||
}
|
||||
|
||||
Blockly.Events.setGroup(true);
|
||||
var blocks = this.getAllBlocks();
|
||||
// Iterate through every block.
|
||||
// Iterate through every block and update name.
|
||||
for (var i = 0; i < blocks.length; i++) {
|
||||
blocks[i].renameVar(oldName, newName);
|
||||
blocks[i].renameVar(variable.name, newName);
|
||||
if (oldCase) {
|
||||
blocks[i].renameVar(oldCase, newName);
|
||||
}
|
||||
}
|
||||
this.variableMap_.renameVariable(variable, newName);
|
||||
Blockly.Events.setGroup(false);
|
||||
};
|
||||
|
||||
|
||||
if (variableIndex == newVariableIndex ||
|
||||
variableIndex != -1 && newVariableIndex == -1) {
|
||||
// Only changing case, or renaming to a completely novel name.
|
||||
this.variableList[variableIndex] = newName;
|
||||
} else if (variableIndex != -1 && newVariableIndex != -1) {
|
||||
// Renaming one existing variable to another existing variable.
|
||||
// The case might have changed, so we update the destination ID.
|
||||
this.variableList[newVariableIndex] = newName;
|
||||
this.variableList.splice(variableIndex, 1);
|
||||
} else {
|
||||
this.variableList.push(newName);
|
||||
console.log('Tried to rename an non-existent variable.');
|
||||
}
|
||||
/**
|
||||
* Rename a variable by updating its name in the variable map. Identify the
|
||||
* variable to rename with the given name.
|
||||
* @param {string} oldName Variable to rename.
|
||||
* @param {string} newName New variable name.
|
||||
*/
|
||||
Blockly.Workspace.prototype.renameVariable = function(oldName, newName) {
|
||||
// Warning: Prefer to use renameVariableById.
|
||||
var variable = this.getVariable(oldName);
|
||||
this.renameVariableInternal_(variable, newName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a variable with the given name.
|
||||
* TODO: #468
|
||||
* @param {string} name The new variable's name.
|
||||
* Rename a variable by updating its name in the variable map. Identify the
|
||||
* variable to rename with the given id.
|
||||
* @param {string} id Id of the variable to rename.
|
||||
* @param {string} newName New variable name.
|
||||
*/
|
||||
Blockly.Workspace.prototype.createVariable = function(name) {
|
||||
var index = this.variableIndexOf(name);
|
||||
if (index == -1) {
|
||||
this.variableList.push(name);
|
||||
}
|
||||
Blockly.Workspace.prototype.renameVariableById = function(id, newName) {
|
||||
var variable = this.getVariableById(id);
|
||||
this.renameVariableInternal_(variable, newName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a variable with a given name, optional type, and optional id.
|
||||
* @param {!string} name The name of the variable. This must be unique across
|
||||
* variables and procedures.
|
||||
* @param {?string} opt_type The type of the variable like 'int' or 'string'.
|
||||
* Does not need to be unique. Field_variable can filter variables based on
|
||||
* their type. This will default to '' which is a specific type.
|
||||
* @param {?string} opt_id The unique id of the variable. This will default to
|
||||
* a UUID.
|
||||
* @return {?Blockly.VariableModel} The newly created variable.
|
||||
*/
|
||||
Blockly.Workspace.prototype.createVariable = function(name, opt_type, opt_id) {
|
||||
return this.variableMap_.createVariable(name, opt_type, opt_id);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -287,7 +324,7 @@ Blockly.Workspace.prototype.getVariableUses = function(name) {
|
||||
for (var j = 0; j < blockVariables.length; j++) {
|
||||
var varName = blockVariables[j];
|
||||
// Variable name may be null if the block is only half-built.
|
||||
if (varName && Blockly.Names.equals(varName, name)) {
|
||||
if (varName && name && Blockly.Names.equals(varName, name)) {
|
||||
uses.push(blocks[i]);
|
||||
}
|
||||
}
|
||||
@@ -297,15 +334,11 @@ Blockly.Workspace.prototype.getVariableUses = function(name) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a variables and all of its uses from this workspace. May prompt the
|
||||
* user for confirmation.
|
||||
* Delete a variable by the passed in name and all of its uses from this
|
||||
* workspace. May prompt the user for confirmation.
|
||||
* @param {string} name Name of variable to delete.
|
||||
*/
|
||||
Blockly.Workspace.prototype.deleteVariable = function(name) {
|
||||
var variableIndex = this.variableIndexOf(name);
|
||||
if (variableIndex == -1) {
|
||||
return;
|
||||
}
|
||||
// Check whether this variable is a function parameter before deleting.
|
||||
var uses = this.getVariableUses(name);
|
||||
for (var i = 0, block; block = uses[i]; i++) {
|
||||
@@ -321,6 +354,7 @@ Blockly.Workspace.prototype.deleteVariable = function(name) {
|
||||
}
|
||||
|
||||
var workspace = this;
|
||||
var variable = workspace.getVariable(name);
|
||||
if (uses.length > 1) {
|
||||
// Confirm before deleting multiple blocks.
|
||||
Blockly.confirm(
|
||||
@@ -328,29 +362,43 @@ Blockly.Workspace.prototype.deleteVariable = function(name) {
|
||||
replace('%2', name),
|
||||
function(ok) {
|
||||
if (ok) {
|
||||
workspace.deleteVariableInternal_(name);
|
||||
workspace.deleteVariableInternal_(variable);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// No confirmation necessary for a single block.
|
||||
this.deleteVariableInternal_(name);
|
||||
this.deleteVariableInternal_(variable);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a variables by the passed in id and all of its uses from this
|
||||
* workspace. May prompt the user for confirmation.
|
||||
* @param {string} id Id of variable to delete.
|
||||
*/
|
||||
Blockly.Workspace.prototype.deleteVariableById = function(id) {
|
||||
var variable = this.getVariableById(id);
|
||||
if (variable) {
|
||||
this.deleteVariableInternal_(variable);
|
||||
} else {
|
||||
console.warn("Can't delete non-existant variable: " + id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes a variable and all of its uses from this workspace without asking the
|
||||
* user for confirmation.
|
||||
* @param {Blockly.VariableModel} variable Variable to delete.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Workspace.prototype.deleteVariableInternal_ = function(name) {
|
||||
var uses = this.getVariableUses(name);
|
||||
var variableIndex = this.variableIndexOf(name);
|
||||
Blockly.Workspace.prototype.deleteVariableInternal_ = function(variable) {
|
||||
var uses = this.getVariableUses(variable.name);
|
||||
Blockly.Events.setGroup(true);
|
||||
for (var i = 0; i < uses.length; i++) {
|
||||
uses[i].dispose(true, false);
|
||||
}
|
||||
this.variableMap_.deleteVariable(variable);
|
||||
Blockly.Events.setGroup(false);
|
||||
this.variableList.splice(variableIndex, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -359,16 +407,34 @@ Blockly.Workspace.prototype.deleteVariableInternal_ = function(name) {
|
||||
* @param {string} name The name to check for.
|
||||
* @return {number} The index of the name in the variable list, or -1 if it is
|
||||
* not present.
|
||||
* @deprecated April 2017
|
||||
*/
|
||||
Blockly.Workspace.prototype.variableIndexOf = function(name) {
|
||||
for (var i = 0, varname; varname = this.variableList[i]; i++) {
|
||||
if (Blockly.Names.equals(varname, name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
console.warn(
|
||||
'Deprecated call to Blockly.Workspace.prototype.variableIndexOf');
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the variable by the given name and return it. Return null if it is not
|
||||
* found.
|
||||
* @param {!string} name The name to check for.
|
||||
* @return {?Blockly.VariableModel} the variable with the given name.
|
||||
*/
|
||||
Blockly.Workspace.prototype.getVariable = function(name) {
|
||||
return this.variableMap_.getVariable(name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the variable by the given id and return it. Return null if it is not
|
||||
* found.
|
||||
* @param {!string} id The id to check for.
|
||||
* @return {?Blockly.VariableModel} The variable with the given id.
|
||||
*/
|
||||
Blockly.Workspace.prototype.getVariableById = function(id) {
|
||||
return this.variableMap_.getVariableById(id);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the horizontal offset of the workspace.
|
||||
* Intended for LTR/RTL compatibility in XML.
|
||||
@@ -426,10 +492,14 @@ Blockly.Workspace.prototype.undo = function(redo) {
|
||||
}
|
||||
events = Blockly.Events.filter(events, redo);
|
||||
Blockly.Events.recordUndo = false;
|
||||
for (var i = 0, event; event = events[i]; i++) {
|
||||
event.run(redo);
|
||||
try {
|
||||
for (var i = 0, event; event = events[i]; i++) {
|
||||
event.run(redo);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Blockly.Events.recordUndo = true;
|
||||
}
|
||||
Blockly.Events.recordUndo = true;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -504,6 +574,33 @@ Blockly.Workspace.prototype.allInputsFilled = function(opt_shadowBlocksAreFilled
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the variable with the specified type. If type is null, return list of
|
||||
* variables with empty string type.
|
||||
* @param {?string} type Type of the variables to find.
|
||||
* @return {Array.<Blockly.VariableModel>} The sought after variables of the
|
||||
* passed in type. An empty array if none are found.
|
||||
*/
|
||||
Blockly.Workspace.prototype.getVariablesOfType = function(type) {
|
||||
return this.variableMap_.getVariablesOfType(type);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return all variable types.
|
||||
* @return {!Array.<string>} List of variable types.
|
||||
*/
|
||||
Blockly.Workspace.prototype.getVariableTypes = function() {
|
||||
return this.variableMap_.getVariableTypes();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return all variables of all types.
|
||||
* @return {!Array.<Blockly.VariableModel>} List of variable models.
|
||||
*/
|
||||
Blockly.Workspace.prototype.getAllVariables = function() {
|
||||
return this.variableMap_.getAllVariables();
|
||||
};
|
||||
|
||||
/**
|
||||
* Database of all workspaces.
|
||||
* @private
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Object in charge of loading, storing, and playing audio for a
|
||||
* workspace.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.WorkspaceAudio');
|
||||
|
||||
/**
|
||||
* Class for loading, storing, and playing audio for a workspace.
|
||||
* @param {Blockly.WorkspaceSvg} parentWorkspace The parent of the workspace
|
||||
* this audio object belongs to, or null.
|
||||
*/
|
||||
Blockly.WorkspaceAudio = function(parentWorkspace) {
|
||||
|
||||
/**
|
||||
* The parent of the workspace this object belongs to, or null. May be
|
||||
* checked for sounds that this object can't find.
|
||||
* @type {Blockly.WorkspaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.parentWorkspace_ = parentWorkspace;
|
||||
|
||||
/**
|
||||
* Database of pre-loaded sounds.
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
this.SOUNDS_ = Object.create(null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Time that the last sound was played.
|
||||
* @type {Date}
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceAudio.prototype.lastSound_ = null;
|
||||
|
||||
/**
|
||||
* Dispose of this audio manager.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceAudio.prototype.dispose = function() {
|
||||
this.parentWorkspace_ = null;
|
||||
this.SOUNDS_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load an audio file. Cache it, ready for instantaneous playing.
|
||||
* @param {!Array.<string>} filenames List of file types in decreasing order of
|
||||
* preference (i.e. increasing size). E.g. ['media/go.mp3', 'media/go.wav']
|
||||
* Filenames include path from Blockly's root. File extensions matter.
|
||||
* @param {string} name Name of sound.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceAudio.prototype.load = function(filenames, name) {
|
||||
if (!filenames.length) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var audioTest = new window['Audio']();
|
||||
} catch (e) {
|
||||
// No browser support for Audio.
|
||||
// IE can throw an error even if the Audio object exists.
|
||||
return;
|
||||
}
|
||||
var sound;
|
||||
for (var i = 0; i < filenames.length; i++) {
|
||||
var filename = filenames[i];
|
||||
var ext = filename.match(/\.(\w+)$/);
|
||||
if (ext && audioTest.canPlayType('audio/' + ext[1])) {
|
||||
// Found an audio format we can play.
|
||||
sound = new window['Audio'](filename);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sound && sound.play) {
|
||||
this.SOUNDS_[name] = sound;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Preload all the audio files so that they play quickly when asked for.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceAudio.prototype.preload = function() {
|
||||
for (var name in this.SOUNDS_) {
|
||||
var sound = this.SOUNDS_[name];
|
||||
sound.volume = .01;
|
||||
sound.play();
|
||||
sound.pause();
|
||||
// iOS can only process one sound at a time. Trying to load more than one
|
||||
// corrupts the earlier ones. Just load one and leave the others uncached.
|
||||
if (goog.userAgent.IPAD || goog.userAgent.IPHONE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Play a named sound at specified volume. If volume is not specified,
|
||||
* use full volume (1).
|
||||
* @param {string} name Name of sound.
|
||||
* @param {number=} opt_volume Volume of sound (0-1).
|
||||
*/
|
||||
Blockly.WorkspaceAudio.prototype.play = function(name, opt_volume) {
|
||||
var sound = this.SOUNDS_[name];
|
||||
if (sound) {
|
||||
// Don't play one sound on top of another.
|
||||
var now = new Date;
|
||||
if (this.lastSound_ != null &&
|
||||
now - this.lastSound_ < Blockly.SOUND_LIMIT) {
|
||||
return;
|
||||
}
|
||||
this.lastSound_ = now;
|
||||
var mySound;
|
||||
var ie9 = goog.userAgent.DOCUMENT_MODE &&
|
||||
goog.userAgent.DOCUMENT_MODE === 9;
|
||||
if (ie9 || goog.userAgent.IPAD || goog.userAgent.ANDROID) {
|
||||
// Creating a new audio node causes lag in IE9, Android and iPad. Android
|
||||
// and IE9 refetch the file from the server, iPad uses a singleton audio
|
||||
// node which must be deleted and recreated for each new audio tag.
|
||||
mySound = sound;
|
||||
} else {
|
||||
mySound = sound.cloneNode();
|
||||
}
|
||||
mySound.volume = (opt_volume === undefined ? 1 : opt_volume);
|
||||
mySound.play();
|
||||
} else if (this.parentWorkspace_) {
|
||||
// Maybe a workspace on a lower level knows about this sound.
|
||||
this.parentWorkspace_.getAudioManager().play(name, opt_volume);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Methods for dragging a workspace visually.
|
||||
* @author fenichel@google.com (Rachel Fenichel)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.WorkspaceDragger');
|
||||
|
||||
goog.require('goog.math.Coordinate');
|
||||
goog.require('goog.asserts');
|
||||
|
||||
|
||||
/**
|
||||
* Class for a workspace dragger. It moves the workspace around when it is
|
||||
* being dragged by a mouse or touch.
|
||||
* Note that the workspace itself manages whether or not it has a drag surface
|
||||
* and how to do translations based on that. This simply passes the right
|
||||
* commands based on events.
|
||||
* @param {!Blockly.WorkspaceSvg} workspace The workspace to drag.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.WorkspaceDragger = function(workspace) {
|
||||
/**
|
||||
* @type {!Blockly.WorkspaceSvg}
|
||||
* @private
|
||||
*/
|
||||
this.workspace_ = workspace;
|
||||
|
||||
/**
|
||||
* The workspace's metrics object at the beginning of the drag. Contains size
|
||||
* and position metrics of a workspace.
|
||||
* Coordinate system: pixel coordinates.
|
||||
* @type {!Object}
|
||||
* @private
|
||||
*/
|
||||
this.startDragMetrics_ = workspace.getMetrics();
|
||||
|
||||
/**
|
||||
* The scroll position of the workspace at the beginning of the drag.
|
||||
* Coordinate system: pixel coordinates.
|
||||
* @type {!goog.math.Coordinate}
|
||||
* @private
|
||||
*/
|
||||
this.startScrollXY_ = new goog.math.Coordinate(workspace.scrollX,
|
||||
workspace.scrollY);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sever all links from this object.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceDragger.prototype.dispose = function() {
|
||||
this.workspace_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start dragging the workspace.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceDragger.prototype.startDrag = function() {
|
||||
if (Blockly.selected) {
|
||||
Blockly.selected.unselect();
|
||||
}
|
||||
this.workspace_.setupDragSurface();
|
||||
};
|
||||
|
||||
/**
|
||||
* Finish dragging the workspace and put everything back where it belongs.
|
||||
* @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel coordinates.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceDragger.prototype.endDrag = function(currentDragDeltaXY) {
|
||||
// Make sure everything is up to date.
|
||||
this.drag(currentDragDeltaXY);
|
||||
this.workspace_.resetDragSurface();
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the workspace based on the most recent mouse movements.
|
||||
* @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has
|
||||
* moved from the position at the start of the drag, in pixel coordinates.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceDragger.prototype.drag = function(currentDragDeltaXY) {
|
||||
var metrics = this.startDragMetrics_;
|
||||
var newXY = goog.math.Coordinate.sum(this.startScrollXY_, currentDragDeltaXY);
|
||||
|
||||
// Bound the new XY based on workspace bounds.
|
||||
var x = Math.min(newXY.x, -metrics.contentLeft);
|
||||
var y = Math.min(newXY.y, -metrics.contentTop);
|
||||
x = Math.max(x, metrics.viewWidth - metrics.contentLeft -
|
||||
metrics.contentWidth);
|
||||
y = Math.max(y, metrics.viewHeight - metrics.contentTop -
|
||||
metrics.contentHeight);
|
||||
|
||||
x = -x - metrics.contentLeft;
|
||||
y = -y - metrics.contentTop;
|
||||
|
||||
this.updateScroll_(x, y);
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the scrollbars to drag the workspace.
|
||||
* x and y are in pixels.
|
||||
* @param {number} x The new x position to move the scrollbar to.
|
||||
* @param {number} y The new y position to move the scrollbar to.
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceDragger.prototype.updateScroll_ = function(x, y) {
|
||||
this.workspace_.scrollbar.set(x, y);
|
||||
};
|
||||
+234
-237
@@ -30,11 +30,14 @@ goog.provide('Blockly.WorkspaceSvg');
|
||||
//goog.require('Blockly.BlockSvg');
|
||||
goog.require('Blockly.ConnectionDB');
|
||||
goog.require('Blockly.constants');
|
||||
goog.require('Blockly.Gesture');
|
||||
goog.require('Blockly.Grid');
|
||||
goog.require('Blockly.Options');
|
||||
goog.require('Blockly.ScrollbarPair');
|
||||
goog.require('Blockly.Touch');
|
||||
goog.require('Blockly.Trashcan');
|
||||
goog.require('Blockly.Workspace');
|
||||
goog.require('Blockly.WorkspaceAudio');
|
||||
goog.require('Blockly.WorkspaceDragSurfaceSvg');
|
||||
goog.require('Blockly.Xml');
|
||||
goog.require('Blockly.ZoomControls');
|
||||
@@ -76,12 +79,6 @@ Blockly.WorkspaceSvg = function(options, opt_blockDragSurface, opt_wsDragSurface
|
||||
this.useWorkspaceDragSurface_ =
|
||||
this.workspaceDragSurface_ && Blockly.utils.is3dSupported();
|
||||
|
||||
/**
|
||||
* Database of pre-loaded sounds.
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
this.SOUNDS_ = Object.create(null);
|
||||
/**
|
||||
* List of currently highlighted blocks. Block highlighting is often used to
|
||||
* visually mark blocks currently being executed.
|
||||
@@ -90,6 +87,21 @@ Blockly.WorkspaceSvg = function(options, opt_blockDragSurface, opt_wsDragSurface
|
||||
*/
|
||||
this.highlightedBlocks_ = [];
|
||||
|
||||
/**
|
||||
* Object in charge of loading, storing, and playing audio for a workspace.
|
||||
* @type {Blockly.WorkspaceAudio}
|
||||
* @private
|
||||
*/
|
||||
this.audioManager_ = new Blockly.WorkspaceAudio(options.parentWorkspace);
|
||||
|
||||
/**
|
||||
* This workspace's grid object or null.
|
||||
* @type {Blockly.Grid}
|
||||
* @private
|
||||
*/
|
||||
this.grid_ = this.options.gridPattern ?
|
||||
new Blockly.Grid(options.gridPattern, options.gridOptions) : null;
|
||||
|
||||
this.registerToolboxCategoryCallback(Blockly.VARIABLE_CATEGORY_NAME,
|
||||
Blockly.Variables.flyoutCategory);
|
||||
this.registerToolboxCategoryCallback(Blockly.PROCEDURE_CATEGORY_NAME,
|
||||
@@ -125,15 +137,6 @@ Blockly.WorkspaceSvg.prototype.isFlyout = false;
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.isMutator = false;
|
||||
|
||||
/**
|
||||
* Is this workspace currently being dragged around?
|
||||
* DRAG_NONE - No drag operation.
|
||||
* DRAG_BEGIN - Still inside the initial DRAG_RADIUS.
|
||||
* DRAG_FREE - Workspace has been dragged further than DRAG_RADIUS.
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.dragMode_ = Blockly.DRAG_NONE;
|
||||
|
||||
/**
|
||||
* Whether this workspace has resizes enabled.
|
||||
* Disable during batch operations for a performance improvement.
|
||||
@@ -143,25 +146,25 @@ Blockly.WorkspaceSvg.prototype.dragMode_ = Blockly.DRAG_NONE;
|
||||
Blockly.WorkspaceSvg.prototype.resizesEnabled_ = true;
|
||||
|
||||
/**
|
||||
* Current horizontal scrolling offset.
|
||||
* Current horizontal scrolling offset in pixel units.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.scrollX = 0;
|
||||
|
||||
/**
|
||||
* Current vertical scrolling offset.
|
||||
* Current vertical scrolling offset in pixel units.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.scrollY = 0;
|
||||
|
||||
/**
|
||||
* Horizontal scroll value when scrolling started.
|
||||
* Horizontal scroll value when scrolling started in pixel units.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.startScrollX = 0;
|
||||
|
||||
/**
|
||||
* Vertical scroll value when scrolling started.
|
||||
* Vertical scroll value when scrolling started in pixel units.
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.startScrollY = 0;
|
||||
@@ -191,6 +194,13 @@ Blockly.WorkspaceSvg.prototype.trashcan = null;
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.scrollbar = null;
|
||||
|
||||
/**
|
||||
* The current gesture in progress on this workspace, if any.
|
||||
* @type {Blockly.Gesture}
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.currentGesture_ = null;
|
||||
|
||||
/**
|
||||
* This workspace's surface for dragging blocks, if it exists.
|
||||
* @type {Blockly.BlockDragSurfaceSvg}
|
||||
@@ -223,13 +233,6 @@ Blockly.WorkspaceSvg.prototype.useWorkspaceDragSurface_ = false;
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.isDragSurfaceActive_ = false;
|
||||
|
||||
/**
|
||||
* Time that the last sound was played.
|
||||
* @type {Date}
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.lastSound_ = null;
|
||||
|
||||
/**
|
||||
* Last known position of the page scroll.
|
||||
* This is used to determine whether we have recalculated screen coordinate
|
||||
@@ -338,14 +341,19 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) {
|
||||
*/
|
||||
this.svgGroup_ = Blockly.utils.createSvgElement('g',
|
||||
{'class': 'blocklyWorkspace'}, null);
|
||||
|
||||
// Note that a <g> alone does not receive mouse events--it must have a
|
||||
// valid target inside it. If no background class is specified, as in the
|
||||
// flyout, the workspace will not receive mouse events.
|
||||
if (opt_backgroundClass) {
|
||||
/** @type {SVGElement} */
|
||||
this.svgBackground_ = Blockly.utils.createSvgElement('rect',
|
||||
{'height': '100%', 'width': '100%', 'class': opt_backgroundClass},
|
||||
this.svgGroup_);
|
||||
if (opt_backgroundClass == 'blocklyMainBackground') {
|
||||
|
||||
if (opt_backgroundClass == 'blocklyMainBackground' && this.grid_) {
|
||||
this.svgBackground_.style.fill =
|
||||
'url(#' + this.options.gridPattern.id + ')';
|
||||
'url(#' + this.grid_.getPatternId() + ')';
|
||||
}
|
||||
}
|
||||
/** @type {SVGElement} */
|
||||
@@ -365,9 +373,6 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) {
|
||||
if (!this.isFlyout) {
|
||||
Blockly.bindEventWithChecks_(this.svgGroup_, 'mousedown', this,
|
||||
this.onMouseDown_);
|
||||
var thisWorkspace = this;
|
||||
Blockly.bindEvent_(this.svgGroup_, 'touchstart', null,
|
||||
function(e) {Blockly.longStart_(e, thisWorkspace);});
|
||||
if (this.options.zoomOptions && this.options.zoomOptions.wheel) {
|
||||
// Mouse-wheel.
|
||||
Blockly.bindEventWithChecks_(this.svgGroup_, 'wheel', this,
|
||||
@@ -384,7 +389,9 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) {
|
||||
*/
|
||||
this.toolbox_ = new Blockly.Toolbox(this);
|
||||
}
|
||||
this.updateGridPattern_();
|
||||
if (this.grid_) {
|
||||
this.grid_.update(this.scale);
|
||||
}
|
||||
this.recordDeleteAreas();
|
||||
return this.svgGroup_;
|
||||
};
|
||||
@@ -396,6 +403,9 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) {
|
||||
Blockly.WorkspaceSvg.prototype.dispose = function() {
|
||||
// Stop rerendering.
|
||||
this.rendered = false;
|
||||
if (this.currentGesture_) {
|
||||
this.currentGesture_.cancel();
|
||||
}
|
||||
Blockly.WorkspaceSvg.superClass_.dispose.call(this);
|
||||
if (this.svgGroup_) {
|
||||
goog.dom.removeNode(this.svgGroup_);
|
||||
@@ -424,6 +434,16 @@ Blockly.WorkspaceSvg.prototype.dispose = function() {
|
||||
this.zoomControls_ = null;
|
||||
}
|
||||
|
||||
if (this.audioManager_) {
|
||||
this.audioManager_.dispose();
|
||||
this.audioManager_ = null;
|
||||
}
|
||||
|
||||
if (this.grid_) {
|
||||
this.grid_.dispose();
|
||||
this.grid_ = null;
|
||||
}
|
||||
|
||||
if (this.toolboxCategoryCallbacks_) {
|
||||
this.toolboxCategoryCallbacks_ = null;
|
||||
}
|
||||
@@ -496,8 +516,16 @@ Blockly.WorkspaceSvg.prototype.addFlyout_ = function(tagName) {
|
||||
horizontalLayout: this.horizontalLayout,
|
||||
toolboxPosition: this.options.toolboxPosition
|
||||
};
|
||||
/** @type {Blockly.Flyout} */
|
||||
this.flyout_ = new Blockly.Flyout(workspaceOptions);
|
||||
/**
|
||||
* @type {!Blockly.Flyout}
|
||||
* @private
|
||||
*/
|
||||
this.flyout_ = null;
|
||||
if (this.horizontalLayout) {
|
||||
this.flyout_ = new Blockly.HorizontalFlyout(workspaceOptions);
|
||||
} else {
|
||||
this.flyout_ = new Blockly.VerticalFlyout(workspaceOptions);
|
||||
}
|
||||
this.flyout_.autoClose = false;
|
||||
|
||||
// Return the element so that callers can place it in their desired
|
||||
@@ -811,10 +839,20 @@ Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) {
|
||||
this.remainingCapacity()) {
|
||||
return;
|
||||
}
|
||||
Blockly.terminateDrag_(); // Dragging while pasting? No.
|
||||
if (this.currentGesture_) {
|
||||
this.currentGesture_.cancel(); // Dragging while pasting? No.
|
||||
}
|
||||
Blockly.Events.disable();
|
||||
try {
|
||||
var block = Blockly.Xml.domToBlock(xmlBlock, this);
|
||||
// Rerender to get around problem with IE and Edge not measuring text
|
||||
// correctly when it is hidden.
|
||||
if (goog.userAgent.IE || goog.userAgent.EDGE) {
|
||||
var blocks = block.getDescendants();
|
||||
for (var i = blocks.length - 1; i >= 0; i--) {
|
||||
blocks[i].render(false);
|
||||
}
|
||||
}
|
||||
// Move the duplicate to original position.
|
||||
var blockX = parseInt(xmlBlock.getAttribute('x'), 10);
|
||||
var blockY = parseInt(xmlBlock.getAttribute('y'), 10);
|
||||
@@ -862,25 +900,86 @@ Blockly.WorkspaceSvg.prototype.paste = function(xmlBlock) {
|
||||
Blockly.Events.enable();
|
||||
}
|
||||
if (Blockly.Events.isEnabled() && !block.isShadow()) {
|
||||
Blockly.Events.fire(new Blockly.Events.Create(block));
|
||||
Blockly.Events.fire(new Blockly.Events.BlockCreate(block));
|
||||
}
|
||||
block.select();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new variable with the given name. Update the flyout to show the new
|
||||
* variable immediately.
|
||||
* TODO: #468
|
||||
* @param {string} name The new variable's name.
|
||||
* Refresh the toolbox unless there's a drag in progress.
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.createVariable = function(name) {
|
||||
Blockly.WorkspaceSvg.superClass_.createVariable.call(this, name);
|
||||
// Don't refresh the toolbox if there's a drag in progress.
|
||||
if (this.toolbox_ && this.toolbox_.flyout_ && !Blockly.Flyout.startFlyout_) {
|
||||
Blockly.WorkspaceSvg.prototype.refreshToolboxSelection_ = function() {
|
||||
if (this.toolbox_ && this.toolbox_.flyout_ && !this.currentGesture_) {
|
||||
this.toolbox_.refreshSelection();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Rename a variable by updating its name in the variable list.
|
||||
* TODO: google/blockly:#468
|
||||
* @param {string} oldName Variable to rename.
|
||||
* @param {string} newName New variable name.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.renameVariable = function(oldName, newName) {
|
||||
Blockly.WorkspaceSvg.superClass_.renameVariable.call(this, oldName, newName);
|
||||
this.refreshToolboxSelection_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Rename a variable by updating its name in the variable map. Update the
|
||||
* flyout to show the renamed variable immediately.
|
||||
* @param {string} id Id of the variable to rename.
|
||||
* @param {string} newName New variable name.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.renameVariableById = function(id, newName) {
|
||||
Blockly.WorkspaceSvg.superClass_.renameVariableById.call(this, id, newName);
|
||||
this.refreshToolboxSelection_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a variable by the passed in name. Update the flyout to show
|
||||
* immediately that the variable is deleted.
|
||||
* @param {string} name Name of variable to delete.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.deleteVariable = function(name) {
|
||||
Blockly.WorkspaceSvg.superClass_.deleteVariable.call(this, name);
|
||||
this.refreshToolboxSelection_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a variable by the passed in id. Update the flyout to show
|
||||
* immediately that the variable is deleted.
|
||||
* @param {string} id Id of variable to delete.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.deleteVariableById = function(id) {
|
||||
Blockly.WorkspaceSvg.superClass_.deleteVariableById.call(this, id);
|
||||
this.refreshToolboxSelection_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new variable with the given name. Update the flyout to show the new
|
||||
* variable immediately.
|
||||
* @param {string} name The new variable's name.
|
||||
* @param {string=} opt_type The type of the variable like 'int' or 'string'.
|
||||
* Does not need to be unique. Field_variable can filter variables based on
|
||||
* their type. This will default to '' which is a specific type.
|
||||
* @param {string=} opt_id The unique id of the variable. This will default to
|
||||
* a UUID.
|
||||
* @return {?Blockly.VariableModel} The newly created variable.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.createVariable = function(name, opt_type, opt_id) {
|
||||
var newVar = Blockly.WorkspaceSvg.superClass_.createVariable.call(this, name,
|
||||
opt_type, opt_id);
|
||||
this.refreshToolboxSelection_();
|
||||
return newVar;
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a list of all the delete areas for this workspace.
|
||||
*/
|
||||
@@ -901,7 +1000,6 @@ Blockly.WorkspaceSvg.prototype.recordDeleteAreas = function() {
|
||||
|
||||
/**
|
||||
* Is the mouse event over a delete area (toolbox or non-closing flyout)?
|
||||
* Opens or closes the trashcan and sets the cursor as a side effect.
|
||||
* @param {!Event} e Mouse move event.
|
||||
* @return {?number} Null if not over a delete area, or an enum representing
|
||||
* which delete area the event is over.
|
||||
@@ -914,7 +1012,7 @@ Blockly.WorkspaceSvg.prototype.isDeleteArea = function(e) {
|
||||
if (this.deleteAreaToolbox_ && this.deleteAreaToolbox_.contains(xy)) {
|
||||
return Blockly.DELETE_AREA_TOOLBOX;
|
||||
}
|
||||
return null;
|
||||
return Blockly.DELETE_AREA_NONE;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -923,60 +1021,10 @@ Blockly.WorkspaceSvg.prototype.isDeleteArea = function(e) {
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.onMouseDown_ = function(e) {
|
||||
this.markFocused();
|
||||
if (Blockly.utils.isTargetInput(e)) {
|
||||
Blockly.Touch.clearTouchIdentifier();
|
||||
return;
|
||||
var gesture = this.getGesture(e);
|
||||
if (gesture) {
|
||||
gesture.handleWsStart(e, this);
|
||||
}
|
||||
Blockly.terminateDrag_(); // In case mouse-up event was lost.
|
||||
Blockly.hideChaff();
|
||||
var isTargetWorkspace = e.target && e.target.nodeName &&
|
||||
(e.target.nodeName.toLowerCase() == 'svg' ||
|
||||
e.target == this.svgBackground_);
|
||||
if (isTargetWorkspace && Blockly.selected && !this.options.readOnly) {
|
||||
// Clicking on the document clears the selection.
|
||||
Blockly.selected.unselect();
|
||||
}
|
||||
if (Blockly.utils.isRightButton(e)) {
|
||||
// Right-click.
|
||||
this.showContextMenu_(e);
|
||||
// This is to handle the case where the event is pretending to be a right
|
||||
// click event but it was really a long press. In that case, we want to make
|
||||
// sure any in progress drags are stopped.
|
||||
Blockly.onMouseUp_(e);
|
||||
// Since this was a click, not a drag, end the gesture immediately.
|
||||
Blockly.Touch.clearTouchIdentifier();
|
||||
} else if (this.scrollbar) {
|
||||
this.dragMode_ = Blockly.DRAG_BEGIN;
|
||||
// Record the current mouse position.
|
||||
this.startDragMouseX = e.clientX;
|
||||
this.startDragMouseY = e.clientY;
|
||||
this.startDragMetrics = this.getMetrics();
|
||||
this.startScrollX = this.scrollX;
|
||||
this.startScrollY = this.scrollY;
|
||||
|
||||
this.setupDragSurface();
|
||||
// If this is a touch event then bind to the mouseup so workspace drag mode
|
||||
// is turned off and double move events are not performed on a block.
|
||||
// See comment in inject.js Blockly.init_ as to why mouseup events are
|
||||
// bound to the document instead of the SVG's surface.
|
||||
if ('mouseup' in Blockly.Touch.TOUCH_MAP) {
|
||||
Blockly.Touch.onTouchUpWrapper_ = Blockly.Touch.onTouchUpWrapper_ || [];
|
||||
Blockly.Touch.onTouchUpWrapper_ = Blockly.Touch.onTouchUpWrapper_.concat(
|
||||
Blockly.bindEventWithChecks_(document, 'mouseup', null,
|
||||
Blockly.onMouseUp_));
|
||||
}
|
||||
Blockly.onMouseMoveWrapper_ = Blockly.onMouseMoveWrapper_ || [];
|
||||
Blockly.onMouseMoveWrapper_ = Blockly.onMouseMoveWrapper_.concat(
|
||||
Blockly.bindEventWithChecks_(document, 'mousemove', null,
|
||||
Blockly.onMouseMove_));
|
||||
} else {
|
||||
// It was a click, but the workspace isn't draggable.
|
||||
Blockly.Touch.clearTouchIdentifier();
|
||||
}
|
||||
// This event has been handled. No need to bubble up to the document.
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1013,10 +1061,7 @@ Blockly.WorkspaceSvg.prototype.moveDrag = function(e) {
|
||||
* @return {boolean} True if currently dragging or scrolling.
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.isDragging = function() {
|
||||
return Blockly.dragMode_ == Blockly.DRAG_FREE ||
|
||||
(Blockly.Flyout.startFlyout_ &&
|
||||
Blockly.Flyout.startFlyout_.dragMode_ == Blockly.DRAG_FREE) ||
|
||||
this.dragMode_ == Blockly.DRAG_FREE;
|
||||
return this.currentGesture_ && this.currentGesture_.isDragging();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1033,8 +1078,11 @@ Blockly.WorkspaceSvg.prototype.isDraggable = function() {
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.onMouseWheel_ = function(e) {
|
||||
// TODO: Remove terminateDrag and compensate for coordinate skew during zoom.
|
||||
Blockly.terminateDrag_();
|
||||
// TODO: Remove gesture cancellation and compensate for coordinate skew during
|
||||
// zoom.
|
||||
if (this.currentGesture_) {
|
||||
this.currentGesture_.cancel();
|
||||
}
|
||||
// The vertical scroll distance that corresponds to a click of a zoom button.
|
||||
var PIXELS_PER_ZOOM_STEP = 50;
|
||||
var delta = -e.deltaY / PIXELS_PER_ZOOM_STEP;
|
||||
@@ -1046,6 +1094,7 @@ Blockly.WorkspaceSvg.prototype.onMouseWheel_ = function(e) {
|
||||
|
||||
/**
|
||||
* Calculate the bounding box for the blocks on the workspace.
|
||||
* Coordinate system: workspace coordinates.
|
||||
*
|
||||
* @return {Object} Contains the position and size of the bounding box
|
||||
* containing the blocks on the workspace.
|
||||
@@ -1115,6 +1164,7 @@ Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) {
|
||||
var menuOptions = [];
|
||||
var topBlocks = this.getTopBlocks(true);
|
||||
var eventGroup = Blockly.utils.genUid();
|
||||
var ws = this;
|
||||
|
||||
// Options to undo/redo previous action.
|
||||
var undoOption = {};
|
||||
@@ -1224,6 +1274,9 @@ Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) {
|
||||
Blockly.Msg.DELETE_X_BLOCKS.replace('%1', String(deleteList.length)),
|
||||
enabled: deleteList.length > 0,
|
||||
callback: function() {
|
||||
if (ws.currentGesture_) {
|
||||
ws.currentGesture_.cancel();
|
||||
}
|
||||
if (deleteList.length < 2 ) {
|
||||
deleteNext();
|
||||
} else {
|
||||
@@ -1242,92 +1295,6 @@ Blockly.WorkspaceSvg.prototype.showContextMenu_ = function(e) {
|
||||
Blockly.ContextMenu.show(e, menuOptions, this.RTL);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load an audio file. Cache it, ready for instantaneous playing.
|
||||
* @param {!Array.<string>} filenames List of file types in decreasing order of
|
||||
* preference (i.e. increasing size). E.g. ['media/go.mp3', 'media/go.wav']
|
||||
* Filenames include path from Blockly's root. File extensions matter.
|
||||
* @param {string} name Name of sound.
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.loadAudio_ = function(filenames, name) {
|
||||
if (!filenames.length) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var audioTest = new window['Audio']();
|
||||
} catch (e) {
|
||||
// No browser support for Audio.
|
||||
// IE can throw an error even if the Audio object exists.
|
||||
return;
|
||||
}
|
||||
var sound;
|
||||
for (var i = 0; i < filenames.length; i++) {
|
||||
var filename = filenames[i];
|
||||
var ext = filename.match(/\.(\w+)$/);
|
||||
if (ext && audioTest.canPlayType('audio/' + ext[1])) {
|
||||
// Found an audio format we can play.
|
||||
sound = new window['Audio'](filename);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sound && sound.play) {
|
||||
this.SOUNDS_[name] = sound;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Preload all the audio files so that they play quickly when asked for.
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.preloadAudio_ = function() {
|
||||
for (var name in this.SOUNDS_) {
|
||||
var sound = this.SOUNDS_[name];
|
||||
sound.volume = .01;
|
||||
sound.play();
|
||||
sound.pause();
|
||||
// iOS can only process one sound at a time. Trying to load more than one
|
||||
// corrupts the earlier ones. Just load one and leave the others uncached.
|
||||
if (goog.userAgent.IPAD || goog.userAgent.IPHONE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Play a named sound at specified volume. If volume is not specified,
|
||||
* use full volume (1).
|
||||
* @param {string} name Name of sound.
|
||||
* @param {number=} opt_volume Volume of sound (0-1).
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.playAudio = function(name, opt_volume) {
|
||||
var sound = this.SOUNDS_[name];
|
||||
if (sound) {
|
||||
// Don't play one sound on top of another.
|
||||
var now = new Date;
|
||||
if (now - this.lastSound_ < Blockly.SOUND_LIMIT) {
|
||||
return;
|
||||
}
|
||||
this.lastSound_ = now;
|
||||
var mySound;
|
||||
var ie9 = goog.userAgent.DOCUMENT_MODE &&
|
||||
goog.userAgent.DOCUMENT_MODE === 9;
|
||||
if (ie9 || goog.userAgent.IPAD || goog.userAgent.ANDROID) {
|
||||
// Creating a new audio node causes lag in IE9, Android and iPad. Android
|
||||
// and IE9 refetch the file from the server, iPad uses a singleton audio
|
||||
// node which must be deleted and recreated for each new audio tag.
|
||||
mySound = sound;
|
||||
} else {
|
||||
mySound = sound.cloneNode();
|
||||
}
|
||||
mySound.volume = (opt_volume === undefined ? 1 : opt_volume);
|
||||
mySound.play();
|
||||
} else if (this.options.parentWorkspace) {
|
||||
// Maybe a workspace on a lower level knows about this sound.
|
||||
this.options.parentWorkspace.playAudio(name, opt_volume);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Modify the block tree on the existing toolbox.
|
||||
* @param {Node|string} tree DOM tree of blocks, or text representation of same.
|
||||
@@ -1511,7 +1478,9 @@ Blockly.WorkspaceSvg.prototype.setScale = function(newScale) {
|
||||
newScale = this.options.zoomOptions.minScale;
|
||||
}
|
||||
this.scale = newScale;
|
||||
this.updateGridPattern_();
|
||||
if (this.grid_) {
|
||||
this.grid_.update(this.scale);
|
||||
}
|
||||
if (this.scrollbar) {
|
||||
this.scrollbar.resize();
|
||||
} else {
|
||||
@@ -1524,46 +1493,10 @@ Blockly.WorkspaceSvg.prototype.setScale = function(newScale) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the grid pattern.
|
||||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.updateGridPattern_ = function() {
|
||||
if (!this.options.gridPattern) {
|
||||
return; // No grid.
|
||||
}
|
||||
// MSIE freaks if it sees a 0x0 pattern, so set empty patterns to 100x100.
|
||||
var safeSpacing = (this.options.gridOptions['spacing'] * this.scale) || 100;
|
||||
this.options.gridPattern.setAttribute('width', safeSpacing);
|
||||
this.options.gridPattern.setAttribute('height', safeSpacing);
|
||||
var half = Math.floor(this.options.gridOptions['spacing'] / 2) + 0.5;
|
||||
var start = half - this.options.gridOptions['length'] / 2;
|
||||
var end = half + this.options.gridOptions['length'] / 2;
|
||||
var line1 = this.options.gridPattern.firstChild;
|
||||
var line2 = line1 && line1.nextSibling;
|
||||
half *= this.scale;
|
||||
start *= this.scale;
|
||||
end *= this.scale;
|
||||
if (line1) {
|
||||
line1.setAttribute('stroke-width', this.scale);
|
||||
line1.setAttribute('x1', start);
|
||||
line1.setAttribute('y1', half);
|
||||
line1.setAttribute('x2', end);
|
||||
line1.setAttribute('y2', half);
|
||||
}
|
||||
if (line2) {
|
||||
line2.setAttribute('stroke-width', this.scale);
|
||||
line2.setAttribute('x1', half);
|
||||
line2.setAttribute('y1', start);
|
||||
line2.setAttribute('x2', half);
|
||||
line2.setAttribute('y2', end);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Return an object with all the metrics required to size scrollbars for a
|
||||
* top level workspace. The following properties are computed:
|
||||
* Coordinate system: pixel coordinates.
|
||||
* .viewHeight: Height of the visible rectangle,
|
||||
* .viewWidth: Width of the visible rectangle,
|
||||
* .contentHeight: Height of the contents,
|
||||
@@ -1600,6 +1533,7 @@ Blockly.WorkspaceSvg.getTopLevelWorkspaceMetrics_ = function() {
|
||||
var MARGIN = Blockly.Flyout.prototype.CORNER_RADIUS - 1;
|
||||
var viewWidth = svgSize.width - MARGIN;
|
||||
var viewHeight = svgSize.height - MARGIN;
|
||||
|
||||
var blockBox = this.getBlocksBoundingBox();
|
||||
|
||||
// Fix scale.
|
||||
@@ -1674,14 +1608,8 @@ Blockly.WorkspaceSvg.setTopLevelWorkspaceMetrics_ = function(xyRatio) {
|
||||
var x = this.scrollX + metrics.absoluteLeft;
|
||||
var y = this.scrollY + metrics.absoluteTop;
|
||||
this.translate(x, y);
|
||||
if (this.options.gridPattern) {
|
||||
this.options.gridPattern.setAttribute('x', x);
|
||||
this.options.gridPattern.setAttribute('y', y);
|
||||
if (goog.userAgent.IE || goog.userAgent.EDGE) {
|
||||
// IE/Edge doesn't notice that the x/y offsets have changed.
|
||||
// Force an update.
|
||||
this.updateGridPattern_();
|
||||
}
|
||||
if (this.grid_) {
|
||||
this.grid_.moveTo(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1783,6 +1711,75 @@ Blockly.WorkspaceSvg.prototype.removeToolboxCategoryCallback = function(key) {
|
||||
this.toolboxCategoryCallbacks_[key] = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Look up the gesture that is tracking this touch stream on this workspace.
|
||||
* May create a new gesture.
|
||||
* @param {!Event} e Mouse event or touch event
|
||||
* @return {Blockly.Gesture} The gesture that is tracking this touch stream,
|
||||
* or null if no valid gesture exists.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.getGesture = function(e) {
|
||||
var isStart = (e.type == 'mousedown' || e.type == 'touchstart');
|
||||
|
||||
var gesture = this.currentGesture_;
|
||||
if (gesture) {
|
||||
if (isStart && gesture.hasStarted()) {
|
||||
console.warn('tried to start the same gesture twice');
|
||||
// That's funny. We must have missed a mouse up.
|
||||
// Cancel it, rather than try to retrieve all of the state we need.
|
||||
gesture.cancel();
|
||||
return null;
|
||||
}
|
||||
return gesture;
|
||||
}
|
||||
|
||||
// No gesture existed on this workspace, but this looks like the start of a
|
||||
// new gesture.
|
||||
if (isStart) {
|
||||
this.currentGesture_ = new Blockly.Gesture(e, this);
|
||||
return this.currentGesture_;
|
||||
}
|
||||
// No gesture existed and this event couldn't be the start of a new gesture.
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the reference to the current gesture.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.clearGesture = function() {
|
||||
this.currentGesture_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel the current gesture, if one exists.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.cancelCurrentGesture = function() {
|
||||
if (this.currentGesture_) {
|
||||
this.currentGesture_.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the audio manager for this workspace.
|
||||
* @return {Blockly.WorkspaceAudio} The audio manager for this workspace.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.getAudioManager = function() {
|
||||
return this.audioManager_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the grid object for this workspace, or null if there is none.
|
||||
* @return {Blockly.Grid} The grid object for this workspace.
|
||||
* @package
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.getGrid = function() {
|
||||
return this.grid_;
|
||||
};
|
||||
|
||||
// Export symbols that would otherwise be renamed by Closure compiler.
|
||||
Blockly.WorkspaceSvg.prototype['setVisible'] =
|
||||
Blockly.WorkspaceSvg.prototype.setVisible;
|
||||
|
||||
+103
-23
@@ -42,6 +42,7 @@ goog.require('goog.dom');
|
||||
*/
|
||||
Blockly.Xml.workspaceToDom = function(workspace, opt_noId) {
|
||||
var xml = goog.dom.createDom('xml');
|
||||
xml.appendChild(Blockly.Xml.variablesToDom(workspace.getAllVariables()));
|
||||
var blocks = workspace.getTopBlocks(true);
|
||||
for (var i = 0, block; block = blocks[i]; i++) {
|
||||
xml.appendChild(Blockly.Xml.blockToDomWithXY(block, opt_noId));
|
||||
@@ -49,6 +50,23 @@ Blockly.Xml.workspaceToDom = function(workspace, opt_noId) {
|
||||
return xml;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode a list of variables as XML.
|
||||
* @param {!Array.<!Blockly.VariableModel>} variableList List of all variable
|
||||
* models.
|
||||
* @return {!Element} List of XML elements.
|
||||
*/
|
||||
Blockly.Xml.variablesToDom = function(variableList) {
|
||||
var variables = goog.dom.createDom('variables');
|
||||
for (var i = 0, variable; variable = variableList[i]; i++) {
|
||||
var element = goog.dom.createDom('variable', null, variable.name);
|
||||
element.setAttribute('type', variable.type);
|
||||
element.setAttribute('id', variable.getId());
|
||||
variables.appendChild(element);
|
||||
}
|
||||
return variables;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode a block subtree as XML with XY coordinates.
|
||||
* @param {!Blockly.Block} block The root block to encode.
|
||||
@@ -91,6 +109,13 @@ Blockly.Xml.blockToDom = function(block, opt_noId) {
|
||||
if (field.name && field.EDITABLE) {
|
||||
var container = goog.dom.createDom('field', null, field.getValue());
|
||||
container.setAttribute('name', field.name);
|
||||
if (field instanceof Blockly.FieldVariable) {
|
||||
var variable = block.workspace.getVariable(field.getValue());
|
||||
if (variable) {
|
||||
container.setAttribute('id', variable.getId());
|
||||
container.setAttribute('variableType', variable.type);
|
||||
}
|
||||
}
|
||||
element.appendChild(container);
|
||||
}
|
||||
}
|
||||
@@ -307,31 +332,47 @@ Blockly.Xml.domToWorkspace = function(xml, workspace) {
|
||||
if (workspace.setResizesEnabled) {
|
||||
workspace.setResizesEnabled(false);
|
||||
}
|
||||
for (var i = 0; i < childCount; i++) {
|
||||
var xmlChild = xml.childNodes[i];
|
||||
var name = xmlChild.nodeName.toLowerCase();
|
||||
if (name == 'block' ||
|
||||
(name == 'shadow' && !Blockly.Events.recordUndo)) {
|
||||
// Allow top-level shadow blocks if recordUndo is disabled since
|
||||
// that means an undo is in progress. Such a block is expected
|
||||
// to be moved to a nested destination in the next operation.
|
||||
var block = Blockly.Xml.domToBlock(xmlChild, workspace);
|
||||
newBlockIds.push(block.id);
|
||||
var blockX = parseInt(xmlChild.getAttribute('x'), 10);
|
||||
var blockY = parseInt(xmlChild.getAttribute('y'), 10);
|
||||
if (!isNaN(blockX) && !isNaN(blockY)) {
|
||||
block.moveBy(workspace.RTL ? width - blockX : blockX, blockY);
|
||||
var variablesFirst = true;
|
||||
try {
|
||||
for (var i = 0; i < childCount; i++) {
|
||||
var xmlChild = xml.childNodes[i];
|
||||
var name = xmlChild.nodeName.toLowerCase();
|
||||
if (name == 'block' ||
|
||||
(name == 'shadow' && !Blockly.Events.recordUndo)) {
|
||||
// Allow top-level shadow blocks if recordUndo is disabled since
|
||||
// that means an undo is in progress. Such a block is expected
|
||||
// to be moved to a nested destination in the next operation.
|
||||
var block = Blockly.Xml.domToBlock(xmlChild, workspace);
|
||||
newBlockIds.push(block.id);
|
||||
var blockX = parseInt(xmlChild.getAttribute('x'), 10);
|
||||
var blockY = parseInt(xmlChild.getAttribute('y'), 10);
|
||||
if (!isNaN(blockX) && !isNaN(blockY)) {
|
||||
block.moveBy(workspace.RTL ? width - blockX : blockX, blockY);
|
||||
}
|
||||
variablesFirst = false;
|
||||
} else if (name == 'shadow') {
|
||||
goog.asserts.fail('Shadow block cannot be a top-level block.');
|
||||
variablesFirst = false;
|
||||
} else if (name == 'variables') {
|
||||
if (variablesFirst) {
|
||||
Blockly.Xml.domToVariables(xmlChild, workspace);
|
||||
}
|
||||
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.');
|
||||
}
|
||||
variablesFirst = false;
|
||||
}
|
||||
} else if (name == 'shadow') {
|
||||
goog.asserts.fail('Shadow block cannot be a top-level block.');
|
||||
}
|
||||
}
|
||||
if (!existingGroup) {
|
||||
Blockly.Events.setGroup(false);
|
||||
finally {
|
||||
if (!existingGroup) {
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
Blockly.Field.stopCache();
|
||||
}
|
||||
Blockly.Field.stopCache();
|
||||
|
||||
workspace.updateVariableList(false);
|
||||
workspace.updateVariableStore(false);
|
||||
// Re-enable workspace resizing.
|
||||
if (workspace.setResizesEnabled) {
|
||||
workspace.setResizesEnabled(true);
|
||||
@@ -440,11 +481,30 @@ Blockly.Xml.domToBlock = function(xmlBlock, workspace) {
|
||||
Blockly.Events.enable();
|
||||
}
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.fire(new Blockly.Events.Create(topBlock));
|
||||
Blockly.Events.fire(new Blockly.Events.BlockCreate(topBlock));
|
||||
}
|
||||
return topBlock;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode an XML list of variables and add the variables to the workspace.
|
||||
* @param {!Element} xmlVariables List of XML variable elements.
|
||||
* @param {!Blockly.Workspace} workspace The workspace to which the variable
|
||||
* should be added.
|
||||
*/
|
||||
Blockly.Xml.domToVariables = function(xmlVariables, workspace) {
|
||||
for (var i = 0, xmlChild; xmlChild = xmlVariables.children[i]; i++) {
|
||||
var type = xmlChild.getAttribute('type');
|
||||
var id = xmlChild.getAttribute('id');
|
||||
var name = xmlChild.textContent;
|
||||
|
||||
if (typeof(type) === undefined || type === null) {
|
||||
throw Error('Variable with id, ' + id + ' is without a type');
|
||||
}
|
||||
workspace.createVariable(name, type, id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Decode an XML block tag and create a block (and possibly sub blocks) on the
|
||||
* workspace.
|
||||
@@ -526,12 +586,32 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) {
|
||||
// Fall through.
|
||||
case 'field':
|
||||
var field = block.getField(name);
|
||||
var text = xmlChild.textContent;
|
||||
if (field instanceof Blockly.FieldVariable) {
|
||||
// TODO (marisaleung): When we change setValue and getValue to
|
||||
// interact with id's instead of names, update this so that we get
|
||||
// the variable based on id instead of textContent.
|
||||
var type = xmlChild.getAttribute('variabletype') || '';
|
||||
var variable = workspace.getVariable(text);
|
||||
if (!variable) {
|
||||
variable = workspace.createVariable(text, type,
|
||||
xmlChild.getAttribute(id));
|
||||
}
|
||||
if (typeof(type) !== undefined && type !== null) {
|
||||
if (type !== variable.type) {
|
||||
throw Error('Serialized variable type with id \'' +
|
||||
variable.getId() + '\' had type ' + variable.type + ', and ' +
|
||||
'does not match variable field that references it: ' +
|
||||
Blockly.Xml.domToText(xmlChild) + '.');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!field) {
|
||||
console.warn('Ignoring non-existent field ' + name + ' in block ' +
|
||||
prototypeName);
|
||||
break;
|
||||
}
|
||||
field.setValue(xmlChild.textContent);
|
||||
field.setValue(text);
|
||||
break;
|
||||
case 'value':
|
||||
case 'statement':
|
||||
|
||||
+17
-17
@@ -6,7 +6,7 @@
|
||||
Blockly.Dart=new Blockly.Generator("Dart");Blockly.Dart.addReservedWords("assert,break,case,catch,class,const,continue,default,do,else,enum,extends,false,final,finally,for,if,in,is,new,null,rethrow,return,super,switch,this,throw,true,try,var,void,while,with,print,identityHashCode,identical,BidirectionalIterator,Comparable,double,Function,int,Invocation,Iterable,Iterator,List,Map,Match,num,Pattern,RegExp,Set,StackTrace,String,StringSink,Type,bool,DateTime,Deprecated,Duration,Expando,Null,Object,RuneIterator,Runes,Stopwatch,StringBuffer,Symbol,Uri,Comparator,AbstractClassInstantiationError,ArgumentError,AssertionError,CastError,ConcurrentModificationError,CyclicInitializationError,Error,Exception,FallThroughError,FormatException,IntegerDivisionByZeroException,NoSuchMethodError,NullThrownError,OutOfMemoryError,RangeError,StackOverflowError,StateError,TypeError,UnimplementedError,UnsupportedError");
|
||||
Blockly.Dart.ORDER_ATOMIC=0;Blockly.Dart.ORDER_UNARY_POSTFIX=1;Blockly.Dart.ORDER_UNARY_PREFIX=2;Blockly.Dart.ORDER_MULTIPLICATIVE=3;Blockly.Dart.ORDER_ADDITIVE=4;Blockly.Dart.ORDER_SHIFT=5;Blockly.Dart.ORDER_BITWISE_AND=6;Blockly.Dart.ORDER_BITWISE_XOR=7;Blockly.Dart.ORDER_BITWISE_OR=8;Blockly.Dart.ORDER_RELATIONAL=9;Blockly.Dart.ORDER_EQUALITY=10;Blockly.Dart.ORDER_LOGICAL_AND=11;Blockly.Dart.ORDER_LOGICAL_OR=12;Blockly.Dart.ORDER_IF_NULL=13;Blockly.Dart.ORDER_CONDITIONAL=14;
|
||||
Blockly.Dart.ORDER_CASCADE=15;Blockly.Dart.ORDER_ASSIGNMENT=16;Blockly.Dart.ORDER_NONE=99;
|
||||
Blockly.Dart.init=function(a){Blockly.Dart.definitions_=Object.create(null);Blockly.Dart.functionNames_=Object.create(null);Blockly.Dart.variableDB_?Blockly.Dart.variableDB_.reset():Blockly.Dart.variableDB_=new Blockly.Names(Blockly.Dart.RESERVED_WORDS_);var b=[];a=a.variableList;if(a.length){for(var c=0;c<a.length;c++)b[c]=Blockly.Dart.variableDB_.getName(a[c],Blockly.Variables.NAME_TYPE);Blockly.Dart.definitions_.variables="var "+b.join(", ")+";"}};
|
||||
Blockly.Dart.init=function(a){Blockly.Dart.definitions_=Object.create(null);Blockly.Dart.functionNames_=Object.create(null);Blockly.Dart.variableDB_?Blockly.Dart.variableDB_.reset():Blockly.Dart.variableDB_=new Blockly.Names(Blockly.Dart.RESERVED_WORDS_);var b=[];a=a.getAllVariables();if(a.length){for(var c=0;c<a.length;c++)b[c]=Blockly.Dart.variableDB_.getName(a[c].name,Blockly.Variables.NAME_TYPE);Blockly.Dart.definitions_.variables="var "+b.join(", ")+";"}};
|
||||
Blockly.Dart.finish=function(a){a&&(a=Blockly.Dart.prefixLines(a,Blockly.Dart.INDENT));a="main() {\n"+a+"}";var b=[],c=[],d;for(d in Blockly.Dart.definitions_){var e=Blockly.Dart.definitions_[d];e.match(/^import\s/)?b.push(e):c.push(e)}delete Blockly.Dart.definitions_;delete Blockly.Dart.functionNames_;Blockly.Dart.variableDB_.reset();return(b.join("\n")+"\n\n"+c.join("\n\n")).replace(/\n\n+/g,"\n\n").replace(/\n*$/,"\n\n\n")+a};Blockly.Dart.scrubNakedValue=function(a){return a+";\n"};
|
||||
Blockly.Dart.quote_=function(a){a=a.replace(/\\/g,"\\\\").replace(/\n/g,"\\\n").replace(/\$/g,"\\$").replace(/'/g,"\\'");return"'"+a+"'"};
|
||||
Blockly.Dart.scrub_=function(a,b){var c="";if(!a.outputConnection||!a.outputConnection.targetConnection){var d=a.getCommentText();(d=Blockly.utils.wrap(d,Blockly.Dart.COMMENT_WRAP-3))&&(c=a.getProcedureDef?c+Blockly.Dart.prefixLines(d+"\n","/// "):c+Blockly.Dart.prefixLines(d+"\n","// "));for(var e=0;e<a.inputList.length;e++)a.inputList[e].type==Blockly.INPUT_VALUE&&(d=a.inputList[e].connection.targetBlock())&&(d=Blockly.Dart.allNestedComments(d))&&(c+=Blockly.Dart.prefixLines(d,"// "))}e=a.nextConnection&&
|
||||
@@ -15,7 +15,7 @@ Blockly.Dart.getAdjusted=function(a,b,c,d,e){c=c||0;e=e||Blockly.Dart.ORDER_NONE
|
||||
d&&(a=c?"-("+a+")":"-"+a,g=Blockly.Dart.ORDER_UNARY_PREFIX);g=Math.floor(g);e=Math.floor(e);g&&e>=g&&(a="("+a+")")}return a};Blockly.Dart.lists={};Blockly.Dart.addReservedWords("Math");Blockly.Dart.lists_create_empty=function(a){return["[]",Blockly.Dart.ORDER_ATOMIC]};Blockly.Dart.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c<a.itemCount_;c++)b[c]=Blockly.Dart.valueToCode(a,"ADD"+c,Blockly.Dart.ORDER_NONE)||"null";return["["+b.join(", ")+"]",Blockly.Dart.ORDER_ATOMIC]};
|
||||
Blockly.Dart.lists_repeat=function(a){var b=Blockly.Dart.valueToCode(a,"ITEM",Blockly.Dart.ORDER_NONE)||"null";return["new List.filled("+(Blockly.Dart.valueToCode(a,"NUM",Blockly.Dart.ORDER_NONE)||"0")+", "+b+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.lists_length=function(a){return[(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]")+".length",Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.lists_isEmpty=function(a){return[(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]")+".isEmpty",Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.lists_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.Dart.valueToCode(a,"FIND",Blockly.Dart.ORDER_NONE)||"''",b=(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.Dart.ORDER_ADDITIVE]:[b,Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.lists_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.Dart.valueToCode(a,"FIND",Blockly.Dart.ORDER_NONE)||"''";b=(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.Dart.ORDER_ADDITIVE]:[b,Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.lists_getIndex=function(a){function b(){var a=Blockly.Dart.variableDB_.getDistinctName("tmp_list",Blockly.Variables.NAME_TYPE),b="List "+a+" = "+e+";\n";e=a;return b}var c=a.getFieldValue("MODE")||"GET";var d=a.getFieldValue("WHERE")||"FROM_START";var e=Blockly.Dart.valueToCode(a,"VALUE","RANDOM"==d||"FROM_END"==d?Blockly.Dart.ORDER_NONE:Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]";if(("RANDOM"!=d||"REMOVE"!=c)&&"FROM_END"!=d||e.match(/^\w+$/))switch(d){case "FIRST":if("GET"==c)return[e+".first",
|
||||
Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return[e+".removeAt(0)",Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return e+".removeAt(0);\n";break;case "LAST":if("GET"==c)return[e+".last",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return[e+".removeLast()",Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return e+".removeLast();\n";break;case "FROM_START":d=Blockly.Dart.getAdjusted(a,"AT");if("GET"==c)return[e+"["+d+"]",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c)return[e+
|
||||
".removeAt("+d+")",Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return e+".removeAt("+d+");\n";break;case "FROM_END":d=Blockly.Dart.getAdjusted(a,"AT",1,!1,Blockly.Dart.ORDER_ADDITIVE);if("GET"==c)return[e+"["+e+".length - "+d+"]",Blockly.Dart.ORDER_UNARY_POSTFIX];if("GET_REMOVE"==c||"REMOVE"==c){a=e+".removeAt("+e+".length - "+d+")";if("GET_REMOVE"==c)return[a,Blockly.Dart.ORDER_UNARY_POSTFIX];if("REMOVE"==c)return a+";\n"}break;case "RANDOM":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";
|
||||
@@ -27,14 +27,14 @@ Blockly.Dart.lists_setIndex=function(a){function b(){if(e.match(/^\w+$/))return"
|
||||
c)return e+".insert(0, "+f+");\n";break;case "LAST":if("SET"==c)return a=b(),a+(e+"["+e+".length - 1] = "+f+";\n");if("INSERT"==c)return e+".add("+f+");\n";break;case "FROM_START":d=Blockly.Dart.getAdjusted(a,"AT");if("SET"==c)return e+"["+d+"] = "+f+";\n";if("INSERT"==c)return e+".insert("+d+", "+f+");\n";break;case "FROM_END":d=Blockly.Dart.getAdjusted(a,"AT",1,!1,Blockly.Dart.ORDER_ADDITIVE);a=b();if("SET"==c)return a+(e+"["+e+".length - "+d+"] = "+f+";\n");if("INSERT"==c)return a+(e+".insert("+
|
||||
e+".length - "+d+", "+f+");\n");break;case "RANDOM":Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";a=b();d=Blockly.Dart.variableDB_.getDistinctName("tmp_x",Blockly.Variables.NAME_TYPE);a+="int "+d+" = new Math.Random().nextInt("+e+".length);\n";if("SET"==c)return a+(e+"["+d+"] = "+f+";\n");if("INSERT"==c)return a+(e+".insert("+d+", "+f+");\n")}throw"Unhandled combination (lists_setIndex).";};
|
||||
Blockly.Dart.lists_getSublist=function(a){var b=Blockly.Dart.valueToCode(a,"LIST",Blockly.Dart.ORDER_UNARY_POSTFIX)||"[]",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");if(b.match(/^\w+$/)||"FROM_END"!=c&&"FROM_START"==d){switch(c){case "FROM_START":var e=Blockly.Dart.getAdjusted(a,"AT1");break;case "FROM_END":e=Blockly.Dart.getAdjusted(a,"AT1",1,!1,Blockly.Dart.ORDER_ADDITIVE);e=b+".length - "+e;break;case "FIRST":e="0";break;default:throw"Unhandled option (lists_getSublist).";}switch(d){case "FROM_START":var f=
|
||||
Blockly.Dart.getAdjusted(a,"AT2",1);break;case "FROM_END":f=Blockly.Dart.getAdjusted(a,"AT2",0,!1,Blockly.Dart.ORDER_ADDITIVE);f=b+".length - "+f;break;case "LAST":break;default:throw"Unhandled option (lists_getSublist).";}f="LAST"==d?b+".sublist("+e+")":b+".sublist("+e+", "+f+")"}else e=Blockly.Dart.getAdjusted(a,"AT1"),f=Blockly.Dart.getAdjusted(a,"AT2"),a=Blockly.Dart.provideFunction_("lists_get_sublist",["List "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(list, where1, at1, where2, at2) {"," int getAt(where, at) {",
|
||||
" if (where == 'FROM_END') {"," at = list.length - 1 - at;"," } else if (where == 'FIRST') {"," at = 0;"," } else if (where == 'LAST') {"," at = list.length - 1;"," } else if (where != 'FROM_START') {"," throw 'Unhandled option (lists_getSublist).';"," }"," return at;"," }"," at1 = getAt(where1, at1);"," at2 = getAt(where2, at2) + 1;"," return list.sublist(at1, at2);","}"]),f=a+"("+b+", '"+c+"', "+e+", '"+d+"', "+f+")";return[f,Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.getAdjusted(a,"AT2",1);break;case "FROM_END":f=Blockly.Dart.getAdjusted(a,"AT2",0,!1,Blockly.Dart.ORDER_ADDITIVE);f=b+".length - "+f;break;case "LAST":break;default:throw"Unhandled option (lists_getSublist).";}a="LAST"==d?b+".sublist("+e+")":b+".sublist("+e+", "+f+")"}else e=Blockly.Dart.getAdjusted(a,"AT1"),f=Blockly.Dart.getAdjusted(a,"AT2"),a=Blockly.Dart.provideFunction_("lists_get_sublist",["List "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(list, where1, at1, where2, at2) {"," int getAt(where, at) {",
|
||||
" if (where == 'FROM_END') {"," at = list.length - 1 - at;"," } else if (where == 'FIRST') {"," at = 0;"," } else if (where == 'LAST') {"," at = list.length - 1;"," } else if (where != 'FROM_START') {"," throw 'Unhandled option (lists_getSublist).';"," }"," return at;"," }"," at1 = getAt(where1, at1);"," at2 = getAt(where2, at2) + 1;"," return list.sublist(at1, at2);","}"])+"("+b+", '"+c+"', "+e+", '"+d+"', "+f+")";return[a,Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.lists_sort=function(a){var b=Blockly.Dart.valueToCode(a,"LIST",Blockly.Dart.ORDER_NONE)||"[]",c="1"===a.getFieldValue("DIRECTION")?1:-1;a=a.getFieldValue("TYPE");return[Blockly.Dart.provideFunction_("lists_sort",["List "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(list, type, direction) {"," var compareFuncs = {",' "NUMERIC": (a, b) => direction * a.compareTo(b),',' "TEXT": (a, b) => direction * a.toString().compareTo(b.toString()),',' "IGNORE_CASE": '," (a, b) => direction * ",
|
||||
" a.toString().toLowerCase().compareTo(b.toString().toLowerCase())"," };"," list = new List.from(list);"," var compare = compareFuncs[type];"," list.sort(compare);"," return list;","}"])+"("+b+', "'+a+'", '+c+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.lists_split=function(a){var b=Blockly.Dart.valueToCode(a,"INPUT",Blockly.Dart.ORDER_UNARY_POSTFIX),c=Blockly.Dart.valueToCode(a,"DELIM",Blockly.Dart.ORDER_NONE)||"''";a=a.getFieldValue("MODE");if("SPLIT"==a)b||(b="''"),a="split";else if("JOIN"==a)b||(b="[]"),a="join";else throw"Unknown mode: "+a;return[b+"."+a+"("+c+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.lists_reverse=function(a){return["new List.from("+(Blockly.Dart.valueToCode(a,"LIST",Blockly.Dart.ORDER_NONE)||"[]")+".reversed)",Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.math={};Blockly.Dart.addReservedWords("Math");Blockly.Dart.math_number=function(a){a=parseFloat(a.getFieldValue("NUM"));if(Infinity==a){a="double.INFINITY";var b=Blockly.Dart.ORDER_UNARY_POSTFIX}else-Infinity==a?(a="-double.INFINITY",b=Blockly.Dart.ORDER_UNARY_PREFIX):b=0>a?Blockly.Dart.ORDER_UNARY_PREFIX:Blockly.Dart.ORDER_ATOMIC;return[a,b]};
|
||||
Blockly.Dart.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.Dart.ORDER_ADDITIVE],MINUS:[" - ",Blockly.Dart.ORDER_ADDITIVE],MULTIPLY:[" * ",Blockly.Dart.ORDER_MULTIPLICATIVE],DIVIDE:[" / ",Blockly.Dart.ORDER_MULTIPLICATIVE],POWER:[null,Blockly.Dart.ORDER_NONE]}[a.getFieldValue("OP")],c=b[0],b=b[1],d=Blockly.Dart.valueToCode(a,"A",b)||"0";a=Blockly.Dart.valueToCode(a,"B",b)||"0";return c?[d+c+a,b]:(Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;",["Math.pow("+d+", "+a+
|
||||
")",Blockly.Dart.ORDER_UNARY_POSTFIX])};
|
||||
Blockly.Dart.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.Dart.ORDER_ADDITIVE],MINUS:[" - ",Blockly.Dart.ORDER_ADDITIVE],MULTIPLY:[" * ",Blockly.Dart.ORDER_MULTIPLICATIVE],DIVIDE:[" / ",Blockly.Dart.ORDER_MULTIPLICATIVE],POWER:[null,Blockly.Dart.ORDER_NONE]}[a.getFieldValue("OP")],c=b[0];b=b[1];var d=Blockly.Dart.valueToCode(a,"A",b)||"0";a=Blockly.Dart.valueToCode(a,"B",b)||"0";return c?[d+c+a,b]:(Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;",["Math.pow("+d+", "+
|
||||
a+")",Blockly.Dart.ORDER_UNARY_POSTFIX])};
|
||||
Blockly.Dart.math_single=function(a){var b=a.getFieldValue("OP");if("NEG"==b)return a=Blockly.Dart.valueToCode(a,"NUM",Blockly.Dart.ORDER_UNARY_PREFIX)||"0","-"==a[0]&&(a=" "+a),["-"+a,Blockly.Dart.ORDER_UNARY_PREFIX];Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;";a="ABS"==b||"ROUND"==b.substring(0,5)?Blockly.Dart.valueToCode(a,"NUM",Blockly.Dart.ORDER_UNARY_POSTFIX)||"0":"SIN"==b||"COS"==b||"TAN"==b?Blockly.Dart.valueToCode(a,"NUM",Blockly.Dart.ORDER_MULTIPLICATIVE)||"0":
|
||||
Blockly.Dart.valueToCode(a,"NUM",Blockly.Dart.ORDER_NONE)||"0";switch(b){case "ABS":var c=a+".abs()";break;case "ROOT":c="Math.sqrt("+a+")";break;case "LN":c="Math.log("+a+")";break;case "EXP":c="Math.exp("+a+")";break;case "POW10":c="Math.pow(10,"+a+")";break;case "ROUND":c=a+".round()";break;case "ROUNDUP":c=a+".ceil()";break;case "ROUNDDOWN":c=a+".floor()";break;case "SIN":c="Math.sin("+a+" / 180 * Math.PI)";break;case "COS":c="Math.cos("+a+" / 180 * Math.PI)";break;case "TAN":c="Math.tan("+a+
|
||||
" / 180 * Math.PI)"}if(c)return[c,Blockly.Dart.ORDER_UNARY_POSTFIX];switch(b){case "LOG10":c="Math.log("+a+") / Math.log(10)";break;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"Unknown math operator: "+b;}return[c,Blockly.Dart.ORDER_MULTIPLICATIVE]};
|
||||
@@ -70,26 +70,26 @@ Blockly.Dart.procedures_ifreturn=function(a){var b="if ("+(Blockly.Dart.valueToC
|
||||
Blockly.Dart.text_join=function(a){switch(a.itemCount_){case 0:return["''",Blockly.Dart.ORDER_ATOMIC];case 1:return[(Blockly.Dart.valueToCode(a,"ADD0",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+".toString()",Blockly.Dart.ORDER_UNARY_POSTFIX];default:for(var b=Array(a.itemCount_),c=0;c<a.itemCount_;c++)b[c]=Blockly.Dart.valueToCode(a,"ADD"+c,Blockly.Dart.ORDER_NONE)||"''";a="["+b.join(",")+"].join()";return[a,Blockly.Dart.ORDER_UNARY_POSTFIX]}};
|
||||
Blockly.Dart.text_append=function(a){var b=Blockly.Dart.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);a=Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_NONE)||"''";return b+" = ["+b+", "+a+"].join();\n"};Blockly.Dart.text_length=function(a){return[(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+".length",Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.text_isEmpty=function(a){return[(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+".isEmpty",Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.text_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.Dart.valueToCode(a,"FIND",Blockly.Dart.ORDER_NONE)||"''",b=(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.Dart.ORDER_ADDITIVE]:[b,Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.text_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.Dart.valueToCode(a,"FIND",Blockly.Dart.ORDER_NONE)||"''";b=(Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.Dart.ORDER_ADDITIVE]:[b,Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.text_charAt=function(a){var b=a.getFieldValue("WHERE")||"FROM_START",c=Blockly.Dart.valueToCode(a,"VALUE",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''";switch(b){case "FIRST":return[c+"[0]",Blockly.Dart.ORDER_UNARY_POSTFIX];case "FROM_START":return a=Blockly.Dart.getAdjusted(a,"AT"),[c+"["+a+"]",Blockly.Dart.ORDER_UNARY_POSTFIX];case "LAST":case "FROM_END":return a=Blockly.Dart.getAdjusted(a,"AT",1),b=Blockly.Dart.provideFunction_("text_get_from_end",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+
|
||||
"(String text, num x) {"," return text[text.length - x];","}"]),[b+"("+c+", "+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX];case "RANDOM":return Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;",b=Blockly.Dart.provideFunction_("text_random_letter",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(String text) {"," int x = new Math.Random().nextInt(text.length);"," return text[x];","}"]),[b+"("+c+")",Blockly.Dart.ORDER_UNARY_POSTFIX]}throw"Unhandled option (text_charAt).";};
|
||||
Blockly.Dart.text_getSubstring=function(a){var b=Blockly.Dart.valueToCode(a,"STRING",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");if("FIRST"==c&&"LAST"==d)var e=b;else if(b.match(/^'?\w+'?$/)||"FROM_END"!=c&&"FROM_START"==d){switch(c){case "FROM_START":var f=Blockly.Dart.getAdjusted(a,"AT1");break;case "FROM_END":f=Blockly.Dart.getAdjusted(a,"AT1",1,!1,Blockly.Dart.ORDER_ADDITIVE);f=b+".length - "+f;break;case "FIRST":f="0";break;default:throw"Unhandled option (text_getSubstring).";
|
||||
}switch(d){case "FROM_START":e=Blockly.Dart.getAdjusted(a,"AT2",1);break;case "FROM_END":e=Blockly.Dart.getAdjusted(a,"AT2",0,!1,Blockly.Dart.ORDER_ADDITIVE);e=b+".length - "+e;break;case "LAST":break;default:throw"Unhandled option (text_getSubstring).";}e="LAST"==d?b+".substring("+f+")":b+".substring("+f+", "+e+")"}else f=Blockly.Dart.getAdjusted(a,"AT1"),e=Blockly.Dart.getAdjusted(a,"AT2"),a=Blockly.Dart.provideFunction_("text_get_substring",["List "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(text, where1, at1, where2, at2) {",
|
||||
" int getAt(where, at) {"," if (where == 'FROM_END') {"," at = text.length - 1 - at;"," } else if (where == 'FIRST') {"," at = 0;"," } else if (where == 'LAST') {"," at = text.length - 1;"," } else if (where != 'FROM_START') {"," throw 'Unhandled option (text_getSubstring).';"," }"," return at;"," }"," at1 = getAt(where1, at1);"," at2 = getAt(where2, at2) + 1;"," return text.substring(at1, at2);","}"]),e=a+"("+b+", '"+c+"', "+f+", '"+d+"', "+e+")";return[e,
|
||||
Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.text_changeCase=function(a){var b={UPPERCASE:".toUpperCase()",LOWERCASE:".toLowerCase()",TITLECASE:null}[a.getFieldValue("CASE")];a=Blockly.Dart.valueToCode(a,"TEXT",b?Blockly.Dart.ORDER_UNARY_POSTFIX:Blockly.Dart.ORDER_NONE)||"''";b?b=a+b:(b=Blockly.Dart.provideFunction_("text_toTitleCase",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(String str) {"," RegExp exp = new RegExp(r'\\b');"," List<String> list = str.split(exp);"," final title = new StringBuffer();"," for (String part in list) {",
|
||||
" if (part.length > 0) {"," title.write(part[0].toUpperCase());"," if (part.length > 0) {"," title.write(part.substring(1).toLowerCase());"," }"," }"," }"," return title.toString();","}"]),b=b+"("+a+")");return[b,Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.text_getSubstring=function(a){var b=Blockly.Dart.valueToCode(a,"STRING",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");if("FIRST"==c&&"LAST"==d)a=b;else if(b.match(/^'?\w+'?$/)||"FROM_END"!=c&&"FROM_START"==d){switch(c){case "FROM_START":var e=Blockly.Dart.getAdjusted(a,"AT1");break;case "FROM_END":e=Blockly.Dart.getAdjusted(a,"AT1",1,!1,Blockly.Dart.ORDER_ADDITIVE);e=b+".length - "+e;break;case "FIRST":e="0";break;default:throw"Unhandled option (text_getSubstring).";
|
||||
}switch(d){case "FROM_START":var f=Blockly.Dart.getAdjusted(a,"AT2",1);break;case "FROM_END":f=Blockly.Dart.getAdjusted(a,"AT2",0,!1,Blockly.Dart.ORDER_ADDITIVE);f=b+".length - "+f;break;case "LAST":break;default:throw"Unhandled option (text_getSubstring).";}a="LAST"==d?b+".substring("+e+")":b+".substring("+e+", "+f+")"}else e=Blockly.Dart.getAdjusted(a,"AT1"),f=Blockly.Dart.getAdjusted(a,"AT2"),a=Blockly.Dart.provideFunction_("text_get_substring",["List "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+
|
||||
"(text, where1, at1, where2, at2) {"," int getAt(where, at) {"," if (where == 'FROM_END') {"," at = text.length - 1 - at;"," } else if (where == 'FIRST') {"," at = 0;"," } else if (where == 'LAST') {"," at = text.length - 1;"," } else if (where != 'FROM_START') {"," throw 'Unhandled option (text_getSubstring).';"," }"," return at;"," }"," at1 = getAt(where1, at1);"," at2 = getAt(where2, at2) + 1;"," return text.substring(at1, at2);","}"])+"("+b+", '"+c+"', "+
|
||||
e+", '"+d+"', "+f+")";return[a,Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.text_changeCase=function(a){var b={UPPERCASE:".toUpperCase()",LOWERCASE:".toLowerCase()",TITLECASE:null}[a.getFieldValue("CASE")];a=Blockly.Dart.valueToCode(a,"TEXT",b?Blockly.Dart.ORDER_UNARY_POSTFIX:Blockly.Dart.ORDER_NONE)||"''";return[b?a+b:Blockly.Dart.provideFunction_("text_toTitleCase",["String "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(String str) {"," RegExp exp = new RegExp(r'\\b');"," List<String> list = str.split(exp);"," final title = new StringBuffer();"," for (String part in list) {",
|
||||
" if (part.length > 0) {"," title.write(part[0].toUpperCase());"," if (part.length > 0) {"," title.write(part.substring(1).toLowerCase());"," }"," }"," }"," return title.toString();","}"])+"("+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.text_trim=function(a){var b={LEFT:".replaceFirst(new RegExp(r'^\\s+'), '')",RIGHT:".replaceFirst(new RegExp(r'\\s+$'), '')",BOTH:".trim()"}[a.getFieldValue("MODE")];return[(Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+b,Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.text_print=function(a){return"print("+(Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_NONE)||"''")+");\n"};
|
||||
Blockly.Dart.text_prompt_ext=function(a){Blockly.Dart.definitions_.import_dart_html="import 'dart:html' as Html;";var b="Html.window.prompt("+(a.getField("TEXT")?Blockly.Dart.quote_(a.getFieldValue("TEXT")):Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_NONE)||"''")+", '')";"NUMBER"==a.getFieldValue("TYPE")&&(Blockly.Dart.definitions_.import_dart_math="import 'dart:math' as Math;",b="Math.parseDouble("+b+")");return[b,Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.text_prompt=Blockly.Dart.text_prompt_ext;
|
||||
Blockly.Dart.text_count=function(a){var b=Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''";a=Blockly.Dart.valueToCode(a,"SUB",Blockly.Dart.ORDER_NONE)||"''";return[Blockly.Dart.provideFunction_("text_count",["int "+Blockly.Dart.FUNCTION_NAME_PLACEHOLDER_+"(String haystack, String needle) {"," if (needle.length == 0) {"," return haystack.length + 1;"," }"," int index = 0;"," int count = 0;"," while (index != -1) {"," index = haystack.indexOf(needle, index);"," if (index != -1) {",
|
||||
" count++;"," index += needle.length;"," }"," }"," return count;","}"])+"("+b+", "+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.text_replace=function(a){var b=Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''",c=Blockly.Dart.valueToCode(a,"FROM",Blockly.Dart.ORDER_NONE)||"''";a=Blockly.Dart.valueToCode(a,"TO",Blockly.Dart.ORDER_NONE)||"''";return[b+".replaceAll("+c+", "+a+")",Blockly.Dart.ORDER_UNARY_POSTFIX]};
|
||||
Blockly.Dart.text_reverse=function(a){return["new String.fromCharCodes("+(Blockly.Dart.valueToCode(a,"TEXT",Blockly.Dart.ORDER_UNARY_POSTFIX)||"''")+".runes.toList().reversed)",Blockly.Dart.ORDER_UNARY_POSTFIX]};Blockly.Dart.loops={};
|
||||
Blockly.Dart.controls_repeat_ext=function(a){var b=a.getField("TIMES")?String(Number(a.getFieldValue("TIMES"))):Blockly.Dart.valueToCode(a,"TIMES",Blockly.Dart.ORDER_ASSIGNMENT)||"0";var c=Blockly.Dart.statementToCode(a,"DO"),c=Blockly.Dart.addLoopTrap(c,a.id),d="",e=Blockly.Dart.variableDB_.getDistinctName("count",Blockly.Variables.NAME_TYPE);a=b;b.match(/^\w+$/)||Blockly.isNumber(b)||(a=Blockly.Dart.variableDB_.getDistinctName("repeat_end",Blockly.Variables.NAME_TYPE),d+="var "+a+" = "+b+";\n");
|
||||
return d+("for (int "+e+" = 0; "+e+" < "+a+"; "+e+"++) {\n"+c+"}\n")};Blockly.Dart.controls_repeat=Blockly.Dart.controls_repeat_ext;Blockly.Dart.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.Dart.valueToCode(a,"BOOL",b?Blockly.Dart.ORDER_UNARY_PREFIX:Blockly.Dart.ORDER_NONE)||"false",d=Blockly.Dart.statementToCode(a,"DO"),d=Blockly.Dart.addLoopTrap(d,a.id);b&&(c="!"+c);return"while ("+c+") {\n"+d+"}\n"};
|
||||
Blockly.Dart.controls_for=function(a){var b=Blockly.Dart.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);var c=Blockly.Dart.valueToCode(a,"FROM",Blockly.Dart.ORDER_ASSIGNMENT)||"0";var d=Blockly.Dart.valueToCode(a,"TO",Blockly.Dart.ORDER_ASSIGNMENT)||"0",e=Blockly.Dart.valueToCode(a,"BY",Blockly.Dart.ORDER_ASSIGNMENT)||"1",f=Blockly.Dart.statementToCode(a,"DO"),f=Blockly.Dart.addLoopTrap(f,a.id);if(Blockly.isNumber(c)&&Blockly.isNumber(d)&&Blockly.isNumber(e)){var g=parseFloat(c)<=
|
||||
Blockly.Dart.controls_repeat_ext=function(a){var b=a.getField("TIMES")?String(Number(a.getFieldValue("TIMES"))):Blockly.Dart.valueToCode(a,"TIMES",Blockly.Dart.ORDER_ASSIGNMENT)||"0";var c=Blockly.Dart.statementToCode(a,"DO");c=Blockly.Dart.addLoopTrap(c,a.id);var d="",e=Blockly.Dart.variableDB_.getDistinctName("count",Blockly.Variables.NAME_TYPE);a=b;b.match(/^\w+$/)||Blockly.isNumber(b)||(a=Blockly.Dart.variableDB_.getDistinctName("repeat_end",Blockly.Variables.NAME_TYPE),d+="var "+a+" = "+b+";\n");
|
||||
return d+("for (int "+e+" = 0; "+e+" < "+a+"; "+e+"++) {\n"+c+"}\n")};Blockly.Dart.controls_repeat=Blockly.Dart.controls_repeat_ext;Blockly.Dart.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.Dart.valueToCode(a,"BOOL",b?Blockly.Dart.ORDER_UNARY_PREFIX:Blockly.Dart.ORDER_NONE)||"false",d=Blockly.Dart.statementToCode(a,"DO");d=Blockly.Dart.addLoopTrap(d,a.id);b&&(c="!"+c);return"while ("+c+") {\n"+d+"}\n"};
|
||||
Blockly.Dart.controls_for=function(a){var b=Blockly.Dart.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);var c=Blockly.Dart.valueToCode(a,"FROM",Blockly.Dart.ORDER_ASSIGNMENT)||"0";var d=Blockly.Dart.valueToCode(a,"TO",Blockly.Dart.ORDER_ASSIGNMENT)||"0",e=Blockly.Dart.valueToCode(a,"BY",Blockly.Dart.ORDER_ASSIGNMENT)||"1",f=Blockly.Dart.statementToCode(a,"DO");f=Blockly.Dart.addLoopTrap(f,a.id);if(Blockly.isNumber(c)&&Blockly.isNumber(d)&&Blockly.isNumber(e)){var g=parseFloat(c)<=
|
||||
parseFloat(d);a="for ("+b+" = "+c+"; "+b+(g?" <= ":" >= ")+d+"; "+b;b=Math.abs(parseFloat(e));a=(1==b?a+(g?"++":"--"):a+((g?" += ":" -= ")+b))+(") {\n"+f+"}\n")}else a="",g=c,c.match(/^\w+$/)||Blockly.isNumber(c)||(g=Blockly.Dart.variableDB_.getDistinctName(b+"_start",Blockly.Variables.NAME_TYPE),a+="var "+g+" = "+c+";\n"),c=d,d.match(/^\w+$/)||Blockly.isNumber(d)||(c=Blockly.Dart.variableDB_.getDistinctName(b+"_end",Blockly.Variables.NAME_TYPE),a+="var "+c+" = "+d+";\n"),d=Blockly.Dart.variableDB_.getDistinctName(b+
|
||||
"_inc",Blockly.Variables.NAME_TYPE),a+="num "+d+" = ",a=Blockly.isNumber(e)?a+(Math.abs(e)+";\n"):a+("("+e+").abs();\n"),a=a+("if ("+g+" > "+c+") {\n")+(Blockly.Dart.INDENT+d+" = -"+d+";\n"),a+="}\n",a+="for ("+b+" = "+g+"; "+d+" >= 0 ? "+b+" <= "+c+" : "+b+" >= "+c+"; "+b+" += "+d+") {\n"+f+"}\n";return a};
|
||||
Blockly.Dart.controls_forEach=function(a){var b=Blockly.Dart.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),c=Blockly.Dart.valueToCode(a,"LIST",Blockly.Dart.ORDER_ASSIGNMENT)||"[]",d=Blockly.Dart.statementToCode(a,"DO"),d=Blockly.Dart.addLoopTrap(d,a.id);return"for (var "+b+" in "+c+") {\n"+d+"}\n"};
|
||||
Blockly.Dart.controls_forEach=function(a){var b=Blockly.Dart.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),c=Blockly.Dart.valueToCode(a,"LIST",Blockly.Dart.ORDER_ASSIGNMENT)||"[]",d=Blockly.Dart.statementToCode(a,"DO");d=Blockly.Dart.addLoopTrap(d,a.id);return"for (var "+b+" in "+c+") {\n"+d+"}\n"};
|
||||
Blockly.Dart.controls_flow_statements=function(a){switch(a.getFieldValue("FLOW")){case "BREAK":return"break;\n";case "CONTINUE":return"continue;\n"}throw"Unknown flow statement.";};Blockly.Dart.logic={};Blockly.Dart.controls_if=function(a){var b=0,c="";do{var d=Blockly.Dart.valueToCode(a,"IF"+b,Blockly.Dart.ORDER_NONE)||"false";var e=Blockly.Dart.statementToCode(a,"DO"+b);c+=(0<b?"else ":"")+"if ("+d+") {\n"+e+"}";++b}while(a.getInput("IF"+b));a.getInput("ELSE")&&(e=Blockly.Dart.statementToCode(a,"ELSE"),c+=" else {\n"+e+"}");return c+"\n"};Blockly.Dart.controls_ifelse=Blockly.Dart.controls_if;
|
||||
Blockly.Dart.logic_compare=function(a){var b={EQ:"==",NEQ:"!=",LT:"<",LTE:"<=",GT:">",GTE:">="}[a.getFieldValue("OP")],c="=="==b||"!="==b?Blockly.Dart.ORDER_EQUALITY:Blockly.Dart.ORDER_RELATIONAL,d=Blockly.Dart.valueToCode(a,"A",c)||"0";a=Blockly.Dart.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]};
|
||||
Blockly.Dart.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?"&&":"||",c="&&"==b?Blockly.Dart.ORDER_LOGICAL_AND:Blockly.Dart.ORDER_LOGICAL_OR,d=Blockly.Dart.valueToCode(a,"A",c);a=Blockly.Dart.valueToCode(a,"B",c);if(d||a){var e="&&"==b?"true":"false";d||(d=e);a||(a=e)}else a=d="false";return[d+" "+b+" "+a,c]};Blockly.Dart.logic_negate=function(a){var b=Blockly.Dart.ORDER_UNARY_PREFIX;return["!"+(Blockly.Dart.valueToCode(a,"BOOL",b)||"true"),b]};
|
||||
|
||||
@@ -4,42 +4,18 @@
|
||||
<meta charset="utf-8">
|
||||
<title>Accessible Blockly Demo</title>
|
||||
|
||||
<!-- Load Blockly -->
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../javascript_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<script src="../../msg/messages.js"></script>
|
||||
<script src="../../accessible/messages.js"></script>
|
||||
|
||||
<!-- Load accessibleBlockly -->
|
||||
<script src="../../accessible/libs/es6-shim.min.js"></script>
|
||||
<script src="../../accessible/libs/angular2-polyfills.min.js"></script>
|
||||
<script src="../../accessible/libs/Rx.umd.min.js"></script>
|
||||
<script src="../../accessible/libs/angular2-all.umd.min.js"></script>
|
||||
|
||||
<script src="../../accessible/utils.service.js"></script>
|
||||
<script src="../../accessible/notifications.service.js"></script>
|
||||
<script src="../../accessible/audio.service.js"></script>
|
||||
<script src="../../accessible/block-connection.service.js"></script>
|
||||
<script src="../../accessible/block-options-modal.service.js"></script>
|
||||
<script src="../../accessible/keyboard-input.service.js"></script>
|
||||
<script src="../../accessible/tree.service.js"></script>
|
||||
<script src="../../accessible/toolbox-modal.service.js"></script>
|
||||
<script src="../../accessible/translate.pipe.js"></script>
|
||||
<script src="../../accessible/variable-modal.service.js"></script>
|
||||
<!-- Load accessibleBlockly -->
|
||||
<script src="../../blockly_accessible_compressed.js"></script>
|
||||
|
||||
<script src="../../accessible/commonModal.js"></script>
|
||||
<script src="../../accessible/field-segment.component.js"></script>
|
||||
<script src="../../accessible/block-options-modal.component.js"></script>
|
||||
<script src="../../accessible/toolbox-modal.component.js"></script>
|
||||
<script src="../../accessible/variable-add-modal.component.js"></script>
|
||||
<script src="../../accessible/variable-rename-modal.component.js"></script>
|
||||
<script src="../../accessible/variable-remove-modal.component.js"></script>
|
||||
<script src="../../accessible/sidebar.component.js"></script>
|
||||
<script src="../../accessible/workspace-block.component.js"></script>
|
||||
<script src="../../accessible/workspace.component.js"></script>
|
||||
<script src="../../accessible/app.component.js"></script>
|
||||
<!-- Load Blockly -->
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<script src="../../accessible/messages.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="../../accessible/media/accessible.css">
|
||||
<style>
|
||||
@@ -168,13 +144,6 @@
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
<block type="math_change">
|
||||
<value name="DELTA">
|
||||
<block type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
<block type="math_round">
|
||||
<value name="NUM">
|
||||
<block type="math_number">
|
||||
|
||||
@@ -352,8 +352,8 @@ FactoryUtils.formatJavaScript_ = function(blockType, rootBlock, workspace) {
|
||||
|
||||
var tooltip = FactoryUtils.getTooltipFromRootBlock_(rootBlock);
|
||||
var helpUrl = FactoryUtils.getHelpUrlFromRootBlock_(rootBlock);
|
||||
code.push(" this.setTooltip('" + tooltip + "');");
|
||||
code.push(" this.setHelpUrl('" + helpUrl + "');");
|
||||
code.push(' this.setTooltip(' + JSON.stringify(tooltip) + ');');
|
||||
code.push(' this.setHelpUrl(' + JSON.stringify(helpUrl) + ');');
|
||||
code.push(' }');
|
||||
code.push('};');
|
||||
return code.join('\n');
|
||||
@@ -875,7 +875,7 @@ FactoryUtils.injectCode = function(code, id) {
|
||||
var pre = document.getElementById(id);
|
||||
pre.textContent = code;
|
||||
code = pre.textContent;
|
||||
code = prettyPrintOne(code, 'js');
|
||||
code = PR.prettyPrintOne(code, 'js');
|
||||
pre.innerHTML = code;
|
||||
};
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@
|
||||
<script src="app_controller.js"></script>
|
||||
<script src="/storage.js"></script>
|
||||
<link rel="stylesheet" href="factory.css">
|
||||
<link rel="stylesheet" href="../prettify.css">
|
||||
<script src="../prettify.js"></script>
|
||||
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>
|
||||
<script>
|
||||
var blocklyFactory;
|
||||
var init = function() {
|
||||
|
||||
@@ -396,8 +396,9 @@ WorkspaceFactoryInit.addWorkspaceFactoryEventListeners_ = function(controller) {
|
||||
// Not listening for Blockly create events because causes the user to drop
|
||||
// blocks when dragging them into workspace. Could cause problems if ever
|
||||
// load blocks into workspace directly without calling updatePreview.
|
||||
if (e.type == Blockly.Events.MOVE || e.type == Blockly.Events.DELETE ||
|
||||
e.type == Blockly.Events.CHANGE) {
|
||||
if (e.type == Blockly.Events.BLOCK_MOVE ||
|
||||
e.type == Blockly.Events.BLOCK_DELETE ||
|
||||
e.type == Blockly.Events.BLOCK_CHANGE) {
|
||||
controller.saveStateFromWorkspace();
|
||||
controller.updatePreview();
|
||||
}
|
||||
@@ -406,7 +407,7 @@ WorkspaceFactoryInit.addWorkspaceFactoryEventListeners_ = function(controller) {
|
||||
// Only enable "Edit Block" when a block is selected and it has a
|
||||
// surrounding parent, meaning it is nested in another block (blocks that
|
||||
// are not nested in parents cannot be shadow blocks).
|
||||
if (e.type == Blockly.Events.MOVE || (e.type == Blockly.Events.UI &&
|
||||
if (e.type == Blockly.Events.BLOCK_MOVE || (e.type == Blockly.Events.UI &&
|
||||
e.element == 'selected')) {
|
||||
var selected = Blockly.selected;
|
||||
|
||||
@@ -480,7 +481,7 @@ WorkspaceFactoryInit.addWorkspaceFactoryEventListeners_ = function(controller) {
|
||||
|
||||
// Convert actual shadow blocks added from the toolbox to user-generated
|
||||
// shadow blocks.
|
||||
if (e.type == Blockly.Events.CREATE) {
|
||||
if (e.type == Blockly.Events.BLOCK_CREATE) {
|
||||
controller.convertShadowBlocks();
|
||||
|
||||
// Let the user create a Variables or Functions category if they use
|
||||
|
||||
@@ -749,7 +749,7 @@ function injectCode(code, id) {
|
||||
var pre = document.getElementById(id);
|
||||
pre.textContent = code;
|
||||
code = pre.textContent;
|
||||
code = prettyPrintOne(code, 'js');
|
||||
code = PR.prettyPrintOne(code, 'js');
|
||||
pre.innerHTML = code;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,8 +88,7 @@
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="../prettify.css">
|
||||
<script src="../prettify.js"></script>
|
||||
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
|
||||
+13
-19
@@ -200,14 +200,8 @@ Code.bindClick = function(el, func) {
|
||||
* Load the Prettify CSS and JavaScript.
|
||||
*/
|
||||
Code.importPrettify = function() {
|
||||
//<link rel="stylesheet" href="../prettify.css">
|
||||
//<script src="../prettify.js"></script>
|
||||
var link = document.createElement('link');
|
||||
link.setAttribute('rel', 'stylesheet');
|
||||
link.setAttribute('href', '../prettify.css');
|
||||
document.head.appendChild(link);
|
||||
var script = document.createElement('script');
|
||||
script.setAttribute('src', '../prettify.js');
|
||||
script.setAttribute('src', 'https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js');
|
||||
document.head.appendChild(script);
|
||||
};
|
||||
|
||||
@@ -313,41 +307,41 @@ Code.renderContent = function() {
|
||||
} else if (content.id == 'content_javascript') {
|
||||
var code = Blockly.JavaScript.workspaceToCode(Code.workspace);
|
||||
content.textContent = code;
|
||||
if (typeof prettyPrintOne == 'function') {
|
||||
if (typeof PR.prettyPrintOne == 'function') {
|
||||
code = content.textContent;
|
||||
code = prettyPrintOne(code, 'js');
|
||||
code = PR.prettyPrintOne(code, 'js');
|
||||
content.innerHTML = code;
|
||||
}
|
||||
} else if (content.id == 'content_python') {
|
||||
code = Blockly.Python.workspaceToCode(Code.workspace);
|
||||
content.textContent = code;
|
||||
if (typeof prettyPrintOne == 'function') {
|
||||
if (typeof PR.prettyPrintOne == 'function') {
|
||||
code = content.textContent;
|
||||
code = prettyPrintOne(code, 'py');
|
||||
code = PR.prettyPrintOne(code, 'py');
|
||||
content.innerHTML = code;
|
||||
}
|
||||
} else if (content.id == 'content_php') {
|
||||
code = Blockly.PHP.workspaceToCode(Code.workspace);
|
||||
content.textContent = code;
|
||||
if (typeof prettyPrintOne == 'function') {
|
||||
if (typeof PR.prettyPrintOne == 'function') {
|
||||
code = content.textContent;
|
||||
code = prettyPrintOne(code, 'php');
|
||||
code = PR.prettyPrintOne(code, 'php');
|
||||
content.innerHTML = code;
|
||||
}
|
||||
} else if (content.id == 'content_dart') {
|
||||
code = Blockly.Dart.workspaceToCode(Code.workspace);
|
||||
content.textContent = code;
|
||||
if (typeof prettyPrintOne == 'function') {
|
||||
if (typeof PR.prettyPrintOne == 'function') {
|
||||
code = content.textContent;
|
||||
code = prettyPrintOne(code, 'dart');
|
||||
code = PR.prettyPrintOne(code, 'dart');
|
||||
content.innerHTML = code;
|
||||
}
|
||||
} else if (content.id == 'content_lua') {
|
||||
code = Blockly.Lua.workspaceToCode(Code.workspace);
|
||||
content.textContent = code;
|
||||
if (typeof prettyPrintOne == 'function') {
|
||||
if (typeof PR.prettyPrintOne == 'function') {
|
||||
code = content.textContent;
|
||||
code = prettyPrintOne(code, 'lua');
|
||||
code = PR.prettyPrintOne(code, 'lua');
|
||||
content.innerHTML = code;
|
||||
}
|
||||
}
|
||||
@@ -387,12 +381,12 @@ Code.init = function() {
|
||||
// format (eg. `<category name="%{BKY_CATLOGIC}">`).
|
||||
// These message keys need to be defined in `Blockly.Msg` in order to
|
||||
// be decoded by the library. Therefore, we'll use the `MSG` dictionary that's
|
||||
// been defined for each language to import each category name message
|
||||
// been defined for each language to import each category name message
|
||||
// into `Blockly.Msg`.
|
||||
// TODO: Clean up the message files so this is done explicitly instead of
|
||||
// through this for-loop.
|
||||
for (var messageKey in MSG) {
|
||||
if (messageKey.startsWith('cat')) {
|
||||
if (goog.string.startsWith(messageKey, 'cat')) {
|
||||
Blockly.Msg[messageKey.toUpperCase()] = MSG[messageKey];
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -107,8 +107,9 @@
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div><a href="interpreter/index.html">JS Interpreter</a></div>
|
||||
<div>Step by step execution in JavaScript.</div>
|
||||
<div style="font-weight: bold">JS Interpreter</div>
|
||||
<div>Demo #1: <a href="interpreter/step-execution.html">Step by step execution in JavaScript.</a></div>
|
||||
<div>Demo #2: <a href="interpreter/async-execution.html">Asynchronous execution in JavaScript.</a></div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Asynchronous Execution with JS Interpreter</title>
|
||||
<script src="acorn_interpreter.js"></script>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../javascript_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<script src="wait_block.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Asynchronous Execution with JS Interpreter</h1>
|
||||
|
||||
<p>This is a demo of executing code asychronously (e.g., waiting for delays or user input) using the JavaScript interpreter.</p>
|
||||
|
||||
<p>→ <a href="https://developers.google.com/blockly/guides/configure-blockly/web/running-javascript#js_interpreter">More info on running code with JS Interpreter</a></p>
|
||||
|
||||
<p>
|
||||
<button onclick="runCode()" id="runButton">Run JavaScript</button>
|
||||
</p>
|
||||
|
||||
<div style="width: 100%">
|
||||
<div id="blocklyDiv"
|
||||
style="display: inline-block; height: 480px; width: 58%"></div>
|
||||
<textarea id="output" disabled="disabled"
|
||||
style="display: inline-block; height: 480px; width: 38%;">
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<xml id="toolbox" style="display: none">
|
||||
<category name="Logic">
|
||||
<block type="controls_if"></block>
|
||||
<block type="logic_compare"></block>
|
||||
<block type="logic_operation"></block>
|
||||
<block type="logic_negate"></block>
|
||||
<block type="logic_boolean"></block>
|
||||
</category>
|
||||
<category name="Loops">
|
||||
<block type="controls_repeat_ext">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
<block type="controls_whileUntil"></block>
|
||||
</category>
|
||||
<category name="Math">
|
||||
<block type="math_number"></block>
|
||||
<block type="math_arithmetic"></block>
|
||||
<block type="math_single"></block>
|
||||
</category>
|
||||
<category name="Text">
|
||||
<block type="text"></block>
|
||||
<block type="text_length"></block>
|
||||
<block type="text_print"></block>
|
||||
<block type="text_prompt_ext">
|
||||
<value name="TEXT">
|
||||
<block type="text"></block>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<category name="Variables" custom="VARIABLE"></category>
|
||||
<category name="Functions" custom="PROCEDURE"></category>
|
||||
</xml>
|
||||
|
||||
<xml id="startBlocks" style="display: none">
|
||||
<block type="variables_set" id="set_n_initial" inline="true" x="20" y="20">
|
||||
<field name="VAR">n</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="controls_repeat_ext" id="repeat" inline="true">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">4</field>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="wait_seconds" id="wait">
|
||||
<field name="SECONDS">1.0</field>
|
||||
<next>
|
||||
<block type="variables_set" id="set_n_update" inline="true">
|
||||
<field name="VAR">n</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_arithmetic" inline="true">
|
||||
<field name="OP">MULTIPLY</field>
|
||||
<value name="A">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">n</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_number">
|
||||
<field name="NUM">2</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="text_print" id="print" inline="false">
|
||||
<value name="TEXT">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">n</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var workspace = Blockly.inject('blocklyDiv',
|
||||
{media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
|
||||
workspace);
|
||||
|
||||
// Exit is used to signal the end of a script.
|
||||
Blockly.JavaScript.addReservedWords('exit');
|
||||
|
||||
var outputArea = document.getElementById('output');
|
||||
var runButton = document.getElementById('runButton');
|
||||
var myInterpreter = null;
|
||||
var runner;
|
||||
|
||||
function initApi(interpreter, scope) {
|
||||
// Add an API function for the alert() block, generated for "text_print" blocks.
|
||||
var wrapper = function(text) {
|
||||
text = text ? text.toString() : '';
|
||||
outputArea.value = outputArea.value + '\n' + text;
|
||||
};
|
||||
interpreter.setProperty(scope, 'alert',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
|
||||
// Add an API function for the prompt() block.
|
||||
var wrapper = function(text) {
|
||||
text = text ? text.toString() : '';
|
||||
return interpreter.createPrimitive(prompt(text));
|
||||
};
|
||||
interpreter.setProperty(scope, 'prompt',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
|
||||
// Add an API for the wait block. See wait_block.js
|
||||
initInterpreterWaitForSeconds(interpreter, scope);
|
||||
|
||||
// Add an API function for highlighting blocks.
|
||||
var wrapper = function(id) {
|
||||
id = id ? id.toString() : '';
|
||||
return interpreter.createPrimitive(highlightBlock(id));
|
||||
};
|
||||
interpreter.setProperty(scope, 'highlightBlock',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
}
|
||||
|
||||
var highlightPause = false;
|
||||
var latestCode = '';
|
||||
|
||||
function highlightBlock(id) {
|
||||
workspace.highlightBlock(id);
|
||||
highlightPause = true;
|
||||
}
|
||||
|
||||
function resetStepUi(clearOutput) {
|
||||
workspace.highlightBlock(null);
|
||||
highlightPause = false;
|
||||
runButton.disabled = '';
|
||||
|
||||
if (clearOutput) {
|
||||
outputArea.value = 'Program output:\n=================';
|
||||
}
|
||||
}
|
||||
|
||||
function generateCodeAndLoadIntoInterpreter() {
|
||||
// Generate JavaScript code and parse it.
|
||||
Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
|
||||
Blockly.JavaScript.addReservedWords('highlightBlock');
|
||||
latestCode = Blockly.JavaScript.workspaceToCode(workspace);
|
||||
|
||||
resetStepUi(true);
|
||||
}
|
||||
|
||||
function resetInterpreter() {
|
||||
myInterpreter = null;
|
||||
if (runner) {
|
||||
clearTimeout(runner);
|
||||
runner = null;
|
||||
}
|
||||
}
|
||||
|
||||
function runCode() {
|
||||
if (!myInterpreter) {
|
||||
// First statement of this code.
|
||||
// Clear the program output.
|
||||
resetStepUi(true);
|
||||
runButton.disabled = 'disabled';
|
||||
|
||||
// And then show generated code in an alert.
|
||||
// In a timeout to allow the outputArea.value to reset first.
|
||||
setTimeout(function() {
|
||||
alert('Ready to execute the following code\n' +
|
||||
'===================================\n' +
|
||||
latestCode);
|
||||
|
||||
// Begin execution
|
||||
highlightPause = false;
|
||||
myInterpreter = new Interpreter(latestCode, initApi);
|
||||
runner = function() {
|
||||
if (myInterpreter) {
|
||||
var hasMore = myInterpreter.run();
|
||||
if (hasMore) {
|
||||
// Execution is currently blocked by some async call.
|
||||
// Try again later.
|
||||
setTimeout(runner, 10);
|
||||
} else {
|
||||
// Program is complete.
|
||||
outputArea.value += '\n\n<< Program complete >>';
|
||||
resetInterpreter();
|
||||
resetStepUi(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
runner();
|
||||
}, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Load the interpreter now, and upon future changes.
|
||||
generateCodeAndLoadIntoInterpreter();
|
||||
workspace.addChangeListener(function(event) {
|
||||
if (!(event instanceof Blockly.Events.Ui)) {
|
||||
// Something changed. Parser needs to be reloaded.
|
||||
resetInterpreter();
|
||||
generateCodeAndLoadIntoInterpreter();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,200 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: JS Interpreter</title>
|
||||
<script src="acorn_interpreter.js"></script>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../javascript_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
</style>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Redirecting...</title>
|
||||
<meta http-equiv="refresh" content="0;URL='step-execution.html'"/>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > JS Interpreter</h1>
|
||||
|
||||
<p>This is a simple demo of executing code with a sandboxed JavaScript interpreter.</p>
|
||||
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/guides/configure-blockly/web/running-javascript#js_interpreter">JS Interpreter</a>…</p>
|
||||
|
||||
<p>
|
||||
<button onclick="parseCode()">Parse JavaScript</button>
|
||||
<button onclick="stepCode()" id="stepButton" disabled="disabled">Step JavaScript</button>
|
||||
</p>
|
||||
|
||||
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
|
||||
|
||||
<xml id="toolbox" style="display: none">
|
||||
<category name="Logic">
|
||||
<block type="controls_if"></block>
|
||||
<block type="logic_compare"></block>
|
||||
<block type="logic_operation"></block>
|
||||
<block type="logic_negate"></block>
|
||||
<block type="logic_boolean"></block>
|
||||
</category>
|
||||
<category name="Loops">
|
||||
<block type="controls_repeat_ext">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
<block type="controls_whileUntil"></block>
|
||||
</category>
|
||||
<category name="Math">
|
||||
<block type="math_number"></block>
|
||||
<block type="math_arithmetic"></block>
|
||||
<block type="math_single"></block>
|
||||
</category>
|
||||
<category name="Text">
|
||||
<block type="text"></block>
|
||||
<block type="text_length"></block>
|
||||
<block type="text_print"></block>
|
||||
<block type="text_prompt_ext">
|
||||
<value name="TEXT">
|
||||
<block type="text"></block>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<category name="Variables" custom="VARIABLE"></category>
|
||||
<category name="Functions" custom="PROCEDURE"></category>
|
||||
</xml>
|
||||
|
||||
<xml id="startBlocks" style="display: none">
|
||||
<block type="variables_set" inline="true" x="20" y="20">
|
||||
<field name="VAR">n</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="controls_repeat_ext" inline="true">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">4</field>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="variables_set" inline="true">
|
||||
<field name="VAR">n</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_arithmetic" inline="true">
|
||||
<field name="OP">MULTIPLY</field>
|
||||
<value name="A">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">n</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_number">
|
||||
<field name="NUM">2</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</statement>
|
||||
<next>
|
||||
<block type="text_print" inline="false">
|
||||
<value name="TEXT">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">n</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var workspace = Blockly.inject('blocklyDiv',
|
||||
{media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
|
||||
workspace);
|
||||
|
||||
var myInterpreter = null;
|
||||
|
||||
function initApi(interpreter, scope) {
|
||||
// Add an API function for the alert() block.
|
||||
var wrapper = function(text) {
|
||||
text = text ? text.toString() : '';
|
||||
return interpreter.createPrimitive(alert(text));
|
||||
};
|
||||
interpreter.setProperty(scope, 'alert',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
|
||||
// Add an API function for the prompt() block.
|
||||
var wrapper = function(text) {
|
||||
text = text ? text.toString() : '';
|
||||
return interpreter.createPrimitive(prompt(text));
|
||||
};
|
||||
interpreter.setProperty(scope, 'prompt',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
|
||||
// Add an API function for highlighting blocks.
|
||||
var wrapper = function(id) {
|
||||
id = id ? id.toString() : '';
|
||||
return interpreter.createPrimitive(highlightBlock(id));
|
||||
};
|
||||
interpreter.setProperty(scope, 'highlightBlock',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
}
|
||||
|
||||
var highlightPause = false;
|
||||
|
||||
function highlightBlock(id) {
|
||||
workspace.highlightBlock(id);
|
||||
highlightPause = true;
|
||||
}
|
||||
|
||||
function parseCode() {
|
||||
// Generate JavaScript code and parse it.
|
||||
Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
|
||||
Blockly.JavaScript.addReservedWords('highlightBlock');
|
||||
var code = Blockly.JavaScript.workspaceToCode(workspace);
|
||||
myInterpreter = new Interpreter(code, initApi);
|
||||
|
||||
alert('Ready to execute this code:\n\n' + code);
|
||||
document.getElementById('stepButton').disabled = '';
|
||||
highlightPause = false;
|
||||
workspace.highlightBlock(null);
|
||||
}
|
||||
|
||||
function stepCode() {
|
||||
try {
|
||||
var ok = myInterpreter.step();
|
||||
} finally {
|
||||
if (!ok) {
|
||||
// Program complete, no more code to execute.
|
||||
document.getElementById('stepButton').disabled = 'disabled';
|
||||
workspace.highlightBlock(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (highlightPause) {
|
||||
// A block has been highlighted. Pause execution here.
|
||||
highlightPause = false;
|
||||
} else {
|
||||
// Keep executing until a highlight statement is reached.
|
||||
stepCode();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Redirecting to <a href="step-execution.html">step execution jsinterpreter demo</a>.
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Step Execution with JS Interpreter</title>
|
||||
<script src="acorn_interpreter.js"></script>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../javascript_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Step Execution with JS Interpreter</h1>
|
||||
|
||||
<p>This is a demo of executing code step-by-step with a sandboxed JavaScript interpreter.</p>
|
||||
|
||||
<p>The generator's <code>Blockly.JavaScript.STATEMENT_PREFIX</code> is assigned <code>'highlightBlock(%1);\n'</code>,
|
||||
where <code>%1</code> is the block id. The call to <code>highlightBlock()</code> will highlight the identified block
|
||||
and set the variable <code>highlightPause</code> to <code>true</code>.</p>
|
||||
|
||||
<p>"Parse JavaScript" will generate the code and load it into the interpreter. Then, each press of the
|
||||
"Step JavaScript" button will run the interpreter one step until the <code>highlightPause</code> is true.
|
||||
That is, until <code>highlightBlock()</code> has highlighted the block that will be executed on the next step.</p>
|
||||
|
||||
<p>→ <a href="https://developers.google.com/blockly/guides/configure-blockly/web/running-javascript#js_interpreter">More info on running code with JS Interpreter</a></p>
|
||||
|
||||
<p>
|
||||
<button onclick="stepCode()" id="stepButton">Step JavaScript</button>
|
||||
</p>
|
||||
|
||||
<div style="width: 100%">
|
||||
<div id="blocklyDiv"
|
||||
style="display: inline-block; height: 480px; width: 58%"></div>
|
||||
<textarea id="output" disabled="disabled"
|
||||
style="display: inline-block; height: 480px; width: 38%;">
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<xml id="toolbox" style="display: none">
|
||||
<category name="Logic">
|
||||
<block type="controls_if"></block>
|
||||
<block type="logic_compare"></block>
|
||||
<block type="logic_operation"></block>
|
||||
<block type="logic_negate"></block>
|
||||
<block type="logic_boolean"></block>
|
||||
</category>
|
||||
<category name="Loops">
|
||||
<block type="controls_repeat_ext">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
<block type="controls_whileUntil"></block>
|
||||
</category>
|
||||
<category name="Math">
|
||||
<block type="math_number"></block>
|
||||
<block type="math_arithmetic"></block>
|
||||
<block type="math_single"></block>
|
||||
</category>
|
||||
<category name="Text">
|
||||
<block type="text"></block>
|
||||
<block type="text_length"></block>
|
||||
<block type="text_print"></block>
|
||||
<block type="text_prompt_ext">
|
||||
<value name="TEXT">
|
||||
<block type="text"></block>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<category name="Variables" custom="VARIABLE"></category>
|
||||
<category name="Functions" custom="PROCEDURE"></category>
|
||||
</xml>
|
||||
|
||||
<xml id="startBlocks" style="display: none">
|
||||
<block type="variables_set" id="set_n_initial" inline="true" x="20" y="20">
|
||||
<field name="VAR">n</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="controls_repeat_ext" id="repeat" inline="true">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">4</field>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="variables_set" id="set_n_update" inline="true">
|
||||
<field name="VAR">n</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_arithmetic" inline="true">
|
||||
<field name="OP">MULTIPLY</field>
|
||||
<value name="A">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">n</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_number">
|
||||
<field name="NUM">2</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="text_print" id="print">
|
||||
<value name="TEXT">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">n</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var workspace = Blockly.inject('blocklyDiv',
|
||||
{media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
|
||||
workspace);
|
||||
|
||||
var outputArea = document.getElementById('output');
|
||||
var stepButton = document.getElementById('stepButton');
|
||||
var myInterpreter = null;
|
||||
|
||||
function initApi(interpreter, scope) {
|
||||
// Add an API function for the alert() block, generated for "text_print" blocks.
|
||||
interpreter.setProperty(scope, 'alert',
|
||||
interpreter.createNativeFunction(function(text) {
|
||||
text = text ? text.toString() : '';
|
||||
outputArea.value += '\n' + text;
|
||||
}));
|
||||
|
||||
// Add an API function for the prompt() block.
|
||||
var wrapper = function(text) {
|
||||
text = text ? text.toString() : '';
|
||||
return interpreter.createPrimitive(prompt(text));
|
||||
};
|
||||
interpreter.setProperty(scope, 'prompt',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
|
||||
// Add an API function for highlighting blocks.
|
||||
var wrapper = function(id) {
|
||||
id = id ? id.toString() : '';
|
||||
return interpreter.createPrimitive(highlightBlock(id));
|
||||
};
|
||||
interpreter.setProperty(scope, 'highlightBlock',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
}
|
||||
|
||||
var highlightPause = false;
|
||||
var latestCode = '';
|
||||
|
||||
function highlightBlock(id) {
|
||||
workspace.highlightBlock(id);
|
||||
highlightPause = true;
|
||||
}
|
||||
|
||||
function resetStepUi(clearOutput) {
|
||||
workspace.highlightBlock(null);
|
||||
highlightPause = false;
|
||||
|
||||
if (clearOutput) {
|
||||
outputArea.value = 'Program output:\n=================';
|
||||
}
|
||||
}
|
||||
|
||||
function generateCodeAndLoadIntoInterpreter() {
|
||||
// Generate JavaScript code and parse it.
|
||||
Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
|
||||
Blockly.JavaScript.addReservedWords('highlightBlock');
|
||||
latestCode = Blockly.JavaScript.workspaceToCode(workspace);
|
||||
|
||||
resetStepUi(true);
|
||||
}
|
||||
|
||||
function stepCode() {
|
||||
if (!myInterpreter) {
|
||||
// First statement of this code.
|
||||
// Clear the program output.
|
||||
resetStepUi(true);
|
||||
myInterpreter = new Interpreter(latestCode, initApi);
|
||||
|
||||
// And then show generated code in an alert.
|
||||
// In a timeout to allow the outputArea.value to reset first.
|
||||
setTimeout(function() {
|
||||
alert('Ready to execute the following code\n' +
|
||||
'===================================\n' + latestCode);
|
||||
highlightPause = true;
|
||||
stepCode();
|
||||
}, 1);
|
||||
return;
|
||||
}
|
||||
highlightPause = false;
|
||||
do {
|
||||
try {
|
||||
var hasMoreCode = myInterpreter.step();
|
||||
} finally {
|
||||
if (!hasMoreCode) {
|
||||
// Program complete, no more code to execute.
|
||||
outputArea.value += '\n\n<< Program complete >>';
|
||||
|
||||
myInterpreter = null;
|
||||
resetStepUi(false);
|
||||
|
||||
// Cool down, to discourage accidentally restarting the program.
|
||||
stepButton.disabled = 'disabled';
|
||||
setTimeout(function() {
|
||||
stepButton.disabled = '';
|
||||
}, 2000);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Keep executing until a highlight statement is reached,
|
||||
// or the code completes or errors.
|
||||
} while (hasMoreCode && !highlightPause);
|
||||
}
|
||||
|
||||
// Load the interpreter now, and upon future changes.
|
||||
generateCodeAndLoadIntoInterpreter();
|
||||
workspace.addChangeListener(function(event) {
|
||||
if (!(event instanceof Blockly.Events.Ui)) {
|
||||
// Something changed. Parser needs to be reloaded.
|
||||
generateCodeAndLoadIntoInterpreter();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2017 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Example "wait" block that will pause the interpreter for a
|
||||
* number of seconds. Because wait is a blocking behavior, such blocks will
|
||||
* only work in interpreted environments.
|
||||
*
|
||||
* See https://neil.fraser.name/software/JS-Interpreter/docs.html
|
||||
*/
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "wait_seconds",
|
||||
"message0": " wait %1 seconds",
|
||||
"args0": [{
|
||||
"type": "field_number",
|
||||
"name": "SECONDS",
|
||||
"min": 0,
|
||||
"max": 600,
|
||||
"value": 1
|
||||
}],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": "%{BKY_LOOPS_HUE}"
|
||||
}]);
|
||||
|
||||
/**
|
||||
* Generator for wait block creates call to new method
|
||||
* <code>waitForSeconds()</code>.
|
||||
*/
|
||||
Blockly.JavaScript['wait_seconds'] = function(block) {
|
||||
var seconds = Number(block.getFieldValue('SECONDS'));
|
||||
var code = 'waitForSeconds(' + seconds + ');\n';
|
||||
return code;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the interpreter asynchronous function
|
||||
* <code>waitForSeconds()</code>.
|
||||
*/
|
||||
function initInterpreterWaitForSeconds(interpreter, scope) {
|
||||
// Ensure function name does not conflict with variable names.
|
||||
Blockly.JavaScript.addReservedWords('waitForSeconds');
|
||||
|
||||
var wrapper = interpreter.createAsyncFunction(
|
||||
function(timeInSeconds, callback) {
|
||||
// Delay the call to the callback.
|
||||
setTimeout(callback, timeInSeconds * 1000);
|
||||
});
|
||||
interpreter.setProperty(scope, 'waitForSeconds', wrapper);
|
||||
}
|
||||
@@ -56,7 +56,7 @@
|
||||
{media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
// Inject secondary workspace.
|
||||
var seconaryWorkspace = Blockly.inject('secondaryDiv',
|
||||
var secondaryWorkspace = Blockly.inject('secondaryDiv',
|
||||
{media: '../../media/',
|
||||
readOnly: true});
|
||||
// Listen to events on primary workspace.
|
||||
@@ -70,7 +70,7 @@
|
||||
var json = primaryEvent.toJson();
|
||||
console.log(json);
|
||||
// Convert JSON back into an event, then execute it.
|
||||
var secondaryEvent = Blockly.Events.fromJson(json, seconaryWorkspace);
|
||||
var secondaryEvent = Blockly.Events.fromJson(json, secondaryWorkspace);
|
||||
secondaryEvent.run(true);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
|
||||
@@ -1,30 +0,0 @@
|
||||
!function(){var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
|
||||
(function(){function S(a){function d(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=r[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a=
|
||||
b[0]==="^",c=["["];a&&c.push("^");for(var a=a?1:0,f=b.length;a<f;++a){var h=b[a];if(/\\[bdsw]/i.test(h))c.push(h);else{var h=d(h),l;a+2<f&&"-"===b[a+1]?(l=d(b[a+2]),a+=2):l=h;e.push([h,l]);l<65||h>122||(l<65||h>90||e.push([Math.max(65,h)|32,Math.min(l,90)|32]),l<97||h>122||e.push([Math.max(97,h)&-33,Math.min(l,122)&-33]))}}e.sort(function(e,a){return e[0]-a[0]||a[1]-e[1]});b=[];f=[];for(a=0;a<e.length;++a)h=e[a],h[0]<=f[1]+1?f[1]=Math.max(f[1],h[1]):b.push(f=h);for(a=0;a<b.length;++a)h=b[a],c.push(g(h[0])),
|
||||
h[1]>h[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(g(h[1])));c.push("]");return c.join("")}function s(e){for(var a=e.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),c=a.length,d=[],f=0,h=0;f<c;++f){var l=a[f];l==="("?++h:"\\"===l.charAt(0)&&(l=+l.substring(1))&&(l<=h?d[l]=-1:a[f]=g(l))}for(f=1;f<d.length;++f)-1===d[f]&&(d[f]=++x);for(h=f=0;f<c;++f)l=a[f],l==="("?(++h,d[h]||(a[f]="(?:")):"\\"===l.charAt(0)&&(l=+l.substring(1))&&l<=h&&
|
||||
(a[f]="\\"+d[l]);for(f=0;f<c;++f)"^"===a[f]&&"^"!==a[f+1]&&(a[f]="");if(e.ignoreCase&&m)for(f=0;f<c;++f)l=a[f],e=l.charAt(0),l.length>=2&&e==="["?a[f]=b(l):e!=="\\"&&(a[f]=l.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return a.join("")}for(var x=0,m=!1,j=!1,k=0,c=a.length;k<c;++k){var i=a[k];if(i.ignoreCase)j=!0;else if(/[a-z]/i.test(i.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){m=!0;j=!1;break}}for(var r={b:8,t:9,n:10,v:11,
|
||||
f:12,r:13},n=[],k=0,c=a.length;k<c;++k){i=a[k];if(i.global||i.multiline)throw Error(""+i);n.push("(?:"+s(i)+")")}return RegExp(n.join("|"),j?"gi":"g")}function T(a,d){function g(a){var c=a.nodeType;if(c==1){if(!b.test(a.className)){for(c=a.firstChild;c;c=c.nextSibling)g(c);c=a.nodeName.toLowerCase();if("br"===c||"li"===c)s[j]="\n",m[j<<1]=x++,m[j++<<1|1]=a}}else if(c==3||c==4)c=a.nodeValue,c.length&&(c=d?c.replace(/\r\n?/g,"\n"):c.replace(/[\t\n\r ]+/g," "),s[j]=c,m[j<<1]=x,x+=c.length,m[j++<<1|1]=
|
||||
a)}var b=/(?:^|\s)nocode(?:\s|$)/,s=[],x=0,m=[],j=0;g(a);return{a:s.join("").replace(/\n$/,""),d:m}}function H(a,d,g,b){d&&(a={a:d,e:a},g(a),b.push.apply(b,a.g))}function U(a){for(var d=void 0,g=a.firstChild;g;g=g.nextSibling)var b=g.nodeType,d=b===1?d?a:g:b===3?V.test(g.nodeValue)?a:d:d;return d===a?void 0:d}function C(a,d){function g(a){for(var j=a.e,k=[j,"pln"],c=0,i=a.a.match(s)||[],r={},n=0,e=i.length;n<e;++n){var z=i[n],w=r[z],t=void 0,f;if(typeof w==="string")f=!1;else{var h=b[z.charAt(0)];
|
||||
if(h)t=z.match(h[1]),w=h[0];else{for(f=0;f<x;++f)if(h=d[f],t=z.match(h[1])){w=h[0];break}t||(w="pln")}if((f=w.length>=5&&"lang-"===w.substring(0,5))&&!(t&&typeof t[1]==="string"))f=!1,w="src";f||(r[z]=w)}h=c;c+=z.length;if(f){f=t[1];var l=z.indexOf(f),B=l+f.length;t[2]&&(B=z.length-t[2].length,l=B-f.length);w=w.substring(5);H(j+h,z.substring(0,l),g,k);H(j+h+l,f,I(w,f),k);H(j+h+B,z.substring(B),g,k)}else k.push(j+h,w)}a.g=k}var b={},s;(function(){for(var g=a.concat(d),j=[],k={},c=0,i=g.length;c<i;++c){var r=
|
||||
g[c],n=r[3];if(n)for(var e=n.length;--e>=0;)b[n.charAt(e)]=r;r=r[1];n=""+r;k.hasOwnProperty(n)||(j.push(r),k[n]=q)}j.push(/[\S\s]/);s=S(j)})();var x=d.length;return g}function v(a){var d=[],g=[];a.tripleQuotedStrings?d.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?d.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
|
||||
q,"'\"`"]):d.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var b=a.hashComments;b&&(a.cStyleComments?(b>1?d.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):d.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,q])):d.push(["com",
|
||||
/^#[^\n\r]*/,q,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,q]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));if(b=a.regexLiterals){var s=(b=b>1?"":"\n\r")?".":"[\\S\\s]";g.push(["lang-regex",RegExp("^(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+s+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+
|
||||
s+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&g.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&g.push(["kwd",RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),q]);d.push(["pln",/^\s+/,q," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");g.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,
|
||||
q],["pun",RegExp(b),q]);return C(d,g)}function J(a,d,g){function b(a){var c=a.nodeType;if(c==1&&!x.test(a.className))if("br"===a.nodeName)s(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((c==3||c==4)&&g){var d=a.nodeValue,i=d.match(m);if(i)c=d.substring(0,i.index),a.nodeValue=c,(d=d.substring(i.index+i[0].length))&&a.parentNode.insertBefore(j.createTextNode(d),a.nextSibling),s(a),c||a.parentNode.removeChild(a)}}function s(a){function b(a,c){var d=
|
||||
c?a.cloneNode(!1):a,e=a.parentNode;if(e){var e=b(e,1),g=a.nextSibling;e.appendChild(d);for(var i=g;i;i=g)g=i.nextSibling,e.appendChild(i)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),d;(d=a.parentNode)&&d.nodeType===1;)a=d;c.push(a)}for(var x=/(?:^|\s)nocode(?:\s|$)/,m=/\r\n?|\n/,j=a.ownerDocument,k=j.createElement("li");a.firstChild;)k.appendChild(a.firstChild);for(var c=[k],i=0;i<c.length;++i)b(c[i]);d===(d|0)&&c[0].setAttribute("value",d);var r=j.createElement("ol");
|
||||
r.className="linenums";for(var d=Math.max(0,d-1|0)||0,i=0,n=c.length;i<n;++i)k=c[i],k.className="L"+(i+d)%10,k.firstChild||k.appendChild(j.createTextNode("\u00a0")),r.appendChild(k);a.appendChild(r)}function p(a,d){for(var g=d.length;--g>=0;){var b=d[g];F.hasOwnProperty(b)?D.console&&console.warn("cannot override language handler %s",b):F[b]=a}}function I(a,d){if(!a||!F.hasOwnProperty(a))a=/^\s*</.test(d)?"default-markup":"default-code";return F[a]}function K(a){var d=a.h;try{var g=T(a.c,a.i),b=g.a;
|
||||
a.a=b;a.d=g.d;a.e=0;I(d,b)(a);var s=/\bMSIE\s(\d+)/.exec(navigator.userAgent),s=s&&+s[1]<=8,d=/\n/g,x=a.a,m=x.length,g=0,j=a.d,k=j.length,b=0,c=a.g,i=c.length,r=0;c[i]=m;var n,e;for(e=n=0;e<i;)c[e]!==c[e+2]?(c[n++]=c[e++],c[n++]=c[e++]):e+=2;i=n;for(e=n=0;e<i;){for(var p=c[e],w=c[e+1],t=e+2;t+2<=i&&c[t+1]===w;)t+=2;c[n++]=p;c[n++]=w;e=t}c.length=n;var f=a.c,h;if(f)h=f.style.display,f.style.display="none";try{for(;b<k;){var l=j[b+2]||m,B=c[r+2]||m,t=Math.min(l,B),A=j[b+1],G;if(A.nodeType!==1&&(G=x.substring(g,
|
||||
t))){s&&(G=G.replace(d,"\r"));A.nodeValue=G;var L=A.ownerDocument,o=L.createElement("span");o.className=c[r+1];var v=A.parentNode;v.replaceChild(o,A);o.appendChild(A);g<l&&(j[b+1]=A=L.createTextNode(x.substring(t,l)),v.insertBefore(A,o.nextSibling))}g=t;g>=l&&(b+=2);g>=B&&(r+=2)}}finally{if(f)f.style.display=h}}catch(u){D.console&&console.log(u&&u.stack||u)}}var D=window,y=["break,continue,do,else,for,if,return,while"],E=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
|
||||
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],M=[E,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],N=[E,"abstract,assert,boolean,byte,extends,final,finally,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],
|
||||
O=[N,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],E=[E,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],P=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
|
||||
Q=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],W=[y,"as,assert,const,copy,drop,enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],R=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
|
||||
V=/\S/,X=v({keywords:[M,O,E,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",P,Q,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),F={};p(X,["default-code"]);p(C([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
|
||||
/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);p(C([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],
|
||||
["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);p(C([],[["atv",/^[\S\s]+/]]),["uq.val"]);p(v({keywords:M,hashComments:!0,cStyleComments:!0,types:R}),["c","cc","cpp","cxx","cyc","m"]);p(v({keywords:"null,true,false"}),["json"]);p(v({keywords:O,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:R}),
|
||||
["cs"]);p(v({keywords:N,cStyleComments:!0}),["java"]);p(v({keywords:y,hashComments:!0,multiLineStrings:!0}),["bash","bsh","csh","sh"]);p(v({keywords:P,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py","python"]);p(v({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:2}),["perl","pl","pm"]);p(v({keywords:Q,
|
||||
hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb","ruby"]);p(v({keywords:E,cStyleComments:!0,regexLiterals:!0}),["javascript","js"]);p(v({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);p(v({keywords:W,cStyleComments:!0,multilineStrings:!0}),["rc","rs","rust"]);
|
||||
p(C([],[["str",/^[\S\s]+/]]),["regex"]);var Y=D.PR={createSimpleLexer:C,registerLangHandler:p,sourceDecorator:v,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:D.prettyPrintOne=function(a,d,g){var b=document.createElement("div");b.innerHTML="<pre>"+a+"</pre>";b=b.firstChild;g&&J(b,g,!0);K({h:d,j:g,c:b,i:1});
|
||||
return b.innerHTML},prettyPrint:D.prettyPrint=function(a,d){function g(){for(var b=D.PR_SHOULD_USE_CONTINUATION?c.now()+250:Infinity;i<p.length&&c.now()<b;i++){for(var d=p[i],j=h,k=d;k=k.previousSibling;){var m=k.nodeType,o=(m===7||m===8)&&k.nodeValue;if(o?!/^\??prettify\b/.test(o):m!==3||/\S/.test(k.nodeValue))break;if(o){j={};o.replace(/\b(\w+)=([\w%+\-.:]+)/g,function(a,b,c){j[b]=c});break}}k=d.className;if((j!==h||e.test(k))&&!v.test(k)){m=!1;for(o=d.parentNode;o;o=o.parentNode)if(f.test(o.tagName)&&
|
||||
o.className&&e.test(o.className)){m=!0;break}if(!m){d.className+=" prettyprinted";m=j.lang;if(!m){var m=k.match(n),y;if(!m&&(y=U(d))&&t.test(y.tagName))m=y.className.match(n);m&&(m=m[1])}if(w.test(d.tagName))o=1;else var o=d.currentStyle,u=s.defaultView,o=(o=o?o.whiteSpace:u&&u.getComputedStyle?u.getComputedStyle(d,q).getPropertyValue("white-space"):0)&&"pre"===o.substring(0,3);u=j.linenums;if(!(u=u==="true"||+u))u=(u=k.match(/\blinenums\b(?::(\d+))?/))?u[1]&&u[1].length?+u[1]:!0:!1;u&&J(d,u,o);r=
|
||||
{h:m,c:d,j:u,i:o};K(r)}}}i<p.length?setTimeout(g,250):"function"===typeof a&&a()}for(var b=d||document.body,s=b.ownerDocument||document,b=[b.getElementsByTagName("pre"),b.getElementsByTagName("code"),b.getElementsByTagName("xmp")],p=[],m=0;m<b.length;++m)for(var j=0,k=b[m].length;j<k;++j)p.push(b[m][j]);var b=q,c=Date;c.now||(c={now:function(){return+new Date}});var i=0,r,n=/\blang(?:uage)?-([\w.]+)(?!\S)/,e=/\bprettyprint\b/,v=/\bprettyprinted\b/,w=/pre|xmp/i,t=/^code$/i,f=/^(?:pre|code|xmp)$/i,
|
||||
h={};g()}};typeof define==="function"&&define.amd&&define("google-code-prettify",[],function(){return Y})})();}()
|
||||
+2
-2
@@ -103,10 +103,10 @@ Blockly.Dart.init = function(workspace) {
|
||||
}
|
||||
|
||||
var defvars = [];
|
||||
var variables = workspace.variableList;
|
||||
var variables = workspace.getAllVariables();
|
||||
if (variables.length) {
|
||||
for (var i = 0; i < variables.length; i++) {
|
||||
defvars[i] = Blockly.Dart.variableDB_.getName(variables[i],
|
||||
defvars[i] = Blockly.Dart.variableDB_.getName(variables[i].name,
|
||||
Blockly.Variables.NAME_TYPE);
|
||||
}
|
||||
Blockly.Dart.definitions_['variables'] =
|
||||
|
||||
@@ -153,10 +153,10 @@ Blockly.JavaScript.init = function(workspace) {
|
||||
}
|
||||
|
||||
var defvars = [];
|
||||
var variables = workspace.variableList;
|
||||
var variables = workspace.getAllVariables();
|
||||
if (variables.length) {
|
||||
for (var i = 0; i < variables.length; i++) {
|
||||
defvars[i] = Blockly.JavaScript.variableDB_.getName(variables[i],
|
||||
defvars[i] = Blockly.JavaScript.variableDB_.getName(variables[i].name,
|
||||
Blockly.Variables.NAME_TYPE);
|
||||
}
|
||||
Blockly.JavaScript.definitions_['variables'] =
|
||||
|
||||
+4
-2
@@ -150,9 +150,11 @@ Blockly.PHP.init = function(workspace) {
|
||||
}
|
||||
|
||||
var defvars = [];
|
||||
var varName;
|
||||
var variables = Blockly.Variables.allVariables(workspace);
|
||||
for (var i = 0; i < variables.length; i++) {
|
||||
defvars[i] = Blockly.PHP.variableDB_.getName(variables[i],
|
||||
for (var i = 0, variable; variable = variables[i]; i++) {
|
||||
varName = variable.name;
|
||||
defvars[i] = Blockly.PHP.variableDB_.getName(varName,
|
||||
Blockly.Variables.NAME_TYPE) + ';';
|
||||
}
|
||||
Blockly.PHP.definitions_['variables'] = defvars.join('\n');
|
||||
|
||||
@@ -33,7 +33,10 @@ Blockly.PHP['procedures_defreturn'] = function(block) {
|
||||
// First, add a 'global' statement for every variable that is not shadowed by
|
||||
// a local parameter.
|
||||
var globals = [];
|
||||
for (var i = 0, varName; varName = block.workspace.variableList[i]; i++) {
|
||||
var varName;
|
||||
var variables = workspace.getAllVariables();
|
||||
for (var i = 0, variable; variable = variables[i]; i++) {
|
||||
varName = variable.name;
|
||||
if (block.arguments_.indexOf(varName) == -1) {
|
||||
globals.push(Blockly.PHP.variableDB_.getName(varName,
|
||||
Blockly.Variables.NAME_TYPE));
|
||||
|
||||
@@ -161,9 +161,9 @@ Blockly.Python.init = function(workspace) {
|
||||
}
|
||||
|
||||
var defvars = [];
|
||||
var variables = workspace.variableList;
|
||||
var variables = workspace.getAllVariables();
|
||||
for (var i = 0; i < variables.length; i++) {
|
||||
defvars[i] = Blockly.Python.variableDB_.getName(variables[i],
|
||||
defvars[i] = Blockly.Python.variableDB_.getName(variables[i].name,
|
||||
Blockly.Variables.NAME_TYPE) + ' = None';
|
||||
}
|
||||
Blockly.Python.definitions_['variables'] = defvars.join('\n');
|
||||
|
||||
@@ -34,7 +34,10 @@ Blockly.Python['procedures_defreturn'] = function(block) {
|
||||
// First, add a 'global' statement for every variable that is not shadowed by
|
||||
// a local parameter.
|
||||
var globals = [];
|
||||
for (var i = 0, varName; varName = block.workspace.variableList[i]; i++) {
|
||||
var varName;
|
||||
var variables = workspace.getAllVariables();
|
||||
for (var i = 0, variable; variable = variables[i]; i++) {
|
||||
varName = variable.name;
|
||||
if (block.arguments_.indexOf(varName) == -1) {
|
||||
globals.push(Blockly.Python.variableDB_.getName(varName,
|
||||
Blockly.Variables.NAME_TYPE));
|
||||
|
||||
+18
-19
@@ -9,7 +9,7 @@ Blockly.JavaScript.ORDER_DIVISION=5.1;Blockly.JavaScript.ORDER_MULTIPLICATION=5.
|
||||
Blockly.JavaScript.ORDER_LOGICAL_AND=13;Blockly.JavaScript.ORDER_LOGICAL_OR=14;Blockly.JavaScript.ORDER_CONDITIONAL=15;Blockly.JavaScript.ORDER_ASSIGNMENT=16;Blockly.JavaScript.ORDER_COMMA=17;Blockly.JavaScript.ORDER_NONE=99;
|
||||
Blockly.JavaScript.ORDER_OVERRIDES=[[Blockly.JavaScript.ORDER_FUNCTION_CALL,Blockly.JavaScript.ORDER_MEMBER],[Blockly.JavaScript.ORDER_FUNCTION_CALL,Blockly.JavaScript.ORDER_FUNCTION_CALL],[Blockly.JavaScript.ORDER_MEMBER,Blockly.JavaScript.ORDER_MEMBER],[Blockly.JavaScript.ORDER_MEMBER,Blockly.JavaScript.ORDER_FUNCTION_CALL],[Blockly.JavaScript.ORDER_LOGICAL_NOT,Blockly.JavaScript.ORDER_LOGICAL_NOT],[Blockly.JavaScript.ORDER_MULTIPLICATION,Blockly.JavaScript.ORDER_MULTIPLICATION],[Blockly.JavaScript.ORDER_ADDITION,
|
||||
Blockly.JavaScript.ORDER_ADDITION],[Blockly.JavaScript.ORDER_LOGICAL_AND,Blockly.JavaScript.ORDER_LOGICAL_AND],[Blockly.JavaScript.ORDER_LOGICAL_OR,Blockly.JavaScript.ORDER_LOGICAL_OR]];
|
||||
Blockly.JavaScript.init=function(a){Blockly.JavaScript.definitions_=Object.create(null);Blockly.JavaScript.functionNames_=Object.create(null);Blockly.JavaScript.variableDB_?Blockly.JavaScript.variableDB_.reset():Blockly.JavaScript.variableDB_=new Blockly.Names(Blockly.JavaScript.RESERVED_WORDS_);var b=[];a=a.variableList;if(a.length){for(var c=0;c<a.length;c++)b[c]=Blockly.JavaScript.variableDB_.getName(a[c],Blockly.Variables.NAME_TYPE);Blockly.JavaScript.definitions_.variables="var "+b.join(", ")+
|
||||
Blockly.JavaScript.init=function(a){Blockly.JavaScript.definitions_=Object.create(null);Blockly.JavaScript.functionNames_=Object.create(null);Blockly.JavaScript.variableDB_?Blockly.JavaScript.variableDB_.reset():Blockly.JavaScript.variableDB_=new Blockly.Names(Blockly.JavaScript.RESERVED_WORDS_);var b=[];a=a.getAllVariables();if(a.length){for(var c=0;c<a.length;c++)b[c]=Blockly.JavaScript.variableDB_.getName(a[c].name,Blockly.Variables.NAME_TYPE);Blockly.JavaScript.definitions_.variables="var "+b.join(", ")+
|
||||
";"}};Blockly.JavaScript.finish=function(a){var b=[],c;for(c in Blockly.JavaScript.definitions_)b.push(Blockly.JavaScript.definitions_[c]);delete Blockly.JavaScript.definitions_;delete Blockly.JavaScript.functionNames_;Blockly.JavaScript.variableDB_.reset();return b.join("\n\n")+"\n\n\n"+a};Blockly.JavaScript.scrubNakedValue=function(a){return a+";\n"};Blockly.JavaScript.quote_=function(a){a=a.replace(/\\/g,"\\\\").replace(/\n/g,"\\\n").replace(/'/g,"\\'");return"'"+a+"'"};
|
||||
Blockly.JavaScript.scrub_=function(a,b){var c="";if(!a.outputConnection||!a.outputConnection.targetConnection){var d=a.getCommentText();(d=Blockly.utils.wrap(d,Blockly.JavaScript.COMMENT_WRAP-3))&&(c=a.getProcedureDef?c+("/**\n"+Blockly.JavaScript.prefixLines(d+"\n"," * ")+" */\n"):c+Blockly.JavaScript.prefixLines(d+"\n","// "));for(var e=0;e<a.inputList.length;e++)a.inputList[e].type==Blockly.INPUT_VALUE&&(d=a.inputList[e].connection.targetBlock())&&(d=Blockly.JavaScript.allNestedComments(d))&&(c+=
|
||||
Blockly.JavaScript.prefixLines(d,"// "))}e=a.nextConnection&&a.nextConnection.targetBlock();e=Blockly.JavaScript.blockToCode(e);return c+b+e};
|
||||
@@ -17,7 +17,7 @@ Blockly.JavaScript.getAdjusted=function(a,b,c,d,e){c=c||0;e=e||Blockly.JavaScrip
|
||||
d&&(a=-a);else{if(0<c){a=a+" + "+c;var g=Blockly.JavaScript.ORDER_ADDITION}else 0>c&&(a=a+" - "+-c,g=Blockly.JavaScript.ORDER_SUBTRACTION);d&&(a=c?"-("+a+")":"-"+a,g=Blockly.JavaScript.ORDER_UNARY_NEGATION);g=Math.floor(g);e=Math.floor(e);g&&e>=g&&(a="("+a+")")}return a};Blockly.JavaScript.lists={};Blockly.JavaScript.lists_create_empty=function(a){return["[]",Blockly.JavaScript.ORDER_ATOMIC]};Blockly.JavaScript.lists_create_with=function(a){for(var b=Array(a.itemCount_),c=0;c<a.itemCount_;c++)b[c]=Blockly.JavaScript.valueToCode(a,"ADD"+c,Blockly.JavaScript.ORDER_COMMA)||"null";return["["+b.join(", ")+"]",Blockly.JavaScript.ORDER_ATOMIC]};
|
||||
Blockly.JavaScript.lists_repeat=function(a){var b=Blockly.JavaScript.provideFunction_("listsRepeat",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(value, n) {"," var array = [];"," for (var i = 0; i < n; i++) {"," array[i] = value;"," }"," return array;","}"]),c=Blockly.JavaScript.valueToCode(a,"ITEM",Blockly.JavaScript.ORDER_COMMA)||"null";a=Blockly.JavaScript.valueToCode(a,"NUM",Blockly.JavaScript.ORDER_COMMA)||"0";return[b+"("+c+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.lists_length=function(a){return[(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+".length",Blockly.JavaScript.ORDER_MEMBER]};Blockly.JavaScript.lists_isEmpty=function(a){return["!"+(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+".length",Blockly.JavaScript.ORDER_LOGICAL_NOT]};
|
||||
Blockly.JavaScript.lists_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.JavaScript.valueToCode(a,"FIND",Blockly.JavaScript.ORDER_NONE)||"''",b=(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.JavaScript.ORDER_ADDITION]:[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.lists_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.JavaScript.valueToCode(a,"FIND",Blockly.JavaScript.ORDER_NONE)||"''";b=(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"[]")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.JavaScript.ORDER_ADDITION]:[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.lists_getIndex=function(a){var b=a.getFieldValue("MODE")||"GET",c=a.getFieldValue("WHERE")||"FROM_START";var d=Blockly.JavaScript.valueToCode(a,"VALUE","RANDOM"==c?Blockly.JavaScript.ORDER_COMMA:Blockly.JavaScript.ORDER_MEMBER)||"[]";switch(c){case "FIRST":if("GET"==b)return[d+"[0]",Blockly.JavaScript.ORDER_MEMBER];if("GET_REMOVE"==b)return[d+".shift()",Blockly.JavaScript.ORDER_MEMBER];if("REMOVE"==b)return d+".shift();\n";break;case "LAST":if("GET"==b)return[d+".slice(-1)[0]",
|
||||
Blockly.JavaScript.ORDER_MEMBER];if("GET_REMOVE"==b)return[d+".pop()",Blockly.JavaScript.ORDER_MEMBER];if("REMOVE"==b)return d+".pop();\n";break;case "FROM_START":a=Blockly.JavaScript.getAdjusted(a,"AT");if("GET"==b)return[d+"["+a+"]",Blockly.JavaScript.ORDER_MEMBER];if("GET_REMOVE"==b)return[d+".splice("+a+", 1)[0]",Blockly.JavaScript.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+".splice("+a+", 1);\n";break;case "FROM_END":a=Blockly.JavaScript.getAdjusted(a,"AT",1,!0);if("GET"==b)return[d+".slice("+
|
||||
a+")[0]",Blockly.JavaScript.ORDER_FUNCTION_CALL];if("GET_REMOVE"==b)return[d+".splice("+a+", 1)[0]",Blockly.JavaScript.ORDER_FUNCTION_CALL];if("REMOVE"==b)return d+".splice("+a+", 1);";break;case "RANDOM":d=Blockly.JavaScript.provideFunction_("listsGetRandomItem",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(list, remove) {"," var x = Math.floor(Math.random() * list.length);"," if (remove) {"," return list.splice(x, 1)[0];"," } else {"," return list[x];"," }","}"])+"("+d+
|
||||
@@ -27,14 +27,14 @@ Blockly.JavaScript.lists_setIndex=function(a){function b(){if(c.match(/^\w+$/))r
|
||||
"] = "+f+";\n");if("INSERT"==d)return a+(c+".splice("+c+".length - "+e+", 0, "+f+");\n");break;case "RANDOM":a=b();e=Blockly.JavaScript.variableDB_.getDistinctName("tmpX",Blockly.Variables.NAME_TYPE);a+="var "+e+" = Math.floor(Math.random() * "+c+".length);\n";if("SET"==d)return a+(c+"["+e+"] = "+f+";\n");if("INSERT"==d)return a+(c+".splice("+e+", 0, "+f+");\n")}throw"Unhandled combination (lists_setIndex).";};
|
||||
Blockly.JavaScript.lists.getIndex_=function(a,b,c){return"FIRST"==b?"0":"FROM_END"==b?a+".length - 1 - "+c:"LAST"==b?a+".length - 1":c};
|
||||
Blockly.JavaScript.lists_getSublist=function(a){var b=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_MEMBER)||"[]",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");if("FIRST"==c&&"LAST"==d)var e=b+".slice(0)";else if(b.match(/^\w+$/)||"FROM_END"!=c&&"FROM_START"==d){switch(c){case "FROM_START":e=Blockly.JavaScript.getAdjusted(a,"AT1");break;case "FROM_END":e=Blockly.JavaScript.getAdjusted(a,"AT1",1,!1,Blockly.JavaScript.ORDER_SUBTRACTION);e=b+".length - "+e;break;case "FIRST":e=
|
||||
"0";break;default:throw"Unhandled option (lists_getSublist).";}switch(d){case "FROM_START":a=Blockly.JavaScript.getAdjusted(a,"AT2",1);break;case "FROM_END":a=Blockly.JavaScript.getAdjusted(a,"AT2",0,!1,Blockly.JavaScript.ORDER_SUBTRACTION);a=b+".length - "+a;break;case "LAST":a=b+".length";break;default:throw"Unhandled option (lists_getSublist).";}e=b+".slice("+e+", "+a+")"}else{e=Blockly.JavaScript.getAdjusted(a,"AT1");a=Blockly.JavaScript.getAdjusted(a,"AT2");var f=Blockly.JavaScript.lists.getIndex_;
|
||||
var g={FIRST:"First",LAST:"Last",FROM_START:"FromStart",FROM_END:"FromEnd"};f=Blockly.JavaScript.provideFunction_("subsequence"+g[c]+g[d],["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(sequence"+("FROM_END"==c||"FROM_START"==c?", at1":"")+("FROM_END"==d||"FROM_START"==d?", at2":"")+") {"," var start = "+f("sequence",c,"at1")+";"," var end = "+f("sequence",d,"at2")+" + 1;"," return sequence.slice(start, end);","}"]);e=f+"("+b+("FROM_END"==c||"FROM_START"==c?", "+e:"")+("FROM_END"==
|
||||
d||"FROM_START"==d?", "+a:"")+")"}return[e,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
"0";break;default:throw"Unhandled option (lists_getSublist).";}switch(d){case "FROM_START":a=Blockly.JavaScript.getAdjusted(a,"AT2",1);break;case "FROM_END":a=Blockly.JavaScript.getAdjusted(a,"AT2",0,!1,Blockly.JavaScript.ORDER_SUBTRACTION);a=b+".length - "+a;break;case "LAST":a=b+".length";break;default:throw"Unhandled option (lists_getSublist).";}e=b+".slice("+e+", "+a+")"}else{e=Blockly.JavaScript.getAdjusted(a,"AT1");a=Blockly.JavaScript.getAdjusted(a,"AT2");var f=Blockly.JavaScript.lists.getIndex_,
|
||||
g={FIRST:"First",LAST:"Last",FROM_START:"FromStart",FROM_END:"FromEnd"};e=Blockly.JavaScript.provideFunction_("subsequence"+g[c]+g[d],["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(sequence"+("FROM_END"==c||"FROM_START"==c?", at1":"")+("FROM_END"==d||"FROM_START"==d?", at2":"")+") {"," var start = "+f("sequence",c,"at1")+";"," var end = "+f("sequence",d,"at2")+" + 1;"," return sequence.slice(start, end);","}"])+"("+b+("FROM_END"==c||"FROM_START"==c?", "+e:"")+("FROM_END"==d||"FROM_START"==
|
||||
d?", "+a:"")+")"}return[e,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.lists_sort=function(a){var b=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_FUNCTION_CALL)||"[]",c="1"===a.getFieldValue("DIRECTION")?1:-1;a=a.getFieldValue("TYPE");var d=Blockly.JavaScript.provideFunction_("listsGetSortCompare",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(type, direction) {"," var compareFuncs = {",' "NUMERIC": function(a, b) {'," return parseFloat(a) - parseFloat(b); },",' "TEXT": function(a, b) {'," return a.toString() > b.toString() ? 1 : -1; },",
|
||||
' "IGNORE_CASE": function(a, b) {'," return a.toString().toLowerCase() > b.toString().toLowerCase() ? 1 : -1; },"," };"," var compare = compareFuncs[type];"," return function(a, b) { return compare(a, b) * direction; }","}"]);return[b+".slice().sort("+d+'("'+a+'", '+c+"))",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.lists_split=function(a){var b=Blockly.JavaScript.valueToCode(a,"INPUT",Blockly.JavaScript.ORDER_MEMBER),c=Blockly.JavaScript.valueToCode(a,"DELIM",Blockly.JavaScript.ORDER_NONE)||"''";a=a.getFieldValue("MODE");if("SPLIT"==a)b||(b="''"),a="split";else if("JOIN"==a)b||(b="[]"),a="join";else throw"Unknown mode: "+a;return[b+"."+a+"("+c+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.lists_reverse=function(a){return[(Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_FUNCTION_CALL)||"[]")+".slice().reverse()",Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.math={};Blockly.JavaScript.math_number=function(a){return[parseFloat(a.getFieldValue("NUM")),Blockly.JavaScript.ORDER_ATOMIC]};
|
||||
Blockly.JavaScript.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.JavaScript.ORDER_ADDITION],MINUS:[" - ",Blockly.JavaScript.ORDER_SUBTRACTION],MULTIPLY:[" * ",Blockly.JavaScript.ORDER_MULTIPLICATION],DIVIDE:[" / ",Blockly.JavaScript.ORDER_DIVISION],POWER:[null,Blockly.JavaScript.ORDER_COMMA]}[a.getFieldValue("OP")],c=b[0],b=b[1],d=Blockly.JavaScript.valueToCode(a,"A",b)||"0";a=Blockly.JavaScript.valueToCode(a,"B",b)||"0";return c?[d+c+a,b]:["Math.pow("+d+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.math_arithmetic=function(a){var b={ADD:[" + ",Blockly.JavaScript.ORDER_ADDITION],MINUS:[" - ",Blockly.JavaScript.ORDER_SUBTRACTION],MULTIPLY:[" * ",Blockly.JavaScript.ORDER_MULTIPLICATION],DIVIDE:[" / ",Blockly.JavaScript.ORDER_DIVISION],POWER:[null,Blockly.JavaScript.ORDER_COMMA]}[a.getFieldValue("OP")],c=b[0];b=b[1];var d=Blockly.JavaScript.valueToCode(a,"A",b)||"0";a=Blockly.JavaScript.valueToCode(a,"B",b)||"0";return c?[d+c+a,b]:["Math.pow("+d+", "+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.math_single=function(a){var b=a.getFieldValue("OP");if("NEG"==b)return a=Blockly.JavaScript.valueToCode(a,"NUM",Blockly.JavaScript.ORDER_UNARY_NEGATION)||"0","-"==a[0]&&(a=" "+a),["-"+a,Blockly.JavaScript.ORDER_UNARY_NEGATION];a="SIN"==b||"COS"==b||"TAN"==b?Blockly.JavaScript.valueToCode(a,"NUM",Blockly.JavaScript.ORDER_DIVISION)||"0":Blockly.JavaScript.valueToCode(a,"NUM",Blockly.JavaScript.ORDER_NONE)||"0";switch(b){case "ABS":var c="Math.abs("+a+")";break;case "ROOT":c="Math.sqrt("+
|
||||
a+")";break;case "LN":c="Math.log("+a+")";break;case "EXP":c="Math.exp("+a+")";break;case "POW10":c="Math.pow(10,"+a+")";break;case "ROUND":c="Math.round("+a+")";break;case "ROUNDUP":c="Math.ceil("+a+")";break;case "ROUNDDOWN":c="Math.floor("+a+")";break;case "SIN":c="Math.sin("+a+" / 180 * Math.PI)";break;case "COS":c="Math.cos("+a+" / 180 * Math.PI)";break;case "TAN":c="Math.tan("+a+" / 180 * Math.PI)"}if(c)return[c,Blockly.JavaScript.ORDER_FUNCTION_CALL];switch(b){case "LOG10":c="Math.log("+a+
|
||||
") / Math.log(10)";break;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"Unknown math operator: "+b;}return[c,Blockly.JavaScript.ORDER_DIVISION]};
|
||||
@@ -64,30 +64,29 @@ Blockly.JavaScript.procedures_callreturn=function(a){for(var b=Blockly.JavaScrip
|
||||
Blockly.JavaScript.procedures_callnoreturn=function(a){for(var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("NAME"),Blockly.Procedures.NAME_TYPE),c=[],d=0;d<a.arguments_.length;d++)c[d]=Blockly.JavaScript.valueToCode(a,"ARG"+d,Blockly.JavaScript.ORDER_COMMA)||"null";return b+"("+c.join(", ")+");\n"};
|
||||
Blockly.JavaScript.procedures_ifreturn=function(a){var b="if ("+(Blockly.JavaScript.valueToCode(a,"CONDITION",Blockly.JavaScript.ORDER_NONE)||"false")+") {\n";a.hasReturnValue_?(a=Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_NONE)||"null",b+=" return "+a+";\n"):b+=" return;\n";return b+"}\n"};Blockly.JavaScript.texts={};Blockly.JavaScript.text=function(a){return[Blockly.JavaScript.quote_(a.getFieldValue("TEXT")),Blockly.JavaScript.ORDER_ATOMIC]};
|
||||
Blockly.JavaScript.text_join=function(a){switch(a.itemCount_){case 0:return["''",Blockly.JavaScript.ORDER_ATOMIC];case 1:return["String("+(Blockly.JavaScript.valueToCode(a,"ADD0",Blockly.JavaScript.ORDER_NONE)||"''")+")",Blockly.JavaScript.ORDER_FUNCTION_CALL];case 2:var b=Blockly.JavaScript.valueToCode(a,"ADD0",Blockly.JavaScript.ORDER_NONE)||"''";a=Blockly.JavaScript.valueToCode(a,"ADD1",Blockly.JavaScript.ORDER_NONE)||"''";return["String("+b+") + String("+a+")",Blockly.JavaScript.ORDER_ADDITION];
|
||||
default:for(var b=Array(a.itemCount_),c=0;c<a.itemCount_;c++)b[c]=Blockly.JavaScript.valueToCode(a,"ADD"+c,Blockly.JavaScript.ORDER_COMMA)||"''";a="["+b.join(",")+"].join('')";return[a,Blockly.JavaScript.ORDER_FUNCTION_CALL]}};Blockly.JavaScript.text_append=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);a=Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_NONE)||"''";return b+" = String("+b+") + String("+a+");\n"};
|
||||
default:b=Array(a.itemCount_);for(var c=0;c<a.itemCount_;c++)b[c]=Blockly.JavaScript.valueToCode(a,"ADD"+c,Blockly.JavaScript.ORDER_COMMA)||"''";a="["+b.join(",")+"].join('')";return[a,Blockly.JavaScript.ORDER_FUNCTION_CALL]}};Blockly.JavaScript.text_append=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);a=Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_NONE)||"''";return b+" = String("+b+") + String("+a+");\n"};
|
||||
Blockly.JavaScript.text_length=function(a){return[(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_FUNCTION_CALL)||"''")+".length",Blockly.JavaScript.ORDER_MEMBER]};Blockly.JavaScript.text_isEmpty=function(a){return["!"+(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"''")+".length",Blockly.JavaScript.ORDER_LOGICAL_NOT]};
|
||||
Blockly.JavaScript.text_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.JavaScript.valueToCode(a,"FIND",Blockly.JavaScript.ORDER_NONE)||"''",b=(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"''")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.JavaScript.ORDER_ADDITION]:[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.text_indexOf=function(a){var b="FIRST"==a.getFieldValue("END")?"indexOf":"lastIndexOf",c=Blockly.JavaScript.valueToCode(a,"FIND",Blockly.JavaScript.ORDER_NONE)||"''";b=(Blockly.JavaScript.valueToCode(a,"VALUE",Blockly.JavaScript.ORDER_MEMBER)||"''")+"."+b+"("+c+")";return a.workspace.options.oneBasedIndex?[b+" + 1",Blockly.JavaScript.ORDER_ADDITION]:[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.text_charAt=function(a){var b=a.getFieldValue("WHERE")||"FROM_START",c=Blockly.JavaScript.valueToCode(a,"VALUE","RANDOM"==b?Blockly.JavaScript.ORDER_NONE:Blockly.JavaScript.ORDER_MEMBER)||"''";switch(b){case "FIRST":return[c+".charAt(0)",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "LAST":return[c+".slice(-1)",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "FROM_START":return a=Blockly.JavaScript.getAdjusted(a,"AT"),[c+".charAt("+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "FROM_END":return a=
|
||||
Blockly.JavaScript.getAdjusted(a,"AT",1,!0),[c+".slice("+a+").charAt(0)",Blockly.JavaScript.ORDER_FUNCTION_CALL];case "RANDOM":return[Blockly.JavaScript.provideFunction_("textRandomLetter",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(text) {"," var x = Math.floor(Math.random() * text.length);"," return text[x];","}"])+"("+c+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]}throw"Unhandled option (text_charAt).";};
|
||||
Blockly.JavaScript.text.getIndex_=function(a,b,c){return"FIRST"==b?"0":"FROM_END"==b?a+".length - 1 - "+c:"LAST"==b?a+".length - 1":c};
|
||||
Blockly.JavaScript.text_getSubstring=function(a){var b=Blockly.JavaScript.valueToCode(a,"STRING",Blockly.JavaScript.ORDER_FUNCTION_CALL)||"''",c=a.getFieldValue("WHERE1"),d=a.getFieldValue("WHERE2");if("FIRST"==c&&"LAST"==d)var e=b;else if(b.match(/^'?\w+'?$/)||"FROM_END"!=c&&"LAST"!=c&&"FROM_END"!=d&&"LAST"!=d){switch(c){case "FROM_START":e=Blockly.JavaScript.getAdjusted(a,"AT1");break;case "FROM_END":e=Blockly.JavaScript.getAdjusted(a,"AT1",1,!1,Blockly.JavaScript.ORDER_SUBTRACTION);e=b+".length - "+
|
||||
e;break;case "FIRST":e="0";break;default:throw"Unhandled option (text_getSubstring).";}switch(d){case "FROM_START":a=Blockly.JavaScript.getAdjusted(a,"AT2",1);break;case "FROM_END":a=Blockly.JavaScript.getAdjusted(a,"AT2",0,!1,Blockly.JavaScript.ORDER_SUBTRACTION);a=b+".length - "+a;break;case "LAST":a=b+".length";break;default:throw"Unhandled option (text_getSubstring).";}e=b+".slice("+e+", "+a+")"}else{e=Blockly.JavaScript.getAdjusted(a,"AT1");a=Blockly.JavaScript.getAdjusted(a,"AT2");var f=Blockly.JavaScript.text.getIndex_;
|
||||
var g={FIRST:"First",LAST:"Last",FROM_START:"FromStart",FROM_END:"FromEnd"};f=Blockly.JavaScript.provideFunction_("subsequence"+g[c]+g[d],["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(sequence"+("FROM_END"==c||"FROM_START"==c?", at1":"")+("FROM_END"==d||"FROM_START"==d?", at2":"")+") {"," var start = "+f("sequence",c,"at1")+";"," var end = "+f("sequence",d,"at2")+" + 1;"," return sequence.slice(start, end);","}"]);e=f+"("+b+("FROM_END"==c||"FROM_START"==c?", "+e:"")+("FROM_END"==
|
||||
d||"FROM_START"==d?", "+a:"")+")"}return[e,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.text_changeCase=function(a){var b={UPPERCASE:".toUpperCase()",LOWERCASE:".toLowerCase()",TITLECASE:null}[a.getFieldValue("CASE")];a=Blockly.JavaScript.valueToCode(a,"TEXT",b?Blockly.JavaScript.ORDER_MEMBER:Blockly.JavaScript.ORDER_NONE)||"''";b?b=a+b:(b=Blockly.JavaScript.provideFunction_("textToTitleCase",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(str) {"," return str.replace(/\\S+/g,"," function(txt) {return txt[0].toUpperCase() + txt.substring(1).toLowerCase();});",
|
||||
"}"]),b=b+"("+a+")");return[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.text_trim=function(a){var b={LEFT:".replace(/^[\\s\\xa0]+/, '')",RIGHT:".replace(/[\\s\\xa0]+$/, '')",BOTH:".trim()"}[a.getFieldValue("MODE")];return[(Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_MEMBER)||"''")+b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.text_print=function(a){return"window.alert("+(Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_NONE)||"''")+");\n"};Blockly.JavaScript.text_prompt_ext=function(a){var b="window.prompt("+(a.getField("TEXT")?Blockly.JavaScript.quote_(a.getFieldValue("TEXT")):Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_NONE)||"''")+")";"NUMBER"==a.getFieldValue("TYPE")&&(b="parseFloat("+b+")");return[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.text_prompt=Blockly.JavaScript.text_prompt_ext;
|
||||
e;break;case "FIRST":e="0";break;default:throw"Unhandled option (text_getSubstring).";}switch(d){case "FROM_START":a=Blockly.JavaScript.getAdjusted(a,"AT2",1);break;case "FROM_END":a=Blockly.JavaScript.getAdjusted(a,"AT2",0,!1,Blockly.JavaScript.ORDER_SUBTRACTION);a=b+".length - "+a;break;case "LAST":a=b+".length";break;default:throw"Unhandled option (text_getSubstring).";}e=b+".slice("+e+", "+a+")"}else{e=Blockly.JavaScript.getAdjusted(a,"AT1");a=Blockly.JavaScript.getAdjusted(a,"AT2");var f=Blockly.JavaScript.text.getIndex_,
|
||||
g={FIRST:"First",LAST:"Last",FROM_START:"FromStart",FROM_END:"FromEnd"};e=Blockly.JavaScript.provideFunction_("subsequence"+g[c]+g[d],["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(sequence"+("FROM_END"==c||"FROM_START"==c?", at1":"")+("FROM_END"==d||"FROM_START"==d?", at2":"")+") {"," var start = "+f("sequence",c,"at1")+";"," var end = "+f("sequence",d,"at2")+" + 1;"," return sequence.slice(start, end);","}"])+"("+b+("FROM_END"==c||"FROM_START"==c?", "+e:"")+("FROM_END"==d||"FROM_START"==
|
||||
d?", "+a:"")+")"}return[e,Blockly.JavaScript.ORDER_FUNCTION_CALL]};
|
||||
Blockly.JavaScript.text_changeCase=function(a){var b={UPPERCASE:".toUpperCase()",LOWERCASE:".toLowerCase()",TITLECASE:null}[a.getFieldValue("CASE")];a=Blockly.JavaScript.valueToCode(a,"TEXT",b?Blockly.JavaScript.ORDER_MEMBER:Blockly.JavaScript.ORDER_NONE)||"''";return[b?a+b:Blockly.JavaScript.provideFunction_("textToTitleCase",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(str) {"," return str.replace(/\\S+/g,"," function(txt) {return txt[0].toUpperCase() + txt.substring(1).toLowerCase();});","}"])+
|
||||
"("+a+")",Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.text_trim=function(a){var b={LEFT:".replace(/^[\\s\\xa0]+/, '')",RIGHT:".replace(/[\\s\\xa0]+$/, '')",BOTH:".trim()"}[a.getFieldValue("MODE")];return[(Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_MEMBER)||"''")+b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.text_print=function(a){return"window.alert("+(Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_NONE)||"''")+");\n"};
|
||||
Blockly.JavaScript.text_prompt_ext=function(a){var b="window.prompt("+(a.getField("TEXT")?Blockly.JavaScript.quote_(a.getFieldValue("TEXT")):Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_NONE)||"''")+")";"NUMBER"==a.getFieldValue("TYPE")&&(b="parseFloat("+b+")");return[b,Blockly.JavaScript.ORDER_FUNCTION_CALL]};Blockly.JavaScript.text_prompt=Blockly.JavaScript.text_prompt_ext;
|
||||
Blockly.JavaScript.text_count=function(a){var b=Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_MEMBER)||"''";a=Blockly.JavaScript.valueToCode(a,"SUB",Blockly.JavaScript.ORDER_NONE)||"''";return[Blockly.JavaScript.provideFunction_("textCount",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(haystack, needle) {"," if (needle.length === 0) {"," return haystack.length + 1;"," } else {"," return haystack.split(needle).length - 1;"," }","}"])+"("+b+", "+a+")",Blockly.JavaScript.ORDER_SUBTRACTION]};
|
||||
Blockly.JavaScript.text_replace=function(a){var b=Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_MEMBER)||"''",c=Blockly.JavaScript.valueToCode(a,"FROM",Blockly.JavaScript.ORDER_NONE)||"''";a=Blockly.JavaScript.valueToCode(a,"TO",Blockly.JavaScript.ORDER_NONE)||"''";return[Blockly.JavaScript.provideFunction_("textReplace",["function "+Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_+"(haystack, needle, replacement) {",' needle = needle.replace(/([-()\\[\\]{}+?*.$\\^|,:#<!\\\\])/g,"\\\\$1")',
|
||||
' .replace(/\\x08/g,"\\\\x08");'," return haystack.replace(new RegExp(needle, 'g'), replacement);","}"])+"("+b+", "+c+", "+a+")",Blockly.JavaScript.ORDER_MEMBER]};Blockly.JavaScript.text_reverse=function(a){return[(Blockly.JavaScript.valueToCode(a,"TEXT",Blockly.JavaScript.ORDER_MEMBER)||"''")+".split('').reverse().join('')",Blockly.JavaScript.ORDER_MEMBER]};Blockly.JavaScript.loops={};
|
||||
Blockly.JavaScript.controls_repeat_ext=function(a){var b=a.getField("TIMES")?String(Number(a.getFieldValue("TIMES"))):Blockly.JavaScript.valueToCode(a,"TIMES",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0";var c=Blockly.JavaScript.statementToCode(a,"DO"),c=Blockly.JavaScript.addLoopTrap(c,a.id),d="",e=Blockly.JavaScript.variableDB_.getDistinctName("count",Blockly.Variables.NAME_TYPE);a=b;b.match(/^\w+$/)||Blockly.isNumber(b)||(a=Blockly.JavaScript.variableDB_.getDistinctName("repeat_end",Blockly.Variables.NAME_TYPE),
|
||||
Blockly.JavaScript.controls_repeat_ext=function(a){var b=a.getField("TIMES")?String(Number(a.getFieldValue("TIMES"))):Blockly.JavaScript.valueToCode(a,"TIMES",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0";var c=Blockly.JavaScript.statementToCode(a,"DO");c=Blockly.JavaScript.addLoopTrap(c,a.id);var d="",e=Blockly.JavaScript.variableDB_.getDistinctName("count",Blockly.Variables.NAME_TYPE);a=b;b.match(/^\w+$/)||Blockly.isNumber(b)||(a=Blockly.JavaScript.variableDB_.getDistinctName("repeat_end",Blockly.Variables.NAME_TYPE),
|
||||
d+="var "+a+" = "+b+";\n");return d+("for (var "+e+" = 0; "+e+" < "+a+"; "+e+"++) {\n"+c+"}\n")};Blockly.JavaScript.controls_repeat=Blockly.JavaScript.controls_repeat_ext;
|
||||
Blockly.JavaScript.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.JavaScript.valueToCode(a,"BOOL",b?Blockly.JavaScript.ORDER_LOGICAL_NOT:Blockly.JavaScript.ORDER_NONE)||"false",d=Blockly.JavaScript.statementToCode(a,"DO"),d=Blockly.JavaScript.addLoopTrap(d,a.id);b&&(c="!"+c);return"while ("+c+") {\n"+d+"}\n"};
|
||||
Blockly.JavaScript.controls_for=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);var c=Blockly.JavaScript.valueToCode(a,"FROM",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0";var d=Blockly.JavaScript.valueToCode(a,"TO",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0",e=Blockly.JavaScript.valueToCode(a,"BY",Blockly.JavaScript.ORDER_ASSIGNMENT)||"1",f=Blockly.JavaScript.statementToCode(a,"DO"),f=Blockly.JavaScript.addLoopTrap(f,a.id);if(Blockly.isNumber(c)&&
|
||||
Blockly.JavaScript.controls_whileUntil=function(a){var b="UNTIL"==a.getFieldValue("MODE"),c=Blockly.JavaScript.valueToCode(a,"BOOL",b?Blockly.JavaScript.ORDER_LOGICAL_NOT:Blockly.JavaScript.ORDER_NONE)||"false",d=Blockly.JavaScript.statementToCode(a,"DO");d=Blockly.JavaScript.addLoopTrap(d,a.id);b&&(c="!"+c);return"while ("+c+") {\n"+d+"}\n"};
|
||||
Blockly.JavaScript.controls_for=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE);var c=Blockly.JavaScript.valueToCode(a,"FROM",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0";var d=Blockly.JavaScript.valueToCode(a,"TO",Blockly.JavaScript.ORDER_ASSIGNMENT)||"0",e=Blockly.JavaScript.valueToCode(a,"BY",Blockly.JavaScript.ORDER_ASSIGNMENT)||"1",f=Blockly.JavaScript.statementToCode(a,"DO");f=Blockly.JavaScript.addLoopTrap(f,a.id);if(Blockly.isNumber(c)&&
|
||||
Blockly.isNumber(d)&&Blockly.isNumber(e)){var g=parseFloat(c)<=parseFloat(d);a="for ("+b+" = "+c+"; "+b+(g?" <= ":" >= ")+d+"; "+b;b=Math.abs(parseFloat(e));a=(1==b?a+(g?"++":"--"):a+((g?" += ":" -= ")+b))+(") {\n"+f+"}\n")}else a="",g=c,c.match(/^\w+$/)||Blockly.isNumber(c)||(g=Blockly.JavaScript.variableDB_.getDistinctName(b+"_start",Blockly.Variables.NAME_TYPE),a+="var "+g+" = "+c+";\n"),c=d,d.match(/^\w+$/)||Blockly.isNumber(d)||(c=Blockly.JavaScript.variableDB_.getDistinctName(b+"_end",Blockly.Variables.NAME_TYPE),
|
||||
a+="var "+c+" = "+d+";\n"),d=Blockly.JavaScript.variableDB_.getDistinctName(b+"_inc",Blockly.Variables.NAME_TYPE),a+="var "+d+" = ",a=Blockly.isNumber(e)?a+(Math.abs(e)+";\n"):a+("Math.abs("+e+");\n"),a=a+("if ("+g+" > "+c+") {\n")+(Blockly.JavaScript.INDENT+d+" = -"+d+";\n"),a+="}\n",a+="for ("+b+" = "+g+"; "+d+" >= 0 ? "+b+" <= "+c+" : "+b+" >= "+c+"; "+b+" += "+d+") {\n"+f+"}\n";return a};
|
||||
Blockly.JavaScript.controls_forEach=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),c=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_ASSIGNMENT)||"[]",d=Blockly.JavaScript.statementToCode(a,"DO"),d=Blockly.JavaScript.addLoopTrap(d,a.id);a="";var e=c;c.match(/^\w+$/)||(e=Blockly.JavaScript.variableDB_.getDistinctName(b+"_list",Blockly.Variables.NAME_TYPE),a+="var "+e+" = "+c+";\n");c=Blockly.JavaScript.variableDB_.getDistinctName(b+
|
||||
Blockly.JavaScript.controls_forEach=function(a){var b=Blockly.JavaScript.variableDB_.getName(a.getFieldValue("VAR"),Blockly.Variables.NAME_TYPE),c=Blockly.JavaScript.valueToCode(a,"LIST",Blockly.JavaScript.ORDER_ASSIGNMENT)||"[]",d=Blockly.JavaScript.statementToCode(a,"DO");d=Blockly.JavaScript.addLoopTrap(d,a.id);a="";var e=c;c.match(/^\w+$/)||(e=Blockly.JavaScript.variableDB_.getDistinctName(b+"_list",Blockly.Variables.NAME_TYPE),a+="var "+e+" = "+c+";\n");c=Blockly.JavaScript.variableDB_.getDistinctName(b+
|
||||
"_index",Blockly.Variables.NAME_TYPE);d=Blockly.JavaScript.INDENT+b+" = "+e+"["+c+"];\n"+d;return a+("for (var "+c+" in "+e+") {\n"+d+"}\n")};Blockly.JavaScript.controls_flow_statements=function(a){switch(a.getFieldValue("FLOW")){case "BREAK":return"break;\n";case "CONTINUE":return"continue;\n"}throw"Unknown flow statement.";};Blockly.JavaScript.logic={};Blockly.JavaScript.controls_if=function(a){var b=0,c="";do{var d=Blockly.JavaScript.valueToCode(a,"IF"+b,Blockly.JavaScript.ORDER_NONE)||"false";var e=Blockly.JavaScript.statementToCode(a,"DO"+b);c+=(0<b?" else ":"")+"if ("+d+") {\n"+e+"}";++b}while(a.getInput("IF"+b));a.getInput("ELSE")&&(e=Blockly.JavaScript.statementToCode(a,"ELSE"),c+=" else {\n"+e+"}");return c+"\n"};Blockly.JavaScript.controls_ifelse=Blockly.JavaScript.controls_if;
|
||||
Blockly.JavaScript.logic_compare=function(a){var b={EQ:"==",NEQ:"!=",LT:"<",LTE:"<=",GT:">",GTE:">="}[a.getFieldValue("OP")],c="=="==b||"!="==b?Blockly.JavaScript.ORDER_EQUALITY:Blockly.JavaScript.ORDER_RELATIONAL,d=Blockly.JavaScript.valueToCode(a,"A",c)||"0";a=Blockly.JavaScript.valueToCode(a,"B",c)||"0";return[d+" "+b+" "+a,c]};
|
||||
Blockly.JavaScript.logic_operation=function(a){var b="AND"==a.getFieldValue("OP")?"&&":"||",c="&&"==b?Blockly.JavaScript.ORDER_LOGICAL_AND:Blockly.JavaScript.ORDER_LOGICAL_OR,d=Blockly.JavaScript.valueToCode(a,"A",c);a=Blockly.JavaScript.valueToCode(a,"B",c);if(d||a){var e="&&"==b?"true":"false";d||(d=e);a||(a=e)}else a=d="false";return[d+" "+b+" "+a,c]};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user