mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 06:23:37 -05:00
Merge branch 'develop' of https://github.com/LLK/scratch-vm into sound
# Conflicts: # playground/index.html
This commit is contained in:
commit
7bd0142deb
25 changed files with 828 additions and 831 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -11,6 +11,7 @@ npm-*
|
|||
/dist.js
|
||||
/vm.js
|
||||
/vm.min.js
|
||||
/playground/assets
|
||||
/playground/media
|
||||
/playground/vendor.js
|
||||
/playground/vm.js
|
||||
|
|
10
.travis.yml
10
.travis.yml
|
@ -1,14 +1,14 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- "4"
|
||||
- "stable"
|
||||
- '4'
|
||||
- stable
|
||||
sudo: false
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
before_install:
|
||||
# Install the most up to date scratch-* dependencies
|
||||
- rm -rf node_modules/scratch-*
|
||||
install:
|
||||
- npm install
|
||||
- npm update
|
||||
after_script:
|
||||
- |
|
||||
# RELEASE_BRANCHES and NPM_TOKEN defined in Travis settings panel
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
"main": "./dist.js",
|
||||
"scripts": {
|
||||
"prepublish": "./node_modules/.bin/webpack --bail",
|
||||
"start": "make serve",
|
||||
"start": "webpack-dev-server",
|
||||
"build": "webpack --colors --progress",
|
||||
"test": "make test",
|
||||
"version": "./node_modules/.bin/json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\""
|
||||
},
|
||||
|
|
|
@ -51,751 +51,6 @@
|
|||
|
||||
<div id="blocks"></div>
|
||||
|
||||
<xml id="toolbox" style="display: none">
|
||||
<category name="Motion" colour="#4C97FF">
|
||||
<block type="motion_movesteps">
|
||||
<value name="STEPS">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_turnright">
|
||||
<value name="DEGREES">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">15</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_turnleft">
|
||||
<value name="DEGREES">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">15</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_pointindirection">
|
||||
<value name="DIRECTION">
|
||||
<shadow type="math_angle">
|
||||
<field name="NUM">90</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_pointtowards">
|
||||
<value name="TOWARDS">
|
||||
<shadow type="motion_pointtowards_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_gotoxy">
|
||||
<value name="X">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_goto">
|
||||
<value name="TO">
|
||||
<shadow type="motion_goto_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_glidesecstoxy">
|
||||
<value name="SECS">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="X">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_changexby">
|
||||
<value name="DX">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_setx">
|
||||
<value name="X">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_changeyby">
|
||||
<value name="DY">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_sety">
|
||||
<value name="Y">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_ifonedgebounce"></block>
|
||||
<block type="motion_setrotationstyle">
|
||||
<value name="STYLE">
|
||||
<shadow type="motion_setrotationstyle_menu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="motion_xposition"></block>
|
||||
<block type="motion_yposition"></block>
|
||||
<block type="motion_direction"></block>
|
||||
</category>
|
||||
<category name="Looks" colour="#9966FF">
|
||||
<block type="looks_sayforsecs">
|
||||
<value name="MESSAGE">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">Hello!</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SECS">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">2</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_say">
|
||||
<value name="MESSAGE">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">Hello!</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_thinkforsecs">
|
||||
<value name="MESSAGE">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">Hmm...</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="SECS">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">2</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_think">
|
||||
<value name="MESSAGE">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">Hmm...</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_show"></block>
|
||||
<block type="looks_hide"></block>
|
||||
<block type="looks_switchcostumeto">
|
||||
<value name="COSTUME">
|
||||
<shadow type="looks_costume"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_nextcostume"></block>
|
||||
<block type="looks_nextbackdrop"></block>
|
||||
<block type="looks_switchbackdropto">
|
||||
<value name="BACKDROP">
|
||||
<shadow type="looks_backdrops"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_switchbackdroptoandwait">
|
||||
<value name="BACKDROP">
|
||||
<shadow type="looks_backdrops"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_changeeffectby">
|
||||
<value name="EFFECT">
|
||||
<shadow type="looks_effectmenu"></shadow>
|
||||
</value>
|
||||
<value name="CHANGE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_seteffectto">
|
||||
<value name="EFFECT">
|
||||
<shadow type="looks_effectmenu"></shadow>
|
||||
</value>
|
||||
<value name="VALUE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_cleargraphiceffects"></block>
|
||||
<block type="looks_changesizeby">
|
||||
<value name="CHANGE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_setsizeto">
|
||||
<value name="SIZE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">100</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_gotofront"></block>
|
||||
<block type="looks_gobacklayers">
|
||||
<value name="NUM">
|
||||
<shadow type="math_integer">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="looks_costumeorder"></block>
|
||||
<block type="looks_backdroporder"></block>
|
||||
<block type="looks_backdropname"></block>
|
||||
<block type="looks_size"></block>
|
||||
</category>
|
||||
|
||||
<category name="Sound" colour="#D65CD6">
|
||||
|
||||
<block type="sound_play">
|
||||
<value name="SOUND_MENU">
|
||||
<shadow type="sound_sounds_option">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_playuntildone">
|
||||
<value name="SOUND_MENU">
|
||||
<shadow type="sound_sounds_option">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_stopallsounds"></block>
|
||||
|
||||
<block type="sound_playdrumforbeats">
|
||||
<value name="DRUMTYPE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="BEATS">
|
||||
<shadow type="sound_beats_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_restforbeats">
|
||||
<value name="BEATS">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0.25</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_playnoteforbeats">
|
||||
<value name="NOTE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">60</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="BEATS">
|
||||
<shadow type="sound_beats_menu">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_seteffectto">
|
||||
<value name="EFFECT">
|
||||
<shadow type="sound_effects_menu"></shadow>
|
||||
</value>
|
||||
<value name="VALUE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">100</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_changeeffectby">
|
||||
<value name="EFFECT">
|
||||
<shadow type="sound_effects_menu"></shadow>
|
||||
</value>
|
||||
<value name="VALUE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
|
||||
<block type="sound_cleareffects"></block>
|
||||
|
||||
<block type="sound_setinstrumentto">
|
||||
<value name="INSTRUMENT">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sound_changevolumeby">
|
||||
<value name="VOLUME">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">-10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sound_setvolumeto">
|
||||
<value name="VOLUME">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">100</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sound_volume"></block>
|
||||
<block type="sound_changetempoby">
|
||||
<value name="TEMPO">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">20</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sound_settempotobpm">
|
||||
<value name="TEMPO">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">60</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sound_tempo"></block>
|
||||
</category>
|
||||
|
||||
<category name="Pen" colour="#00B295">
|
||||
<block type="pen_clear"></block>
|
||||
<block type="pen_stamp"></block>
|
||||
<block type="pen_pendown"></block>
|
||||
<block type="pen_penup"></block>
|
||||
<block type="pen_setpencolortocolor">
|
||||
<value name="COLOR">
|
||||
<shadow type="colour_picker">
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="pen_changepencolorby">
|
||||
<value name="COLOR">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="pen_setpencolortonum">
|
||||
<value name="COLOR">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="pen_changepenshadeby">
|
||||
<value name="SHADE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="pen_setpenshadeto">
|
||||
<value name="SHADE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">50</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="pen_changepensizeby">
|
||||
<value name="SIZE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="pen_setpensizeto">
|
||||
<value name="SIZE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<category name="Data" colour="#FF8C1A" custom="VARIABLE">
|
||||
</category>
|
||||
<category name="Lists" colour="#FF8C1A">
|
||||
<block type="data_listcontents"></block>
|
||||
<block type="data_addtolist">
|
||||
<value name="ITEM">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">thing</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="data_deleteoflist">
|
||||
<value name="INDEX">
|
||||
<shadow type="math_integer">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="data_insertatlist">
|
||||
<value name="INDEX">
|
||||
<shadow type="math_integer">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ITEM">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">thing</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="data_replaceitemoflist">
|
||||
<value name="INDEX">
|
||||
<shadow type="math_integer">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="ITEM">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">thing</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="data_itemoflist">
|
||||
<value name="INDEX">
|
||||
<shadow type="math_integer">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="data_lengthoflist"></block>
|
||||
<block type="data_listcontainsitem">
|
||||
<value name="ITEM">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">thing</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="data_showlist"></block>
|
||||
<block type="data_hidelist"></block>
|
||||
</category>
|
||||
<category name="Events" colour="#FFD500">
|
||||
<block type="event_whenflagclicked"></block>
|
||||
<block type="event_whenkeypressed">
|
||||
</block>
|
||||
<block type="event_whenthisspriteclicked"></block>
|
||||
<block type="event_whenbackdropswitchesto">
|
||||
</block>
|
||||
<block type="event_whengreaterthan">
|
||||
<value name="VALUE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="event_whenbroadcastreceived">
|
||||
</block>
|
||||
<block type="event_broadcast">
|
||||
<value name="BROADCAST_OPTION">
|
||||
<shadow type="event_broadcast_menu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="event_broadcastandwait">
|
||||
<value name="BROADCAST_OPTION">
|
||||
<shadow type="event_broadcast_menu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<category name="Control" colour="#FFAB19">
|
||||
<block type="control_wait">
|
||||
<value name="DURATION">
|
||||
<shadow type="math_positive_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="control_repeat">
|
||||
<value name="TIMES">
|
||||
<shadow type="math_whole_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="control_forever"></block>
|
||||
<block type="control_if"></block>
|
||||
<block type="control_if_else"></block>
|
||||
<block type="control_wait_until"></block>
|
||||
<block type="control_repeat_until"></block>
|
||||
<block type="control_stop">
|
||||
<value name="STOP_OPTION">
|
||||
<shadow type="control_stop_menu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="control_start_as_clone"></block>
|
||||
<block type="control_create_clone_of">
|
||||
<value name="CLONE_OPTION">
|
||||
<shadow type="control_create_clone_of_menu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="control_delete_this_clone"></block>
|
||||
</category>
|
||||
<category name="Sensing" colour="#4CBFE6">
|
||||
<block type="sensing_touchingobject">
|
||||
<value name="TOUCHINGOBJECTMENU">
|
||||
<shadow type="sensing_touchingobjectmenu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_touchingcolor">
|
||||
<value name="COLOR">
|
||||
<shadow type="colour_picker"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_coloristouchingcolor">
|
||||
<value name="COLOR">
|
||||
<shadow type="colour_picker"></shadow>
|
||||
</value>
|
||||
<value name="COLOR2">
|
||||
<shadow type="colour_picker"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_distanceto">
|
||||
<value name="DISTANCETOMENU">
|
||||
<shadow type="sensing_distancetomenu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_askandwait">
|
||||
<value name="QUESTION">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">What's your name?</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_answer"></block>
|
||||
<block type="sensing_keypressed">
|
||||
<value name="KEY_OPTION">
|
||||
<shadow type="sensing_keyoptions"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_mousedown"></block>
|
||||
<block type="sensing_mousex"></block>
|
||||
<block type="sensing_mousey"></block>
|
||||
<block type="sensing_loudness"></block>
|
||||
<block type="sensing_videoon">
|
||||
<value name="VIDEOONMENU1">
|
||||
<shadow type="sensing_videoonmenuone"></shadow>
|
||||
</value>
|
||||
<value name="VIDEOONMENU2">
|
||||
<shadow type="sensing_videoonmenutwo"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_videotoggle">
|
||||
<value name="VIDEOTOGGLEMENU">
|
||||
<shadow type="sensing_videotogglemenu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_setvideotransparency">
|
||||
<value name="TRANSPARENCY">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">50</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_timer"></block>
|
||||
<block type="sensing_resettimer"></block>
|
||||
<block type="sensing_of">
|
||||
<value name="PROPERTY">
|
||||
<shadow type="sensing_of_property_menu"></shadow>
|
||||
</value>
|
||||
<value name="OBJECT">
|
||||
<shadow type="sensing_of_object_menu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_current">
|
||||
<value name="CURRENTMENU">
|
||||
<shadow type="sensing_currentmenu"></shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="sensing_dayssince2000"></block>
|
||||
<block type="sensing_username"></block>
|
||||
</category>
|
||||
<category name="Operators" colour="#40BF4A">
|
||||
<block type="operator_add">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_subtract">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_multiply">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_divide">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_random">
|
||||
<value name="FROM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="TO">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_lt">
|
||||
<value name="OPERAND1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="OPERAND2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_equals">
|
||||
<value name="OPERAND1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="OPERAND2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_gt">
|
||||
<value name="OPERAND1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="OPERAND2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_and"></block>
|
||||
<block type="operator_or"></block>
|
||||
<block type="operator_not"></block>
|
||||
<block type="operator_join">
|
||||
<value name="STRING1">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">hello</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="STRING2">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">world</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_letter_of">
|
||||
<value name="LETTER">
|
||||
<shadow type="math_whole_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="STRING">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">world</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_length">
|
||||
<value name="STRING">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">world</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_mod">
|
||||
<value name="NUM1">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="NUM2">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_round">
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="operator_mathop">
|
||||
<value name="OPERATOR">
|
||||
<shadow type="operator_mathop_menu"></shadow>
|
||||
</value>
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM"></field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<category name="More Blocks" colour="#FF6680" custom="PROCEDURE"></category>
|
||||
</xml>
|
||||
<!-- FPS counter, Syntax highlighter, Blocks, Renderer -->
|
||||
<script src="./vendor.js"></script>
|
||||
<!-- VM Worker -->
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
var NEW_PROJECT_HASH = 'createEmptyProject';
|
||||
|
||||
var loadProject = function () {
|
||||
var id = location.hash.substring(1);
|
||||
if (id.length < 1) {
|
||||
if (id === NEW_PROJECT_HASH) {
|
||||
window.vm.createEmptyProject();
|
||||
return;
|
||||
}
|
||||
if (id.length < 1 || !isFinite(id)) {
|
||||
id = '119615668';
|
||||
}
|
||||
var url = 'https://projects.scratch.mit.edu/internalapi/project/' +
|
||||
|
@ -32,7 +38,7 @@ window.onload = function() {
|
|||
};
|
||||
document.getElementById('createEmptyProject').addEventListener('click',
|
||||
function() {
|
||||
document.location = '#' + 'createEmptyProject';
|
||||
document.location = '#' + NEW_PROJECT_HASH;
|
||||
location.reload();
|
||||
});
|
||||
loadProject();
|
||||
|
@ -47,9 +53,7 @@ window.onload = function() {
|
|||
window.audioEngine = new window.AudioEngine();
|
||||
|
||||
// Instantiate scratch-blocks and attach it to the DOM.
|
||||
var toolbox = document.getElementById('toolbox');
|
||||
var workspace = window.Blockly.inject('blocks', {
|
||||
toolbox: toolbox,
|
||||
media: './media/',
|
||||
zoom: {
|
||||
controls: true,
|
||||
|
@ -70,8 +74,9 @@ window.onload = function() {
|
|||
window.workspace = workspace;
|
||||
|
||||
// Attach scratch-blocks events to VM.
|
||||
// @todo: Re-enable flyout listening after fixing GH-69.
|
||||
workspace.addChangeListener(vm.blockListener);
|
||||
var flyoutWorkspace = workspace.getFlyout().getWorkspace();
|
||||
flyoutWorkspace.addChangeListener(vm.flyoutBlockListener);
|
||||
|
||||
// Create FPS counter.
|
||||
var stats = new window.Stats();
|
||||
|
@ -116,11 +121,9 @@ window.onload = function() {
|
|||
|
||||
// Receipt of new block XML for the selected target.
|
||||
vm.on('workspaceUpdate', function (data) {
|
||||
window.Blockly.Events.disable();
|
||||
workspace.clear();
|
||||
var dom = window.Blockly.Xml.textToDom(data.xml);
|
||||
window.Blockly.Xml.domToWorkspace(dom, workspace);
|
||||
window.Blockly.Events.enable();
|
||||
});
|
||||
|
||||
// Receipt of new list of targets, selected target update.
|
||||
|
|
|
@ -133,9 +133,16 @@ Scratch3ControlBlocks.prototype.ifElse = function(args, util) {
|
|||
}
|
||||
};
|
||||
|
||||
Scratch3ControlBlocks.prototype.stop = function() {
|
||||
// @todo - don't use this.runtime
|
||||
this.runtime.stopAll();
|
||||
Scratch3ControlBlocks.prototype.stop = function(args, util) {
|
||||
var option = args.STOP_OPTION;
|
||||
if (option == 'all') {
|
||||
util.stopAll();
|
||||
} else if (option == 'other scripts in sprite' ||
|
||||
option == 'other scripts in stage') {
|
||||
util.stopOtherTargetThreads();
|
||||
} else if (option == 'this script') {
|
||||
util.stopThread();
|
||||
}
|
||||
};
|
||||
|
||||
// @todo (GH-146): remove.
|
||||
|
@ -160,6 +167,7 @@ Scratch3ControlBlocks.prototype.createClone = function (args, util) {
|
|||
};
|
||||
|
||||
Scratch3ControlBlocks.prototype.deleteClone = function (args, util) {
|
||||
if (util.target.isOriginal) return;
|
||||
this.runtime.disposeTarget(util.target);
|
||||
this.runtime.stopForTarget(util.target);
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ Scratch3DataBlocks.prototype.getPrimitives = function () {
|
|||
'data_variable': this.getVariable,
|
||||
'data_setvariableto': this.setVariableTo,
|
||||
'data_changevariableby': this.changeVariableBy,
|
||||
'data_list': this.getListContents,
|
||||
'data_listcontents': this.getListContents,
|
||||
'data_addtolist': this.addToList,
|
||||
'data_deleteoflist': this.deleteOfList,
|
||||
'data_insertatlist': this.insertAtList,
|
||||
|
|
|
@ -37,7 +37,7 @@ Scratch3MotionBlocks.prototype.getPrimitives = function() {
|
|||
|
||||
Scratch3MotionBlocks.prototype.moveSteps = function (args, util) {
|
||||
var steps = Cast.toNumber(args.STEPS);
|
||||
var radians = MathUtil.degToRad(util.target.direction);
|
||||
var radians = MathUtil.degToRad(90 - util.target.direction);
|
||||
var dx = steps * Math.cos(radians);
|
||||
var dy = steps * Math.sin(radians);
|
||||
util.target.setXY(util.target.x + dx, util.target.y + dy);
|
||||
|
|
|
@ -13,7 +13,8 @@ function Scratch3ProcedureBlocks(runtime) {
|
|||
Scratch3ProcedureBlocks.prototype.getPrimitives = function() {
|
||||
return {
|
||||
'procedures_defnoreturn': this.defNoReturn,
|
||||
'procedures_callnoreturn': this.callNoReturn
|
||||
'procedures_callnoreturn': this.callNoReturn,
|
||||
'procedures_param': this.param
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -23,10 +24,24 @@ Scratch3ProcedureBlocks.prototype.defNoReturn = function () {
|
|||
|
||||
Scratch3ProcedureBlocks.prototype.callNoReturn = function (args, util) {
|
||||
if (!util.stackFrame.executed) {
|
||||
var procedureName = args.mutation.name;
|
||||
var procedureName = args.mutation.proccode;
|
||||
var paramNames = util.getProcedureParamNames(procedureName);
|
||||
for (var i = 0; i < paramNames.length; i++) {
|
||||
if (args.hasOwnProperty('input' + i)) {
|
||||
util.pushParam(paramNames[i], args['input' + i]);
|
||||
}
|
||||
}
|
||||
util.stackFrame.executed = true;
|
||||
util.startProcedure(procedureName);
|
||||
}
|
||||
};
|
||||
|
||||
Scratch3ProcedureBlocks.prototype.param = function (args, util) {
|
||||
var value = util.getParam(args.mutation.paramname);
|
||||
if (!value) {
|
||||
return 0;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
module.exports = Scratch3ProcedureBlocks;
|
||||
|
|
|
@ -23,7 +23,8 @@ Scratch3SensingBlocks.prototype.getPrimitives = function() {
|
|||
'sensing_mousey': this.getMouseY,
|
||||
'sensing_mousedown': this.getMouseDown,
|
||||
'sensing_keypressed': this.getKeyPressed,
|
||||
'sensing_current': this.current
|
||||
'sensing_current': this.current,
|
||||
'sensing_dayssince2000': this.daysSince2000
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -99,4 +100,15 @@ Scratch3SensingBlocks.prototype.getKeyPressed = function (args, util) {
|
|||
return util.ioQuery('keyboard', 'getKeyIsDown', args.KEY_OPTION);
|
||||
};
|
||||
|
||||
Scratch3SensingBlocks.prototype.daysSince2000 = function()
|
||||
{
|
||||
var msPerDay = 24 * 60 * 60 * 1000;
|
||||
var start = new Date(2000, 1-1, 1);
|
||||
var today = new Date();
|
||||
var dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset();
|
||||
var mSecsSinceStart = today.valueOf() - start.valueOf();
|
||||
mSecsSinceStart += ((today.getTimezoneOffset() - dstAdjust) * 60 * 1000);
|
||||
return mSecsSinceStart / msPerDay;
|
||||
};
|
||||
|
||||
module.exports = Scratch3SensingBlocks;
|
||||
|
|
|
@ -151,24 +151,40 @@ Blocks.prototype.getProcedureDefinition = function (name) {
|
|||
var block = this._blocks[id];
|
||||
if ((block.opcode == 'procedures_defnoreturn' ||
|
||||
block.opcode == 'procedures_defreturn') &&
|
||||
block.fields['NAME'].value == name) {
|
||||
block.mutation.proccode == name) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the procedure definition for a given name.
|
||||
* @param {?string} name Name of procedure to query.
|
||||
* @return {?string} ID of procedure definition.
|
||||
*/
|
||||
Blocks.prototype.getProcedureParamNames = function (name) {
|
||||
for (var id in this._blocks) {
|
||||
var block = this._blocks[id];
|
||||
if ((block.opcode == 'procedures_defnoreturn' ||
|
||||
block.opcode == 'procedures_defreturn') &&
|
||||
block.mutation.proccode == name) {
|
||||
return JSON.parse(block.mutation.argumentnames);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Create event listener for blocks. Handles validation and serves as a generic
|
||||
* adapter between the blocks and the runtime interface.
|
||||
* @param {Object} e Blockly "block" event
|
||||
* @param {boolean} isFlyout If true, create a listener for flyout events.
|
||||
* @param {?Runtime} opt_runtime Optional runtime to forward click events to.
|
||||
*/
|
||||
|
||||
Blocks.prototype.blocklyListen = function (e, isFlyout, opt_runtime) {
|
||||
Blocks.prototype.blocklyListen = function (e, opt_runtime) {
|
||||
// Validate event
|
||||
if (typeof e !== 'object') return;
|
||||
if (typeof e.blockId !== 'string') return;
|
||||
|
@ -187,7 +203,7 @@ Blocks.prototype.blocklyListen = function (e, isFlyout, opt_runtime) {
|
|||
var newBlocks = adapter(e);
|
||||
// A create event can create many blocks. Add them all.
|
||||
for (var i = 0; i < newBlocks.length; i++) {
|
||||
this.createBlock(newBlocks[i], isFlyout);
|
||||
this.createBlock(newBlocks[i]);
|
||||
}
|
||||
break;
|
||||
case 'change':
|
||||
|
@ -209,8 +225,10 @@ Blocks.prototype.blocklyListen = function (e, isFlyout, opt_runtime) {
|
|||
});
|
||||
break;
|
||||
case 'delete':
|
||||
// Don't accept delete events for shadow blocks being obscured.
|
||||
if (this._blocks[e.blockId].shadow) {
|
||||
// Don't accept delete events for missing blocks,
|
||||
// or shadow blocks being obscured.
|
||||
if (!this._blocks.hasOwnProperty(e.blockId) ||
|
||||
this._blocks[e.blockId].shadow) {
|
||||
return;
|
||||
}
|
||||
// Inform any runtime to forget about glows on this script.
|
||||
|
@ -229,9 +247,8 @@ Blocks.prototype.blocklyListen = function (e, isFlyout, opt_runtime) {
|
|||
/**
|
||||
* Block management: create blocks and scripts from a `create` event
|
||||
* @param {!Object} block Blockly create event to be processed
|
||||
* @param {boolean} opt_isFlyoutBlock Whether the block is in the flyout.
|
||||
*/
|
||||
Blocks.prototype.createBlock = function (block, opt_isFlyoutBlock) {
|
||||
Blocks.prototype.createBlock = function (block) {
|
||||
// Does the block already exist?
|
||||
// Could happen, e.g., for an unobscured shadow.
|
||||
if (this._blocks.hasOwnProperty(block.id)) {
|
||||
|
@ -241,9 +258,8 @@ Blocks.prototype.createBlock = function (block, opt_isFlyoutBlock) {
|
|||
this._blocks[block.id] = block;
|
||||
// Push block id to scripts array.
|
||||
// Blocks are added as a top-level stack if they are marked as a top-block
|
||||
// (if they were top-level XML in the event) and if they are not
|
||||
// flyout blocks.
|
||||
if (!opt_isFlyoutBlock && block.topLevel) {
|
||||
// (if they were top-level XML in the event).
|
||||
if (block.topLevel) {
|
||||
this._addScript(block.id);
|
||||
}
|
||||
};
|
||||
|
@ -271,6 +287,10 @@ Blocks.prototype.changeBlock = function (args) {
|
|||
* @param {!Object} e Blockly move event to be processed
|
||||
*/
|
||||
Blocks.prototype.moveBlock = function (e) {
|
||||
if (!this._blocks.hasOwnProperty(e.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move coordinate changes.
|
||||
if (e.newCoordinate) {
|
||||
this._blocks[e.id].x = e.newCoordinate.x;
|
||||
|
@ -434,7 +454,9 @@ Blocks.prototype.mutationToXML = function (mutation) {
|
|||
var mutationString = '<' + mutation.tagName;
|
||||
for (var prop in mutation) {
|
||||
if (prop == 'children' || prop == 'tagName') continue;
|
||||
mutationString += ' ' + prop + '="' + mutation[prop] + '"';
|
||||
var mutationValue = (typeof mutation[prop] === 'string') ?
|
||||
xmlEscape(mutation[prop]) : mutation[prop];
|
||||
mutationString += ' ' + prop + '="' + mutationValue + '"';
|
||||
}
|
||||
mutationString += '>';
|
||||
for (var i = 0; i < mutation.children.length; i++) {
|
||||
|
|
|
@ -22,19 +22,34 @@ var execute = function (sequencer, thread) {
|
|||
var currentBlockId = thread.peekStack();
|
||||
var currentStackFrame = thread.peekStackFrame();
|
||||
|
||||
// Verify that the block still exists.
|
||||
if (!target ||
|
||||
typeof target.blocks.getBlock(currentBlockId) === 'undefined') {
|
||||
// Check where the block lives: target blocks or flyout blocks.
|
||||
var targetHasBlock = (
|
||||
typeof target.blocks.getBlock(currentBlockId) !== 'undefined'
|
||||
);
|
||||
var flyoutHasBlock = (
|
||||
typeof runtime.flyoutBlocks.getBlock(currentBlockId) !== 'undefined'
|
||||
);
|
||||
|
||||
// Stop if block or target no longer exists.
|
||||
if (!target || (!targetHasBlock && !flyoutHasBlock)) {
|
||||
// No block found: stop the thread; script no longer exists.
|
||||
sequencer.retireThread(thread);
|
||||
return;
|
||||
}
|
||||
|
||||
// Query info about the block.
|
||||
var opcode = target.blocks.getOpcode(currentBlockId);
|
||||
var blockContainer = null;
|
||||
if (targetHasBlock) {
|
||||
blockContainer = target.blocks;
|
||||
} else {
|
||||
blockContainer = runtime.flyoutBlocks;
|
||||
}
|
||||
var opcode = blockContainer.getOpcode(currentBlockId);
|
||||
var fields = blockContainer.getFields(currentBlockId);
|
||||
var inputs = blockContainer.getInputs(currentBlockId);
|
||||
var blockFunction = runtime.getOpcodeFunction(opcode);
|
||||
var isHat = runtime.getIsHat(opcode);
|
||||
var fields = target.blocks.getFields(currentBlockId);
|
||||
var inputs = target.blocks.getInputs(currentBlockId);
|
||||
|
||||
|
||||
if (!opcode) {
|
||||
console.warn('Could not get opcode for block: ' + currentBlockId);
|
||||
|
@ -133,7 +148,7 @@ var execute = function (sequencer, thread) {
|
|||
}
|
||||
|
||||
// Add any mutation to args (e.g., for procedures).
|
||||
var mutation = target.blocks.getMutation(currentBlockId);
|
||||
var mutation = blockContainer.getMutation(currentBlockId);
|
||||
if (mutation) {
|
||||
argValues.mutation = mutation;
|
||||
}
|
||||
|
@ -161,9 +176,27 @@ var execute = function (sequencer, thread) {
|
|||
startBranch: function (branchNum) {
|
||||
sequencer.stepToBranch(thread, branchNum);
|
||||
},
|
||||
stopAll: function () {
|
||||
runtime.stopAll();
|
||||
},
|
||||
stopOtherTargetThreads: function() {
|
||||
runtime.stopForTarget(target, thread);
|
||||
},
|
||||
stopThread: function() {
|
||||
sequencer.retireThread(thread);
|
||||
},
|
||||
startProcedure: function (procedureName) {
|
||||
sequencer.stepToProcedure(thread, procedureName);
|
||||
},
|
||||
getProcedureParamNames: function (procedureName) {
|
||||
return blockContainer.getProcedureParamNames(procedureName);
|
||||
},
|
||||
pushParam: function (paramName, paramValue) {
|
||||
thread.pushParam(paramName, paramValue);
|
||||
},
|
||||
getParam: function (paramName) {
|
||||
return thread.getParam(paramName);
|
||||
},
|
||||
startHats: function(requestedHat, opt_matchFields, opt_target) {
|
||||
return (
|
||||
runtime.startHats(requestedHat, opt_matchFields, opt_target)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var EventEmitter = require('events');
|
||||
var Sequencer = require('./sequencer');
|
||||
var Blocks = require('./blocks');
|
||||
var Thread = require('./thread');
|
||||
var util = require('util');
|
||||
|
||||
|
@ -45,6 +46,8 @@ function Runtime () {
|
|||
/** @type {!Sequencer} */
|
||||
this.sequencer = new Sequencer(this);
|
||||
|
||||
this.flyoutBlocks = new Blocks();
|
||||
|
||||
/**
|
||||
* Map to look up a block primitive's implementation function by its opcode.
|
||||
* This is a two-step lookup: package name first, then primitive name.
|
||||
|
@ -244,6 +247,9 @@ Runtime.prototype._pushThread = function (id, target) {
|
|||
* @param {?Thread} thread Thread object to remove from actives
|
||||
*/
|
||||
Runtime.prototype._removeThread = function (thread) {
|
||||
// Inform sequencer to stop executing that thread.
|
||||
this.sequencer.retireThread(thread);
|
||||
// Remove from the list.
|
||||
var i = this.threads.indexOf(thread);
|
||||
if (i > -1) {
|
||||
this.threads.splice(i, 1);
|
||||
|
@ -342,7 +348,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode,
|
|||
// any existing threads starting with the top block.
|
||||
for (var i = 0; i < instance.threads.length; i++) {
|
||||
if (instance.threads[i].topBlock === topBlockId &&
|
||||
(!opt_target || instance.threads[i].target == opt_target)) {
|
||||
instance.threads[i].target == target) {
|
||||
instance._removeThread(instance.threads[i]);
|
||||
}
|
||||
}
|
||||
|
@ -351,7 +357,7 @@ Runtime.prototype.startHats = function (requestedHatOpcode,
|
|||
// give up if any threads with the top block are running.
|
||||
for (var j = 0; j < instance.threads.length; j++) {
|
||||
if (instance.threads[j].topBlock === topBlockId &&
|
||||
(!opt_target || instance.threads[j].target == opt_target)) {
|
||||
instance.threads[j].target == target) {
|
||||
// Some thread is already running.
|
||||
return;
|
||||
}
|
||||
|
@ -364,26 +370,38 @@ Runtime.prototype.startHats = function (requestedHatOpcode,
|
|||
};
|
||||
|
||||
/**
|
||||
* Dispose of a target.
|
||||
* @param {!Target} target Target to dispose of.
|
||||
* Dispose all targets. Return to clean state.
|
||||
*/
|
||||
Runtime.prototype.disposeTarget = function (target) {
|
||||
// Allow target to do dispose actions.
|
||||
target.dispose();
|
||||
// Remove from list of targets.
|
||||
var index = this.targets.indexOf(target);
|
||||
if (index > -1) {
|
||||
this.targets.splice(index, 1);
|
||||
}
|
||||
Runtime.prototype.dispose = function () {
|
||||
this.stopAll();
|
||||
this.targets.map(this.disposeTarget, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispose of a target.
|
||||
* @param {!Target} disposingTarget Target to dispose of.
|
||||
*/
|
||||
Runtime.prototype.disposeTarget = function (disposingTarget) {
|
||||
this.targets = this.targets.filter(function (target) {
|
||||
if (disposingTarget !== target) return true;
|
||||
// Allow target to do dispose actions.
|
||||
target.dispose();
|
||||
// Remove from list of targets.
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop any threads acting on the target.
|
||||
* @param {!Target} target Target to stop threads for.
|
||||
* @param {Thread=} opt_threadException Optional thread to skip.
|
||||
*/
|
||||
Runtime.prototype.stopForTarget = function (target) {
|
||||
Runtime.prototype.stopForTarget = function (target, opt_threadException) {
|
||||
// Stop any threads on the target.
|
||||
for (var i = 0; i < this.threads.length; i++) {
|
||||
if (this.threads[i] === opt_threadException) {
|
||||
continue;
|
||||
}
|
||||
if (this.threads[i].target == target) {
|
||||
this._removeThread(this.threads[i]);
|
||||
}
|
||||
|
@ -464,6 +482,10 @@ Runtime.prototype._updateScriptGlows = function () {
|
|||
if (thread.requestScriptGlowInFrame && target == this._editingTarget) {
|
||||
var blockForThread = thread.peekStack() || thread.topBlock;
|
||||
var script = target.blocks.getTopLevelScript(blockForThread);
|
||||
if (!script) {
|
||||
// Attempt to find in flyout blocks.
|
||||
script = this.flyoutBlocks.getTopLevelScript(blockForThread);
|
||||
}
|
||||
if (script) {
|
||||
requestedGlowsThisFrame.push(script);
|
||||
}
|
||||
|
|
|
@ -132,6 +132,11 @@ Sequencer.prototype.stepToBranch = function (thread, branchNum) {
|
|||
Sequencer.prototype.stepToProcedure = function (thread, procedureName) {
|
||||
var definition = thread.target.blocks.getProcedureDefinition(procedureName);
|
||||
thread.pushStack(definition);
|
||||
// Check if the call is recursive. If so, yield.
|
||||
// @todo: Have behavior match Scratch 2.0.
|
||||
if (thread.stack.indexOf(definition) > -1) {
|
||||
thread.setStatus(Thread.STATUS_YIELD_FRAME);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -83,6 +83,7 @@ Thread.prototype.pushStack = function (blockId) {
|
|||
this.stackFrames.push({
|
||||
reported: {}, // Collects reported input values.
|
||||
waitingReporter: null, // Name of waiting reporter.
|
||||
params: {}, // Procedure parameters.
|
||||
executionContext: {} // A context passed to block implementations.
|
||||
});
|
||||
}
|
||||
|
@ -135,6 +136,21 @@ Thread.prototype.pushReportedValue = function (value) {
|
|||
}
|
||||
};
|
||||
|
||||
Thread.prototype.pushParam = function (paramName, value) {
|
||||
var stackFrame = this.peekStackFrame();
|
||||
stackFrame.params[paramName] = value;
|
||||
};
|
||||
|
||||
Thread.prototype.getParam = function (paramName) {
|
||||
for (var i = this.stackFrames.length - 1; i >= 0; i--) {
|
||||
var frame = this.stackFrames[i];
|
||||
if (frame.params.hasOwnProperty(paramName)) {
|
||||
return frame.params[paramName];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the current execution of a thread is at the top of the stack.
|
||||
* @return {Boolean} True if execution is at top of the stack.
|
||||
|
|
|
@ -218,6 +218,40 @@ function flatten (blocks) {
|
|||
return finalBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Scratch 2.0 procedure string (e.g., "my_procedure %s %b %n")
|
||||
* into an argument map. This allows us to provide the expected inputs
|
||||
* to a mutated procedure call.
|
||||
* @param {string} procCode Scratch 2.0 procedure string.
|
||||
* @return {Object} Argument map compatible with those in sb2specmap.
|
||||
*/
|
||||
function parseProcedureArgMap (procCode) {
|
||||
var argMap = [
|
||||
{} // First item in list is op string.
|
||||
];
|
||||
var INPUT_PREFIX = 'input';
|
||||
var inputCount = 0;
|
||||
// Split by %n, %b, %s.
|
||||
var parts = procCode.split(/(?=[^\\]\%[nbs])/);
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var part = parts[i].trim();
|
||||
if (part.substring(0, 1) == '%') {
|
||||
var argType = part.substring(1, 2);
|
||||
var arg = {
|
||||
type: 'input',
|
||||
inputName: INPUT_PREFIX + (inputCount++)
|
||||
};
|
||||
if (argType == 'n') {
|
||||
arg.inputOp = 'math_number';
|
||||
} else if (argType == 's') {
|
||||
arg.inputOp = 'text';
|
||||
}
|
||||
argMap.push(arg);
|
||||
}
|
||||
}
|
||||
return argMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single SB2 JSON-formatted block and its children.
|
||||
* @param {!Object} sb2block SB2 JSON-formatted block.
|
||||
|
@ -242,6 +276,10 @@ function parseBlock (sb2block) {
|
|||
shadow: false, // No shadow blocks in an SB2 by default.
|
||||
children: [] // Store any generated children, flattened in `flatten`.
|
||||
};
|
||||
// For a procedure call, generate argument map from proc string.
|
||||
if (oldOpcode == 'call') {
|
||||
blockMetadata.argMap = parseProcedureArgMap(sb2block[1]);
|
||||
}
|
||||
// Look at the expected arguments in `blockMetadata.argMap.`
|
||||
// The basic problem here is to turn positional SB2 arguments into
|
||||
// non-positional named Scratch VM arguments.
|
||||
|
@ -269,8 +307,14 @@ function parseBlock (sb2block) {
|
|||
// Single block occupies the input.
|
||||
innerBlocks = [parseBlock(providedArg)];
|
||||
}
|
||||
var previousBlock = null;
|
||||
for (var j = 0; j < innerBlocks.length; j++) {
|
||||
innerBlocks[j].parent = activeBlock.id;
|
||||
if (j == 0) {
|
||||
innerBlocks[j].parent = activeBlock.id;
|
||||
} else {
|
||||
innerBlocks[j].parent = previousBlock;
|
||||
}
|
||||
previousBlock = innerBlocks[j].id;
|
||||
}
|
||||
// Obscures any shadow.
|
||||
shadowObscured = true;
|
||||
|
@ -343,11 +387,44 @@ function parseBlock (sb2block) {
|
|||
}
|
||||
}
|
||||
// Special cases to generate mutations.
|
||||
if (oldOpcode == 'call') {
|
||||
if (oldOpcode == 'stopScripts') {
|
||||
// Mutation for stop block: if the argument is 'other scripts',
|
||||
// the block needs a next connection.
|
||||
if (sb2block[1] == 'other scripts in sprite' ||
|
||||
sb2block[1] == 'other scripts in stage') {
|
||||
activeBlock.mutation = {
|
||||
tagName: 'mutation',
|
||||
hasnext: 'true',
|
||||
children: []
|
||||
};
|
||||
}
|
||||
} else if (oldOpcode == 'procDef') {
|
||||
// Mutation for procedure definition:
|
||||
// store all 2.0 proc data.
|
||||
var procData = sb2block.slice(1);
|
||||
activeBlock.mutation = {
|
||||
tagName: 'mutation',
|
||||
proccode: procData[0], // e.g., "abc %n %b %s"
|
||||
argumentnames: JSON.stringify(procData[1]), // e.g. ['arg1', 'arg2']
|
||||
argumentdefaults: JSON.stringify(procData[2]), // e.g., [1, 'abc']
|
||||
warp: procData[3], // Warp mode, e.g., true/false.
|
||||
children: []
|
||||
};
|
||||
} else if (oldOpcode == 'call') {
|
||||
// Mutation for procedure call:
|
||||
// string for proc code (e.g., "abc %n %b %s").
|
||||
activeBlock.mutation = {
|
||||
tagName: 'mutation',
|
||||
children: [],
|
||||
name: sb2block[1]
|
||||
proccode: sb2block[1]
|
||||
};
|
||||
} else if (oldOpcode == 'getParam') {
|
||||
// Mutation for procedure parameter.
|
||||
activeBlock.mutation = {
|
||||
tagName: 'mutation',
|
||||
children: [],
|
||||
paramname: sb2block[1], // Name of parameter.
|
||||
shape: sb2block[2] // Shape - in 2.0, 'r' or 'b'.
|
||||
};
|
||||
}
|
||||
return activeBlock;
|
||||
|
|
|
@ -752,9 +752,8 @@ var specMap = {
|
|||
'opcode':'control_stop',
|
||||
'argMap':[
|
||||
{
|
||||
'type':'input',
|
||||
'inputOp':'control_stop_menu',
|
||||
'inputName':'STOP_OPTION'
|
||||
'type':'field',
|
||||
'fieldName':'STOP_OPTION'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1375,15 +1374,10 @@ var specMap = {
|
|||
},
|
||||
'procDef':{
|
||||
'opcode':'procedures_defnoreturn',
|
||||
'argMap':[
|
||||
{
|
||||
'type':'field',
|
||||
'fieldName':'NAME'
|
||||
}
|
||||
]
|
||||
'argMap':[]
|
||||
},
|
||||
'getParam':{
|
||||
'opcode':'proc_param',
|
||||
'opcode':'procedures_param',
|
||||
'argMap':[]
|
||||
},
|
||||
'call':{
|
||||
|
|
35
src/index.js
35
src/index.js
|
@ -44,6 +44,7 @@ function VirtualMachine () {
|
|||
});
|
||||
|
||||
this.blockListener = this.blockListener.bind(this);
|
||||
this.flyoutBlockListener = this.flyoutBlockListener.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,6 +73,15 @@ VirtualMachine.prototype.stopAll = function () {
|
|||
this.runtime.stopAll();
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear out current running project data.
|
||||
*/
|
||||
VirtualMachine.prototype.clear = function () {
|
||||
this.runtime.dispose();
|
||||
this.editingTarget = null;
|
||||
this.emitTargetsUpdate();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get data for playground. Data comes back in an emitted event.
|
||||
*/
|
||||
|
@ -115,6 +125,7 @@ VirtualMachine.prototype.postIOData = function (device, data) {
|
|||
* @param {?string} json JSON string representing the project.
|
||||
*/
|
||||
VirtualMachine.prototype.loadProject = function (json) {
|
||||
this.clear();
|
||||
// @todo: Handle other formats, e.g., Scratch 1.4, Scratch 3.0.
|
||||
sb2import(json, this.runtime);
|
||||
// Select the first target for editing, e.g., the stage.
|
||||
|
@ -132,10 +143,10 @@ VirtualMachine.prototype.loadProject = function (json) {
|
|||
VirtualMachine.prototype.createEmptyProject = function () {
|
||||
// Stage.
|
||||
var blocks2 = new Blocks();
|
||||
var stage = new Sprite(blocks2);
|
||||
var stage = new Sprite(blocks2, this.runtime);
|
||||
stage.name = 'Stage';
|
||||
stage.costumes.push({
|
||||
skin: '/assets/stage.png',
|
||||
skin: './assets/stage.png',
|
||||
name: 'backdrop1',
|
||||
bitmapResolution: 2,
|
||||
rotationCenterX: 480,
|
||||
|
@ -151,10 +162,10 @@ VirtualMachine.prototype.createEmptyProject = function () {
|
|||
target2.isStage = true;
|
||||
// Sprite1 (cat).
|
||||
var blocks1 = new Blocks();
|
||||
var sprite = new Sprite(blocks1);
|
||||
var sprite = new Sprite(blocks1, this.runtime);
|
||||
sprite.name = 'Sprite1';
|
||||
sprite.costumes.push({
|
||||
skin: '/assets/scratch_cat.svg',
|
||||
skin: './assets/scratch_cat.svg',
|
||||
name: 'costume1',
|
||||
bitmapResolution: 1,
|
||||
rotationCenterX: 47,
|
||||
|
@ -186,14 +197,18 @@ VirtualMachine.prototype.attachRenderer = function (renderer) {
|
|||
*/
|
||||
VirtualMachine.prototype.blockListener = function (e) {
|
||||
if (this.editingTarget) {
|
||||
this.editingTarget.blocks.blocklyListen(
|
||||
e,
|
||||
false,
|
||||
this.runtime
|
||||
);
|
||||
this.editingTarget.blocks.blocklyListen(e, this.runtime);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle a Blockly event for the flyout.
|
||||
* @param {!Blockly.Event} e Any Blockly event.
|
||||
*/
|
||||
VirtualMachine.prototype.flyoutBlockListener = function (e) {
|
||||
this.runtime.flyoutBlocks.blocklyListen(e, this.runtime);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set an editing target. An editor UI can use this function to switch
|
||||
* between editing different targets, sprites, etc.
|
||||
|
@ -232,7 +247,7 @@ VirtualMachine.prototype.emitTargetsUpdate = function () {
|
|||
return [target.id, target.getName()];
|
||||
}),
|
||||
// Currently editing target id.
|
||||
editingTarget: this.editingTarget.id
|
||||
editingTarget: this.editingTarget ? this.editingTarget.id : null
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -438,9 +438,6 @@ Clone.prototype.onGreenFlag = function () {
|
|||
* Dispose of this clone, destroying any run-time properties.
|
||||
*/
|
||||
Clone.prototype.dispose = function () {
|
||||
if (this.isOriginal) { // Don't allow a non-clone to delete itself.
|
||||
return;
|
||||
}
|
||||
this.runtime.changeCloneCounter(-1);
|
||||
if (this.renderer && this.drawableID !== null) {
|
||||
this.renderer.destroyDrawable(this.drawableID);
|
||||
|
|
|
@ -6,7 +6,7 @@ function MathUtil () {}
|
|||
* @return {!number} Equivalent value in radians.
|
||||
*/
|
||||
MathUtil.degToRad = function (deg) {
|
||||
return (Math.PI * (90 - deg)) / 180;
|
||||
return deg * Math.PI / 180;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
71
test/fixtures/default.json
vendored
Normal file
71
test/fixtures/default.json
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
{
|
||||
"objName": "Stage",
|
||||
"sounds": [{
|
||||
"soundName": "pop",
|
||||
"soundID": -1,
|
||||
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
|
||||
"sampleCount": 258,
|
||||
"rate": 11025,
|
||||
"format": ""
|
||||
}],
|
||||
"costumes": [{
|
||||
"costumeName": "backdrop1",
|
||||
"baseLayerID": -1,
|
||||
"baseLayerMD5": "739b5e2a2435f6e1ec2993791b423146.png",
|
||||
"bitmapResolution": 1,
|
||||
"rotationCenterX": 240,
|
||||
"rotationCenterY": 180
|
||||
}],
|
||||
"currentCostumeIndex": 0,
|
||||
"penLayerMD5": "5c81a336fab8be57adc039a8a2b33ca9.png",
|
||||
"penLayerID": -1,
|
||||
"tempoBPM": 60,
|
||||
"videoAlpha": 0.5,
|
||||
"children": [{
|
||||
"objName": "Sprite1",
|
||||
"sounds": [{
|
||||
"soundName": "meow",
|
||||
"soundID": -1,
|
||||
"md5": "83c36d806dc92327b9e7049a565c6bff.wav",
|
||||
"sampleCount": 18688,
|
||||
"rate": 22050,
|
||||
"format": ""
|
||||
}],
|
||||
"costumes": [{
|
||||
"costumeName": "costume1",
|
||||
"baseLayerID": -1,
|
||||
"baseLayerMD5": "09dc888b0b7df19f70d81588ae73420e.svg",
|
||||
"bitmapResolution": 1,
|
||||
"rotationCenterX": 47,
|
||||
"rotationCenterY": 55
|
||||
},
|
||||
{
|
||||
"costumeName": "costume2",
|
||||
"baseLayerID": -1,
|
||||
"baseLayerMD5": "3696356a03a8d938318876a593572843.svg",
|
||||
"bitmapResolution": 1,
|
||||
"rotationCenterX": 47,
|
||||
"rotationCenterY": 55
|
||||
}],
|
||||
"currentCostumeIndex": 0,
|
||||
"scratchX": 0,
|
||||
"scratchY": 0,
|
||||
"scale": 1,
|
||||
"direction": 90,
|
||||
"rotationStyle": "normal",
|
||||
"isDraggable": false,
|
||||
"indexInLibrary": 1,
|
||||
"visible": true,
|
||||
"spriteInfo": {
|
||||
}
|
||||
}],
|
||||
"info": {
|
||||
"videoOn": false,
|
||||
"userAgent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/50.0.2661.102 Safari\/537.36",
|
||||
"swfVersion": "v446",
|
||||
"scriptCount": 0,
|
||||
"spriteCount": 1,
|
||||
"hasCloudData": false,
|
||||
"flashVersion": "MAC 21,0,0,242"
|
||||
}
|
||||
}
|
359
test/fixtures/demo.json
vendored
Normal file
359
test/fixtures/demo.json
vendored
Normal file
|
@ -0,0 +1,359 @@
|
|||
{
|
||||
"objName": "Stage",
|
||||
"variables": [{
|
||||
"name": "x",
|
||||
"value": "1",
|
||||
"isPersistent": false
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"value": "1",
|
||||
"isPersistent": false
|
||||
},
|
||||
{
|
||||
"name": "z",
|
||||
"value": "1",
|
||||
"isPersistent": false
|
||||
},
|
||||
{
|
||||
"name": "d",
|
||||
"value": "1",
|
||||
"isPersistent": false
|
||||
},
|
||||
{
|
||||
"name": "a",
|
||||
"value": 4,
|
||||
"isPersistent": false
|
||||
}],
|
||||
"lists": [{
|
||||
"listName": "D# Minor Pentatonic",
|
||||
"contents": ["78",
|
||||
"75",
|
||||
"73",
|
||||
"75",
|
||||
"70",
|
||||
"78",
|
||||
"73",
|
||||
"75",
|
||||
"75",
|
||||
"78",
|
||||
"75",
|
||||
"73",
|
||||
"75",
|
||||
"70",
|
||||
"75",
|
||||
"78",
|
||||
"73",
|
||||
"75",
|
||||
"78",
|
||||
"75",
|
||||
"73",
|
||||
"75",
|
||||
"70",
|
||||
"73",
|
||||
"68",
|
||||
"70",
|
||||
"66",
|
||||
"68",
|
||||
"63"],
|
||||
"isPersistent": false,
|
||||
"x": 5,
|
||||
"y": 32,
|
||||
"width": 125,
|
||||
"height": 206,
|
||||
"visible": true
|
||||
}],
|
||||
"scripts": [[52,
|
||||
8,
|
||||
[["whenIReceive", "start"],
|
||||
["setVar:to:", "a", "1"],
|
||||
["doRepeat",
|
||||
["lineCountOfList:", "D# Minor Pentatonic"],
|
||||
[["noteOn:duration:elapsed:from:", ["getLine:ofList:", ["readVariable", "a"], "D# Minor Pentatonic"], 0.5], ["changeVar:by:", "a", 1]]]]],
|
||||
[53,
|
||||
186,
|
||||
[["whenIReceive", "start"],
|
||||
["setVar:to:", "x", "1"],
|
||||
["rest:elapsed:from:", 7.25],
|
||||
["doRepeat",
|
||||
["lineCountOfList:", "D# Minor Pentatonic"],
|
||||
[["noteOn:duration:elapsed:from:", ["getLine:ofList:", ["readVariable", "x"], "D# Minor Pentatonic"], 0.25], ["changeVar:by:", "x", 1]]]]],
|
||||
[48,
|
||||
557,
|
||||
[["whenIReceive", "start"],
|
||||
["setVar:to:", "z", "1"],
|
||||
["rest:elapsed:from:", 13],
|
||||
["doRepeat",
|
||||
["lineCountOfList:", "D# Minor Pentatonic"],
|
||||
[["noteOn:duration:elapsed:from:", ["getLine:ofList:", ["readVariable", "z"], "D# Minor Pentatonic"], 0.0625], ["changeVar:by:", "z", 1]]]]],
|
||||
[49,
|
||||
368,
|
||||
[["whenIReceive", "start"],
|
||||
["setVar:to:", "y", "1"],
|
||||
["rest:elapsed:from:", 11],
|
||||
["doRepeat",
|
||||
["lineCountOfList:", "D# Minor Pentatonic"],
|
||||
[["noteOn:duration:elapsed:from:", ["getLine:ofList:", ["readVariable", "y"], "D# Minor Pentatonic"], 0.125], ["changeVar:by:", "y", 1]]]]],
|
||||
[52,
|
||||
745,
|
||||
[["whenIReceive", "start"],
|
||||
["setVar:to:", "d", "1"],
|
||||
["rest:elapsed:from:", 13.5],
|
||||
["doRepeat",
|
||||
["lineCountOfList:", "D# Minor Pentatonic"],
|
||||
[["noteOn:duration:elapsed:from:", ["getLine:ofList:", ["readVariable", "d"], "D# Minor Pentatonic"], 0.03125], ["changeVar:by:", "d", 1]]]]]],
|
||||
"sounds": [{
|
||||
"soundName": "pop",
|
||||
"soundID": 0,
|
||||
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
|
||||
"sampleCount": 258,
|
||||
"rate": 11025,
|
||||
"format": ""
|
||||
}],
|
||||
"costumes": [{
|
||||
"costumeName": "backdrop1",
|
||||
"baseLayerID": 4,
|
||||
"baseLayerMD5": "b61b1077b0ea1931abee9dbbfa7903ff.png",
|
||||
"bitmapResolution": 2,
|
||||
"rotationCenterX": 480,
|
||||
"rotationCenterY": 360
|
||||
}],
|
||||
"currentCostumeIndex": 0,
|
||||
"penLayerMD5": "5c81a336fab8be57adc039a8a2b33ca9.png",
|
||||
"penLayerID": 0,
|
||||
"tempoBPM": 60,
|
||||
"videoAlpha": 0.5,
|
||||
"children": [{
|
||||
"objName": "Indicator",
|
||||
"scripts": [[247.85,
|
||||
32.8,
|
||||
[["procDef", "foo %n", ["bar"], [1], false],
|
||||
["hide"],
|
||||
["clearPenTrails"],
|
||||
["penColor:", 5968094],
|
||||
["say:", ["getParam", "bar", "r"]],
|
||||
["stopScripts", "this script"]]],
|
||||
[41, 36, [["whenGreenFlag"], ["call", "foo %n", 1]]]],
|
||||
"sounds": [{
|
||||
"soundName": "pop",
|
||||
"soundID": 0,
|
||||
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
|
||||
"sampleCount": 258,
|
||||
"rate": 11025,
|
||||
"format": ""
|
||||
}],
|
||||
"costumes": [{
|
||||
"costumeName": "costume1",
|
||||
"baseLayerID": 1,
|
||||
"baseLayerMD5": "d36f6603ec293d2c2198d3ea05109fe0.png",
|
||||
"bitmapResolution": 2,
|
||||
"rotationCenterX": 0,
|
||||
"rotationCenterY": 0
|
||||
}],
|
||||
"currentCostumeIndex": 0,
|
||||
"scratchX": 22,
|
||||
"scratchY": -26,
|
||||
"scale": 1,
|
||||
"direction": 90,
|
||||
"rotationStyle": "normal",
|
||||
"isDraggable": false,
|
||||
"indexInLibrary": 3,
|
||||
"visible": false,
|
||||
"spriteInfo": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"target": "Stage",
|
||||
"cmd": "timer",
|
||||
"param": null,
|
||||
"color": 2926050,
|
||||
"label": "timer",
|
||||
"mode": 1,
|
||||
"sliderMin": 0,
|
||||
"sliderMax": 100,
|
||||
"isDiscrete": true,
|
||||
"x": 5,
|
||||
"y": 5,
|
||||
"visible": false
|
||||
},
|
||||
{
|
||||
"target": "Stage",
|
||||
"cmd": "getVar:",
|
||||
"param": "x",
|
||||
"color": 15629590,
|
||||
"label": "x",
|
||||
"mode": 1,
|
||||
"sliderMin": 0,
|
||||
"sliderMax": 100,
|
||||
"isDiscrete": true,
|
||||
"x": 5,
|
||||
"y": 268,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"target": "Stage",
|
||||
"cmd": "getVar:",
|
||||
"param": "y",
|
||||
"color": 15629590,
|
||||
"label": "y",
|
||||
"mode": 1,
|
||||
"sliderMin": 0,
|
||||
"sliderMax": 100,
|
||||
"isDiscrete": true,
|
||||
"x": 5,
|
||||
"y": 295,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"target": "Stage",
|
||||
"cmd": "getVar:",
|
||||
"param": "z",
|
||||
"color": 15629590,
|
||||
"label": "z",
|
||||
"mode": 1,
|
||||
"sliderMin": 0,
|
||||
"sliderMax": 100,
|
||||
"isDiscrete": true,
|
||||
"x": 78,
|
||||
"y": 268,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"objName": "Play",
|
||||
"scripts": [[32, 33, [["whenClicked"], ["broadcast:", "start"]]]],
|
||||
"sounds": [{
|
||||
"soundName": "pop",
|
||||
"soundID": 0,
|
||||
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
|
||||
"sampleCount": 258,
|
||||
"rate": 11025,
|
||||
"format": ""
|
||||
}],
|
||||
"costumes": [{
|
||||
"costumeName": "costume1",
|
||||
"baseLayerID": 2,
|
||||
"baseLayerMD5": "30f811366ae3a53e6447932cc7f0212d.png",
|
||||
"bitmapResolution": 2,
|
||||
"rotationCenterX": 68,
|
||||
"rotationCenterY": 115
|
||||
}],
|
||||
"currentCostumeIndex": 0,
|
||||
"scratchX": 2,
|
||||
"scratchY": -48,
|
||||
"scale": 1,
|
||||
"direction": 90,
|
||||
"rotationStyle": "normal",
|
||||
"isDraggable": false,
|
||||
"indexInLibrary": 1,
|
||||
"visible": true,
|
||||
"spriteInfo": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"target": "Stage",
|
||||
"cmd": "getVar:",
|
||||
"param": "d",
|
||||
"color": 15629590,
|
||||
"label": "d",
|
||||
"mode": 1,
|
||||
"sliderMin": 0,
|
||||
"sliderMax": 100,
|
||||
"isDiscrete": true,
|
||||
"x": 5,
|
||||
"y": 241,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"target": "Stage",
|
||||
"cmd": "getVar:",
|
||||
"param": "a",
|
||||
"color": 15629590,
|
||||
"label": "a",
|
||||
"mode": 1,
|
||||
"sliderMin": 0,
|
||||
"sliderMax": 100,
|
||||
"isDiscrete": true,
|
||||
"x": 78,
|
||||
"y": 241,
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"objName": "Stop",
|
||||
"scripts": [[45, 104, [["whenClicked"], ["stopScripts", "all"]]]],
|
||||
"sounds": [{
|
||||
"soundName": "pop",
|
||||
"soundID": 0,
|
||||
"md5": "83a9787d4cb6f3b7632b4ddfebf74367.wav",
|
||||
"sampleCount": 258,
|
||||
"rate": 11025,
|
||||
"format": ""
|
||||
}],
|
||||
"costumes": [{
|
||||
"costumeName": "costume1",
|
||||
"baseLayerID": 3,
|
||||
"baseLayerMD5": "3de406f265b8d664406adf7c70762514.png",
|
||||
"bitmapResolution": 2,
|
||||
"rotationCenterX": 68,
|
||||
"rotationCenterY": 70
|
||||
}],
|
||||
"currentCostumeIndex": 0,
|
||||
"scratchX": 121,
|
||||
"scratchY": -33,
|
||||
"scale": 1,
|
||||
"direction": 90,
|
||||
"rotationStyle": "normal",
|
||||
"isDraggable": false,
|
||||
"indexInLibrary": 2,
|
||||
"visible": true,
|
||||
"spriteInfo": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"listName": "D# Minor Pentatonic",
|
||||
"contents": ["78",
|
||||
"75",
|
||||
"73",
|
||||
"75",
|
||||
"70",
|
||||
"78",
|
||||
"73",
|
||||
"75",
|
||||
"75",
|
||||
"78",
|
||||
"75",
|
||||
"73",
|
||||
"75",
|
||||
"70",
|
||||
"75",
|
||||
"78",
|
||||
"73",
|
||||
"75",
|
||||
"78",
|
||||
"75",
|
||||
"73",
|
||||
"75",
|
||||
"70",
|
||||
"73",
|
||||
"68",
|
||||
"70",
|
||||
"66",
|
||||
"68",
|
||||
"63"],
|
||||
"isPersistent": false,
|
||||
"x": 5,
|
||||
"y": 32,
|
||||
"width": 125,
|
||||
"height": 206,
|
||||
"visible": true
|
||||
}],
|
||||
"info": {
|
||||
"spriteCount": 3,
|
||||
"projectID": "118381369",
|
||||
"videoOn": false,
|
||||
"hasCloudData": false,
|
||||
"userAgent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/53.0.2785.143 Safari\/537.36",
|
||||
"scriptCount": 9,
|
||||
"flashVersion": "MAC 23,0,0,185",
|
||||
"swfVersion": "v450.1"
|
||||
}
|
||||
}
|
88
test/unit/import_sb2.js
Normal file
88
test/unit/import_sb2.js
Normal file
|
@ -0,0 +1,88 @@
|
|||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var test = require('tap').test;
|
||||
|
||||
var clone = require('../../src/sprites/clone');
|
||||
var runtime = require('../../src/engine/runtime');
|
||||
var sb2 = require('../../src/import/sb2import');
|
||||
|
||||
test('spec', function (t) {
|
||||
t.type(sb2, 'function');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('default', function (t) {
|
||||
// Get SB2 JSON (string)
|
||||
var uri = path.resolve(__dirname, '../fixtures/default.json');
|
||||
var file = fs.readFileSync(uri, 'utf8');
|
||||
|
||||
// Create runtime instance & load SB2 into it
|
||||
var rt = new runtime();
|
||||
sb2(file, rt);
|
||||
|
||||
// Test
|
||||
t.type(file, 'string');
|
||||
t.type(rt, 'object');
|
||||
t.type(rt.targets, 'object');
|
||||
|
||||
t.ok(rt.targets[0] instanceof clone);
|
||||
t.type(rt.targets[0].id, 'string');
|
||||
t.type(rt.targets[0].blocks, 'object');
|
||||
t.type(rt.targets[0].variables, 'object');
|
||||
t.type(rt.targets[0].lists, 'object');
|
||||
|
||||
t.equal(rt.targets[0].isOriginal, true);
|
||||
t.equal(rt.targets[0].currentCostume, 0);
|
||||
t.equal(rt.targets[0].isOriginal, true);
|
||||
t.equal(rt.targets[0].isStage, true);
|
||||
|
||||
t.ok(rt.targets[1] instanceof clone);
|
||||
t.type(rt.targets[1].id, 'string');
|
||||
t.type(rt.targets[1].blocks, 'object');
|
||||
t.type(rt.targets[1].variables, 'object');
|
||||
t.type(rt.targets[1].lists, 'object');
|
||||
|
||||
t.equal(rt.targets[1].isOriginal, true);
|
||||
t.equal(rt.targets[1].currentCostume, 0);
|
||||
t.equal(rt.targets[1].isOriginal, true);
|
||||
t.equal(rt.targets[1].isStage, false);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('demo', function (t) {
|
||||
// Get SB2 JSON (string)
|
||||
var uri = path.resolve(__dirname, '../fixtures/demo.json');
|
||||
var file = fs.readFileSync(uri, 'utf8');
|
||||
|
||||
// Create runtime instance & load SB2 into it
|
||||
var rt = new runtime();
|
||||
sb2(file, rt);
|
||||
|
||||
// Test
|
||||
t.type(file, 'string');
|
||||
t.type(rt, 'object');
|
||||
t.type(rt.targets, 'object');
|
||||
|
||||
t.ok(rt.targets[0] instanceof clone);
|
||||
t.type(rt.targets[0].id, 'string');
|
||||
t.type(rt.targets[0].blocks, 'object');
|
||||
t.type(rt.targets[0].variables, 'object');
|
||||
t.type(rt.targets[0].lists, 'object');
|
||||
|
||||
t.equal(rt.targets[0].isOriginal, true);
|
||||
t.equal(rt.targets[0].currentCostume, 0);
|
||||
t.equal(rt.targets[0].isOriginal, true);
|
||||
t.equal(rt.targets[0].isStage, true);
|
||||
|
||||
t.ok(rt.targets[1] instanceof clone);
|
||||
t.type(rt.targets[1].id, 'string');
|
||||
t.type(rt.targets[1].blocks, 'object');
|
||||
t.type(rt.targets[1].variables, 'object');
|
||||
t.type(rt.targets[1].lists, 'object');
|
||||
|
||||
t.equal(rt.targets[1].isOriginal, true);
|
||||
t.equal(rt.targets[1].currentCostume, 0);
|
||||
t.equal(rt.targets[1].isOriginal, true);
|
||||
t.equal(rt.targets[1].isStage, false);
|
||||
t.end();
|
||||
});
|
|
@ -2,12 +2,11 @@ var test = require('tap').test;
|
|||
var math = require('../../src/util/math-util');
|
||||
|
||||
test('degToRad', function (t) {
|
||||
// @todo This is incorrect
|
||||
t.strictEqual(math.degToRad(0), 1.5707963267948966);
|
||||
t.strictEqual(math.degToRad(1), 1.5533430342749535);
|
||||
t.strictEqual(math.degToRad(180), -1.5707963267948966);
|
||||
t.strictEqual(math.degToRad(360), -4.71238898038469);
|
||||
t.strictEqual(math.degToRad(720), -10.995574287564276);
|
||||
t.strictEqual(math.degToRad(0), 0);
|
||||
t.strictEqual(math.degToRad(1), 0.017453292519943295);
|
||||
t.strictEqual(math.degToRad(180), Math.PI);
|
||||
t.strictEqual(math.degToRad(360), 2 * Math.PI);
|
||||
t.strictEqual(math.degToRad(720), 4 * Math.PI);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ module.exports = [
|
|||
entry: {
|
||||
'dist': './src/index.js'
|
||||
},
|
||||
|
||||
output: {
|
||||
library: 'VirtualMachine',
|
||||
libraryTarget: 'commonjs2',
|
||||
|
@ -108,6 +109,9 @@ module.exports = [
|
|||
to: 'media'
|
||||
}, {
|
||||
from: 'node_modules/highlightjs/styles/zenburn.css'
|
||||
}, {
|
||||
from: 'assets',
|
||||
to: 'assets'
|
||||
}])
|
||||
])
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue